Nathan McFarland did a Perl port of my PHP netgrowl code that is much more in the Perl spirit that I would ever be bothered to do and was kind enough to allow me to host a copy for easy reference. The original is here, and the latest version is on CPAN under Net-Growl.
# Derived from Rui Carmo's (http://the.taoofmac.com)
# netgrowl.php (http://the.taoofmac.com/space/Projects/netgrowl.php)
# by Nathan McFarland - (http://nmcfarl.org)
# Make sure this is installed as Mac/Growl/Net.pm
# in a location list in @INC.
# If you do not perl will not be able to find it
# To view your @INC run `perl -V`
package Mac::Growl::Net;
use strict;
use IO::Socket;
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = ('register', 'notify');
use constant GROWL_UDP_PORT=>9887;
use constant GROWL_PROTOCOL_VERSION=>1;
use constant GROWL_TYPE_REGISTRATION=>0;
use constant GROWL_TYPE_NOTIFICATION=>1;
sub register{
my %args = @_;
my %addr = (PeerAddr => $args{host} || "localhost",
PeerPort => Mac::Growl::Net::GROWL_UDP_PORT,
Proto => 'udp');
my $s = IO::Socket::INET->new ( %addr ) || die "Could not create socket: $!\n" ;
my $p = Mac::Growl::Net::RegistrationPacket->new(%args);
$p->addNotification();
print ($s $p->payload());
close($s);
}
sub notify{
my %args = @_;
my %addr = (PeerAddr => $args{host} || "localhost",
PeerPort => Mac::Growl::Net::GROWL_UDP_PORT,
Proto => 'udp');
my $s = IO::Socket::INET->new ( %addr ) || die "Could not create socket: $!\n" ;
my $p = Mac::Growl::Net::NotificationPacket->new( %args);
print ($s $p->payload());
close($s);
}
1;
package Mac::Growl::Net::RegistrationPacket;
use Digest::MD5 qw( md5_hex );
use base 'Mac::Growl::Net';
sub new {
my $class = shift;
my %args = @_;
$args{application} ||="growlnotify";
my $self = bless \%args, $class;
$self->{notifications} = {};
utf8::encode($self->{application} );
return $self;
}
sub addNotification{
my ($self, %args)=@_;
$args{notification}||="Command-Line Growl Notification";
$args{enabled}="True" if !defined $args{enabled} ;
$self->{notifications}->{$args{notification}}=$args{enabled};
}
sub payload{
my $self = shift;
my ($name, $defaults);
my ($name_count, $defaults_count);
for (keys %{$self->{notifications}})
{
utf8::encode( $_ );
$name .= pack( "n", length($_) ) . $_;
$name_count++;
if( $self->{notifications}->{$_} ) {
$defaults .= pack( "c", $name_count-1 );
$defaults_count++;
}
}
$self->{data} = pack( "c2nc2",
$self->GROWL_PROTOCOL_VERSION,
$self->GROWL_TYPE_REGISTRATION,
length($self->{application}),
$name_count,
$defaults_count );
$self->{data} .= $self->{application} . $name . $defaults;
my $checksum;
if( $self->{password} ){
$checksum = pack( "H32", md5_hex( $self->{data} . $self->{password} ) );
}else
{
$checksum = pack( "H32", md5_hex( $self->{data} ));
}
$self->{data} .= $checksum;
return $self->{data};
}
1;
package Mac::Growl::Net::NotificationPacket;
use Digest::MD5 qw( md5_hex );
use base 'Mac::Growl::Net';
sub new {
my ($class,%args ) = @_;
$args{application} ||="growlnotify";
$args{notification} ||="Command-Line Growl Notification";
$args{title} ||="Title";
$args{description} ||="Description";
$args{priority} ||= 0;
my $self = bless \%args, $class;
utf8::encode($self->{application});
utf8::encode($self->{notification});
utf8::encode($self->{title});
utf8::encode($self->{description});
my $flags = ($args{priority} & 0x07) * 2;
if ($args{priority} < 0){
$flags |= 0x08;
}
if ($args{sticky}){
$flags = $flags | 0x0001;
}
$self->{data} = pack( "c2n5",
$self->GROWL_PROTOCOL_VERSION,
$self->GROWL_TYPE_NOTIFICATION,
$flags,
length($self->{notification}),
length($self->{title}),
length($self->{description}),
length($self->{application}) );
$self->{data} .= $self->{notification};
$self->{data} .= $self->{title};
$self->{data} .= $self->{description};
$self->{data} .= $self->{application};
if ($args{password})
{
$self->{checksum} = pack( "H32", md5_hex( $self->{data} . $args{password} ) );
}
else
{
$self->{checksum} = pack( "H32", md5_hex( $self->{data}) );
}
$self->{data} .= $self->{checksum};
return $self;
}
sub payload{
my $self = shift;
return $self->{data};
}
1;
package main;
# only when called directly
# perl Mac/Growl/Net.pm
if ($0 =~ m{Mac/Growl/Net.pm})
{
$0="mac_growl_net_tests";
my %addr = (PeerAddr => "localhost",
PeerPort => Mac::Growl::Net::GROWL_UDP_PORT,
Proto => 'udp');
my $s = IO::Socket::INET->new ( %addr ) || die "Could not create socket: $!\n" ;
my $p = Mac::Growl::Net::RegistrationPacket->new( application=>"Perl Notifier");
$p->addNotification();
print ($s $p->payload());
$p = Mac::Growl::Net::NotificationPacket->new( application=>"Perl Notifier",
title=>'warning',
description=>'some text',
priority=>2,
sticky=>'True');
print ($s $p->payload());
close($s);
## or the easy way -- more sockets are created though
# when outside the module you can just do
# use Mac::Growl::Net;
# register();
# notify();
Mac::Growl::Net::register();
Mac::Growl::Net::notify();
}
1;