iTunes Now Playing in VB 2005

As I’ve been using VB 2005 a lot more a work the past few weeks, I thought I’d rewrite my iTunes Now Playing console app in VB.

It does the same as the previous C# example, and again I’ve chosen to use the free version of the language and Microsoft’s Visual Basic 2005 Express Edition.

The first thing to do, is to make sure you have downloaded the iTunes SDK from Apple.

In Visual Basic, create a new Console Application. We need to add a reference to the iTunes COM library, so we do this by right clicking over our application in the Solution Explorer, and selecting “Add Reference…”. Select the COM tab, and scroll down to find the iTunes library. Mine was called “iTunes 1.7 Type Library”.

adding the iTunes COM object to VB 2005

Now for some code.

Firstly, like all good programmers we turn on VB’s strict mode to make sure we’re not using too many bad programming practices.

Option Strict On

Next we need to import the libraries we want to use. In this case, System> and iTunesLib.

Imports System
Imports iTunesLib

As we’ve created a Console App in VB, we can just drop the following code into the Main sub.

Firstly we need to create an iTunesApp object so we can get talk to iTunes. After that we need to get the current track.

Dim app As New iTunesApp()
Dim track As IITTrack = app.CurrentTrack

We should now have the details of the current track in the track object so we just need to print these to the console.

Console.WriteLine("Current Track: {0}", track.Name)
Console.WriteLine("Current Artist: {0}", track.Artist)

Finally, just so we can see the result we need to wait for the user to press return before we exit.

Console.ReadLine()

Fire up iTunes if it’s not already on and start a track playing. Now start your VB program, and it should tell you the current track title and artist as shown below.

iTunes now playing script

It’s as simple as that. Obviously I’m not checking for exceptions and errors as this is just an basic example.

Your final code should look something like this.

Option Strict On
Imports System
Imports iTunesLib
Module Module1
Sub Main()
Dim app As New iTunesApp()
Dim track As IITTrack = app.CurrentTrack
Console.WriteLine("Current Track: {0}", track.Name)
Console.WriteLine("Current Artist: {0}", track.Artist)
Console.ReadLine()
End Sub
End Module

iTunes Now Playing In Perl

After working on the C# iTunes Now Playing program, I thought I’d give the Perl Win32::OLE module a try.

This module is Activestate Perl‘s COM interface for Microsoft Windows.

After using Perl for nearly 8 years, I’ve never given it a try before, and it turns out to be surprisingly easy to use.

The following script works exactly the same way as the C# version.

Firstly we need to create a new iTunes.Application object.

my $iTunesApp = new Win32::OLE("iTunes.Application");

Next we get the current track and print the name and artist.

my $track = $iTunesApp->CurrentTrack;
print "Current Track: " . $track->Name . "nCurrent Artist: " . $track->Artist . "n"

And that’s it. Obviously we should be checking to make sure the objects were create correctly, but this is just a simple example, not live code.

So to recap, here’s the final working example code.

#!/usr/bin/perl

use strict;
use Win32::OLE;

my $iTunesApp = new Win32::OLE("iTunes.Application");
my $track = $iTunesApp->CurrentTrack;

print "Current Track: " . $track->Name . "nCurrent Artist: " . $track->Artist . "n"

iTunes Now Playing In C#

I’ve been playing about with the COM interface to Apple’s iTunes running on a windows machine with .NET installed.

My choice of language has been C# as it’s a bit more Perl like than VB. I’ve used Microsoft’s “free” Visual C# Express in this case.

The iTunes SDK is available from Apple and has all the documentation needed.

I’m going to run through how to get Now Playing information out of iTunes. The quickest way to do this is as a console application.

Start up Visual C# and create a new console application. I called mine NowPlaying.

You’ll need to add a reference to the iTunes come library. Do this on the righthand side of the C# project window by right clicking and selecting “Add Reference…”, selecting the COM tab, and finding the iTunes library.

The first thing you’ll need to in your code is to import the iTunes namespace to make the code look a little cleaner. We’ll also need the System interface for our console input and output.

using System;
using iTunesLib;

In your Main method, the first thing we need to do is to create an instance of the iTunesAppClass. I’ve rather originally called mine, app.

// Create a new iTunesApp object to use
iTunesApp app = new iTunesAppClass();

Next we need to get the current track. This is a simple attribute call to the app object.

// Get the current track from iTunes.
ITTrack track = app.CurrentTrack;

Finally we need to show the current track. To do this we call two attributes on our track variable, name and artist. There are plenty of other attributes we could call, but these are the two most useful for this example. See the SDK for more choice.

Once we have displayed our name and artist, we need to wait for the user to hit return. This is useful if our program wasn’t launched from a console window as it would end before we’ve had a chance to read any output.

// Display some info on the current track.
Console.WriteLine("Current Track: {0}rnCurrent Artist: {1}" , track.Name, track.Artist);
// Pause until we hit return.
Console.ReadLine();

That’s it, nice and easy.

Here’s the full .cs source code.

using System;
using iTunesLib;
namespace NowPlaying
{
class Program
{
static void Main(string[] args)
{
// Create a new iTunesApp object to use
iTunesApp app = new iTunesAppClass();
// Get the current track from iTunes and return its artist and name.
IITTrack track = app.CurrentTrack;
Console.WriteLine("Current Track: {0}rnCurrent Artist: {1}" , track.Name, track.Artist);
// Pause until we hit return.
Console.ReadLine();
}
}
}

It’s Time To Learn Python

Learning Python
No one would have believed, in the first years of the twenty first centry, that mobile affairs were being watched across many timeless RSS feeds. No one could have dreamed that they were being scrutinized as someone studies creatures that swarm and multiply in a drop of water. Few Perl coders even considered the possibility of life with other programming languages. And yet, across the gulf of the blogosphere, a mind immeasurably superior to theirs regarded a certain language with envious eyes, and slowly and surely, he drew his plans to use it.

Yes, I’m fed up waiting for J2ME‘s long development times and restrictive programming model. I’ve been watching Python enviously for a while, and I’ve decided it’s time I added another language to my developers toolbelt.

I’ve gone out and bought O’Reilly’s Learning Python 2nd Edition, and installed Series 60 Python on my Nokia smartphone.

I’m about a quarter of the way through the book, and it all seems fairly simple so far.

Once I’ve got myself up to speed with the standard language, I’m going to move to the Symbian specific stuff. I may then treat myself to Programming Python and the Python Cookbook to get my skills up to a decent level.

Series 60 Python looks to be really full featured offering…

  • 2D Graphics, Images and Full-screen applications
  • Camera and Screenshot API
  • Contacts and Calendar API
  • Sound recording and playback
  • Access to system info such as IMEI number, disk space, free memory etc
  • Rich text display (fonts, colours, styles)
  • Support for Scabale UI
  • Expanded key events
  • Telephone dialing
  • Zip compression
  • Networking support for GPRS and Bluetooth
  • Native GUI widget
  • SMS

In other words, the ability to do nearly anything you want quickly and easily on the phone once you’ve installed the Python sis file.

Another benefit of learning Python, will be the ability to script in Civilization IV. πŸ˜‰

Adding Class Variables To Object Orientated Inline::C

I’ve been working using the Perl module Inline::C lately.

I’ve been using it for some Object Orientated code, and I’ve had the need to have a shared class variable between various C classes. I thought I’d share how I’d accomplished this.

The code I’m using here is based on the object example in Inline::C-Cookbook.

I’m going to add a class variable called count to the example Soldier class.

Firstly we need to add a new line of code to declare this variable

## class variable
static int counter = 0;

Noticed I’ve made the variable static to ensure only one copy of it exists. I’ve also initialised it to 0.

One thing I’ve done, that isn’t really necessary is to add it to the Soldier object. The new Soldier object looks like this…

typedef struct {
char* name;
char* rank;
long serial;
int* count;
} Soldier;

The new line here is int* count, a pointer to an integer called count. We’ll point this at our static counter variable.

To point count to counter we modify the new function to add the following…

solder->serial = serial; ## existing code
soldier->count = &counter;

Here we’re specifically saying that our pointer count should point to the address of counter.

Great, but we need a way of accessing this data. We’ll create a new method called get_count to return the current value of count.

int get_count(SV* obj) {
return *((Soldier*)SvIV(SvRV(obj)))->count;
}

Great, now we can just call get_count from our Soldier object.

By itself this isn’t really much use as we never manipulate the value of counter. To prove this works, just modify the new method to add a line that increments counter. For example,

## increment counter
counter++;

Voila! We we have a static class variable that can now be used with Inline::C objects!

Using SOAP::Lite With Perl

I’ve been trying to access a SOAP service using Perl for a project, so I took a quick look at CPAN to see what was available. The answer seemed to be to use SOAP::Lite.

This module turned out to be a real devil to use, and my problems with it were probably compounded by my lack of SOAP experience.

SOAP (incase you didn’t know) is a method of accessing remote services using XML. In my case, I was trying to access content supplied by a third party ringtone provider using SOAP over HTTP.

Using SOAP::Lite you have to specify a uri and a proxy. What stumped me for a while is that the proxy is the URI of service you wish to access, and uri is the namespace of the service.

So if the provider I was using had their SOAP service available at ws.robstones-services.co.uk/external.asmx, I would use this as the proxy, and in this case I would use ws.robstones-services.co.uk/External as the urinamespace.

Great, I knew the service I needed was called getCallList, and required a Username and Password to be passed to it. In return it would give me a list of valid content types. The SOAPAction header to be added to the call to proxy, letting the remote SOAP service I wanted to use the service http://ws.robstones-services.co.uk/External/getCallList.

My first attempt was the following code…

#!/usr/bin/perl -w
use strict;
use SOAP::Lite 'trace', 'debug';
my $server = SOAP::Lite
->uri('http://ws.robstones-services.co.uk/External')
->proxy('http://ws.robstones-services.co.uk/external.asmx');
my $returned = $server
->getCallList({
'Username' => 'RobsUser',
'Password' => 'RobsPassword'
});
foreach my $type ($returned->valueof('//getCallListResult/string')) {
next unless ($type); ## ignore any undefs
print "$typen";
}

You’ll notice I’m using trace and debug to see the SOAP messages being sent and received. The dialogue from this script was…

> perl -w test.pl
SOAP::Transport::HTTP::Client::send_receive: POST http://ws.robstones-services.co.uk/external.asmx HTTP/1.1
Accept: text/xml
Accept: multipart/*
Content-Length: 615
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://ws.robstones-services.co.uk/External#getCallList"
<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><namesp1:getCallList xmlns:namesp1="http://ws.robstones-services.co.uk/External"><c-gensym3><Username xsi:type="xsd:string">RobsUser</Username><Password xsi:type="xsd:string">RobsPassword</Password></c-gensym3></namesp1:getCallList></SOAP-ENV:Body></SOAP-ENV:Envelope>
SOAP::Transport::HTTP::Client::send_receive: HTTP/1.1 500 (Internal Server Error) Internal Server Error.
Cache-Control: private
Connection: close
Date: Tue, 29 Nov 2005 15:23:38 GMT
Server: Microsoft-IIS/6.0
Content-Length: 508
Content-Type: text/xml; charset=utf-8
Client-Date: Tue, 29 Nov 2005 15:23:09 GMT
Client-Response-Num: 1
X-AspNet-Version: 1.1.4322
X-Powered-By: ASP.NET
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>Server did not recognize the value of HTTP Header SOAPAction: http://ws.robstones-services.co.uk/External#getCallList.</faultstring>
<detail />
</soap:Fault>
</soap:Body>
</soap:Envelope>

The call didn’t work, you’ll see the SOAPAction variable is wrong, it’s http://ws.robstones-services.co.uk/External#getCallList instead of http://ws.robstones-services.co.uk/External/getCallList.

I needed to get SOAP::Lite to use the correct URL. The secret turned out to be to change the use SOAP::Lite line to the following.

use SOAP::Lite on_action => sub {sprintf '%s/%s', @_},
'trace', 'debug';

on_action is a parameter SOAP::Lite uses to separate the URI from the action, here we’re telling it to use /.

This gave me the following SOAP dialogue…

> perl -w test.pl
SOAP::Transport::HTTP::Client::send_receive: POST http://ws.robstones-services.co.uk/external.asmx HTTP/1.1
Accept: text/xml
Accept: multipart/*
Content-Length: 615
Content-Type: text/xml; charset=utf-8
SOAPAction: http://ws.robstones-services.co.uk/External/getCallList
<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><namesp1:getCallList xmlns:namesp1="http://ws.robstones-services.co.uk/External"><c-gensym3><Username xsi:type="xsd:string">RobsUser</Username><Password xsi:type="xsd:string">RobsPassword</Password></c-gensym3></namesp1:getCallList></SOAP-ENV:Body></SOAP-ENV:Envelope>
SOAP::Transport::HTTP::Client::send_receive: HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Connection: close
Date: Tue, 29 Nov 2005 15:22:53 GMT
Server: Microsoft-IIS/6.0
Content-Length: 405
Content-Type: text/xml; charset=utf-8
Client-Date: Tue, 29 Nov 2005 15:22:25 GMT
Client-Response-Num: 1
X-AspNet-Version: 1.1.4322
X-Powered-By: ASP.NET
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><getCallListResponse xmlns="http://ws.robstones-services.co.uk/External"><getCallListResult><string xsi:nil="true" /></getCallListResult></getCallListResponse></soap:Body></soap:Envelope>

Bugger, it still didn’t work. I looked again at the SOAP message being sent. It was sending a lot of namespace information and a wrapper around the Username and Password. The wrapper was definitely wrong, and the remote .NET service didn’t seem to like the namespaces.

I needed to sort that out, so I had to tell SOAP::Lite to not to include namespaces or wrap up the Username and Password. The way to do this is to use SOAP::Data to hardcode the data being sent.

This meant changing the call to getCallList to the following…

my $returned = $server
->getCallList(
SOAP::Data->name('Username')->value('RobsUser')->type(''),
SOAP::Data->name('Password')->value('RobsPassword')->type('')
);

The SOAP dialogue after these changes looked like this..

SOAP::Transport::HTTP::Client::send_receive: POST http://ws.robstones-services.co.uk/external.asmx HTTP/1.1
Accept: text/xml
Accept: multipart/*
Content-Length: 548
Content-Type: text/xml; charset=utf-8
SOAPAction: http://ws.robstones-services.co.uk/External/getCallList
<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><namesp1:getCallList xmlns:namesp1="http://ws.robstones-services.co.uk/External"><Username>RobsUser</Username><Password>RobsPassword</Password></namesp1:getCallList></SOAP-ENV:Body></SOAP-ENV:Envelope>
SOAP::Transport::HTTP::Client::send_receive: HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Connection: close
Date: Tue, 29 Nov 2005 15:19:49 GMT
Server: Microsoft-IIS/6.0
Content-Length: 405
Content-Type: text/xml; charset=utf-8
Client-Date: Tue, 29 Nov 2005 15:19:21 GMT
Client-Response-Num: 1
X-AspNet-Version: 1.1.4322
X-Powered-By: ASP.NET
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><getCallListResponse xmlns="http://ws.robstones-services.co.uk/External"><getCallListResult><string xsi:nil="true" /></getCallListResult></getCallListResponse></soap:Body></soap:Envelope>

Close, but still not right. The problem was with the getCallList itself, it was still trying to use a namespace.

The final solution was to this problem was to not use getCallList, but instead to use SOAP::Lite’s call method to implicitly set how the function was being called.

The code after making this change was the following…

my $returned = $server
->call(SOAP::Data->name('getCallList')->attr({xmlns => 'http://ws.robstones-services.co.uk/External'}) =>
SOAP::Data->name('Username')->value('RobsUser')->type(''),
SOAP::Data->name('Password')->value('RobsPassword')->type('')
);

This gave me the SOAP dialogue…

> perl -w test_soap.pl
SOAP::Transport::HTTP::Client::send_receive: POST http://ws.robstones-services.co.uk/external.asmx HTTP/1.1
Accept: text/xml
Accept: multipart/*
Content-Length: 524
Content-Type: text/xml; charset=utf-8
SOAPAction: http://ws.robstones-services.co.uk/External/getCallList
<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><getCallList xmlns="http://ws.robstones-services.co.uk/External"><Username>RobsUser</Username><Password>RobsPassword</Password></getCallList></SOAP-ENV:Body></SOAP-ENV:Envelope>
SOAP::Transport::HTTP::Client::send_receive: HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Connection: close
Date: Tue, 29 Nov 2005 15:16:07 GMT
Server: Microsoft-IIS/6.0
Content-Length: 540
Content-Type: text/xml; charset=utf-8
Client-Date: Tue, 29 Nov 2005 15:15:39 GMT
Client-Response-Num: 1
X-AspNet-Version: 1.1.4322
X-Powered-By: ASP.NET
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><getCallListResponse xmlns="http://ws.robstones-services.co.uk/External"><getCallListResult><string>ROBS_JAVA</string><string>ROBS_WALL</string><string>ROBS_POLY</string><string>ROBS_MONO</string><string>ROBS_REAL</string><string xsi:nil="true" /></getCallListResult></getCallListResponse></soap:Body></soap:Envelope>
ROBS_JAVA
ROBS_WALL
ROBS_POLY
ROBS_MONO
ROBS_REAL

Hurrah, after all that effort, I have a list of 5 types of content offered by my third party supplier. I can now go ahead and build my service.

SOAP::Lite is a powerful module, but the lack of simple, easy to follow documentation and examples holds it back. I hope this small article helps out other programmers just starting on the SOAP path.

Using Symbian Perl

I’ve been playing with Jarkko Hietaniemi’s port of Perl for Series 60 Symbian devices on my Nokia 7610.

There is a lot of work to be done on it still, but it works, and I can run simple Perl scripts.

Perl running on a Nokia 7610 phone

The screenshot above show’s me running a simple script to list the contents of the Lifeblog directory on my series 7610 phone. Here’s the code that was run on the phone to generate the listing.


my $directory = "C:\system\Data\lifeblog\";
print "$directoryn";
opendir(DIR,$directory);
foreach my $file (readdir(DIR)) {
print "$filen";
}
closedir(DIR);
sleep(5);

As you can see it’s simple stuff. I added the sleep(5) at the end just to allow me to take a screenshot as when the script has been run it prompts you to press any key to exit. In this case, the screenshot key, caused an exit. πŸ™‚

It may not look much, but it’s bloody exciting stuff for me. The possibility of running decent Perl applications on my phone has me salivating in anticipation of the next release. At present the version out there (0.1.0) doesn’t offer any interface into the native Symbian libraries from Perl, but I’m sure that will be changing soon.

It’s easy to write simple scripts, as all I had to do for this one was to just drop it into the Perl directory on my memory card (via Nokai PC Suite) and use the PerlApp application supplied in the Symbian distribution.

The Python guys are luckier to have a fuller featured release of their favourite scripting language, but watch out, the Perl Mongers are coming!

Nokia Release Series 60 Patch For Perl

It looks like Perl on Nokia Series 60 phones is getting closing as Jarkko Hietaniemi has just commited a patch to the Perl 5 Porters mailing list that enables Perl 5.8.x and Perl 5.9.x to work on Symbian smartphones. The message specifically states that it is known to work on Nokia Series 60 phones. The port is copyright Nokia.

I’m now officially very excited! Perl could very soon be running on my Nokia 6630!

A quick delve into the attached README reveals…

The attached patches enable compiling Perl on the Symbian OS platform:
Symbian OS releases 7.0s and 8.0a; and the corresponding Series 60
SDKs 2.0, 2.1, and 2.6.

Note that the patches only implement a “base port”, enabling one to
run Perl on Symbian, the basic operating system platform. The patches
do not implement any further Symbian OS or Series 60 (an application
framework) bindings to Perl. (A small Symbian / Series 60 interface
class and a small Series 60 application are included, though.)

It also seems that the patch allows Perl to be embedded into Series 60 C++ applications.

Since the primary way of using Perl on Symbian is a DLL (as described above),
I also wrote a small wrapper class for Series 60 (C++) applications that
want to embed a Perl interpreter, and a small Series 60 demonstration
application (PerlApp) using that wrapper class. As a bonus PerlApp knows
how to install Perl scripts (.pl, or hash-bang-perl) and Perl modules (.pm)
from the messaging application’s Inbox, and how to run scripts when invoked
via a filebrowser (either the one builtin to PerlApp, or an external one).

It’s fantastic to see that Nokia are working on getting Perl onto their smartphones. I’ve jealously looked on as Python developers have had their language implemented, now it seems that Perl could well be nearing an official launch.

Grayscaling Images With Perl

One thing that caught my interest today was how to convert a colour image into grayscale.

It turns out the basic algorithm is very simple. Basically it’s just…

grey = 0.15 * red + 0.55 * green + 0.30 * blue;

This can be turned into a Perl subroutine using the following code.

sub grayscale {
my ($r, $g, $b) = @_;
my $s = 0.15 * $r + 0.55 * $g + 0.30 * $b;
return int($s);
}

Here we pass in the RGB values of the colour we want to turn into gray. We apply the algorithm and return the integer value of gray.

The value we get for gray is used to replace each of the values for red, green and blue.

We can test this subroutine out with the help of the Perl GD module (available for free on CPAN).

#!/usr/bin/perl -w
use GD;
## grayscale subroutine
sub grayscale {
my ($r, $g,$b) = @_;
my $s = 0.15 * $r + 0.55 * $g + 0.30 * $b;
return int($s);
}
## create a new GD object with the data passed via STDIN
my $image = new GD::Image(*STDIN);
## iterate over the number of colours in the colour table
for (my $i = 0; $i < $image->colorsTotal(); $i++) {
## get the RGB values for the colour at index $i
my ($r, $g, $b) = $image->rgb($i);
## convert the RGB to grayscale
my $gray = grayscale($r,$g,$b);
## remove the original colour from the colour table
$image->colorDeallocate($i);
## add in the new gray
$image->colorAllocate($gray,$gray,$gray);
}
## make sure we output binary
binmode STDOUT;
## pass the image as a raw GIF to STDOUT
print $image->gif;

This code takes an image piped in from STDIN and outputs a grayscale GIF version of the image to STDOUT.

If the code was called convert.pl it would be called as ./convert.pl <test.gif >>test_result.gif.

Here’s a conversion I did earlier of a GIF image of Kitt, Bev and Justin at the Emap Performance Awards 2004 using the above Perl code.

Kitt, Bev and Justin in colour

Kitt, Bev and Justin in grayscale

WSSE Authentication For Atom Using Perl

Atom uses the WSSE authentication for posting and editing weblogs.

Mark Pilgrim explains more about this in layman’s terms in an old XML.com article, Atom Authentication.

This information is passed in an HTTP header, for example…

HTTP_X_WSSE UsernameToken Username="robertprice", PasswordDigest="l7FbmWdq8gBwHgshgQ4NonjrXPA=", Nonce="4djRSlpeyWeGzcNgatneSA==", Created="2005-2-5T17:18:15Z"

We need 4 pieces of information to create this string.

  1. Username
  2. Password
  3. Nonce
  4. Timestamp

A nonce is a cryptographically random string in this case, not the word Clinton Baptiste gets in Phoenix Nights (thanks to Matt Facer for the link). In this case, it’s encoded in base64.

The timestamp is the current time in W3DTF format.

The for items are then encoded together to form a password digest that is used for the verification of the authenticity of the request on the remote atom system. As it already knows you username and password, it can decrypt the password the nonce and timestamp passed in the WSSE header. It uses the well known SHA1 algorithm to encrypt the pasword and encodes it in base64 for transportation across the web.

We can use Perl to create the password digest, as shown in this example code.

my $username = "robertprice";
my $password = "secret password";
my $nonce = "4djRSlpeyWeGzcNgatneSA==";
my $timestamp = "2005-2-5T17:18:15Z";
my $digest = MIME::Base64::encode_base64(Digest::SHA1::sha1($nonce . $timestamp . $password), '');

The password digest is now stored in the variable $digest.

We can also create the HTTP header from this if needed.

print qq{HTTP_X_WSSE UsernameToken Username="$username", PasswordDigest="$digest", Nonce="$nonce", Created="$created"n};

Please note, to use this Perl code, you have to have the MIME::Base64 and Digest::SHA1 modules installed. Both are freely available on CPAN.

Update – 22nd November 2006

Some more recent versions of Atom expect the digest to be generated with a base64 decoded version of the nonce. Using the example above, some example code for this would be…


## generate alternative digest
my $alternative_digest = MIME::Base64::encode_base64(Digest::SHA1::sha1(MIME::Base64::decode_base64($nonce) . $timestamp . $password), '');

When using WSSE for password validation, I now always check the incoming digest with both versions of my generated digested to ensure it’s compatible with different versions of Atom enabled software. One of the best examples of this is the Nokia Lifeblog. Older versions expect the nonce to be left, newer versions expect the nonce to be decoded first.