File Coverage

blib/lib/Net/GrowlClient.pm
Criterion Covered Total %
statement 90 147 61.2
branch 2 18 11.1
condition 3 25 12.0
subroutine 29 34 85.2
pod 2 2 100.0
total 126 226 55.7


line stmt bran cond sub pod time code
1             package Net::GrowlClient;
2              
3 1     1   24347 use 5.006000;
  1         5  
  1         42  
4 1     1   6 use strict;
  1         2  
  1         37  
5 1     1   6 use warnings;
  1         6  
  1         41  
6 1     1   1112 use IO::Socket;
  1         32306  
  1         5  
7 1     1   677 use Digest::MD5 qw( md5_hex );
  1         3  
  1         66  
8 1     1   10256 use Digest::SHA qw( sha256_hex );
  1         4971  
  1         89  
9 1     1   1462 use utf8;
  1         11  
  1         5  
10 1     1   32 use Carp;
  1         1  
  1         117  
11             require Exporter;
12             our @ISA = ("Exporter");
13             our @EXPORT = qw(notify $VERSION @ISA);
14             our $VERSION = '0.02';
15              
16             ##############################################
17             ## CONSTANTS
18             ##############################################
19 1     1   5 use constant GROWL_UDP_PORT => '9887';
  1         2  
  1         73  
20 1     1   5 use constant GROWL_PROTOCOL_VERSION => '1';
  1         2  
  1         43  
21 1     1   5 use constant GROWL_PROTOCOL_VERSION_AES128 => '2';
  1         1  
  1         38  
22 1     1   4 use constant GROWL_TYPE_REGISTRATION => '0';
  1         7  
  1         34  
23 1     1   5 use constant GROWL_TYPE_NOTIFICATION => '1';
  1         2  
  1         39  
24 1     1   12 use constant GROWL_TYPE_REGISTRATION_SHA256 => '2';
  1         2  
  1         45  
25 1     1   13 use constant GROWL_TYPE_NOTIFICATION_SHA256 => '3';
  1         1  
  1         34  
26 1     1   4 use constant GROWL_TYPE_REGISTRATION_NOAUTH => '4';
  1         2  
  1         45  
27 1     1   5 use constant GROWL_TYPE_NOTIFICATION_NOAUTH => '5';
  1         2  
  1         42  
28              
29 1     1   5 use constant CLIENT_DEFAULT_APPNAME => 'Net::GrowlClient';
  1         1  
  1         38  
30 1     1   4 use constant CLIENT_DEFAULT_NOTIFICATION_LIST => ['Net::GrowlClient Notification'];
  1         2  
  1         219  
31 1     1   7 use constant CLIENT_DEFAULT_NOTIFICATION => CLIENT_DEFAULT_NOTIFICATION_LIST->[0];
  1         2  
  1         58  
32 1     1   6 use constant CLIENT_DEFAULT_TITLE => 'Hello from Net::GrowlClient!';
  1         1  
  1         79  
33 1     1   5 use constant CLIENT_DEFAULT_MESSAGE => "Yeah! It Works!\nThis is the default message.";
  1         2  
  1         57  
34 1     1   23 use constant CLIENT_DEFAULT_PRIORITY => 0;
  1         2  
  1         50  
35 1     1   6 use constant FALSE => 0;
  1         1  
  1         98  
36 1     1   7 use constant TRUE => 1;
  1         2  
  1         731  
37              
38             ##############################################
39             ## CONSTRUCTOR
40             ##############################################
41             sub init
42             {
43 1     1 1 365 my $caller = shift;
44 1   33     9 my $class = ref($caller) || $caller;
45 1         20 my $self = {
46             'CLIENT_PASSWORD' => FALSE,
47             'CLIENT_PEER_HOST' => FALSE,
48             'CLIENT_PEER_PORT' => FALSE,
49             'CLIENT_STICKY' => FALSE,
50             'CLIENT_SKIP_REGISTER' => FALSE,
51             'CLIENT_CRYPT' => FALSE,
52             'CLIENT_APPLICATION_NAME' => CLIENT_DEFAULT_APPNAME,
53             'CLIENT_NOTIFICATION_LIST' => CLIENT_DEFAULT_NOTIFICATION_LIST,
54             'CLIENT_TYPE_REGISTRATION' => GROWL_TYPE_REGISTRATION_NOAUTH,
55             'CLIENT_TYPE_NOTIFICATION' => GROWL_TYPE_NOTIFICATION_NOAUTH,
56             'CLIENT_NOTIFICATION' => CLIENT_DEFAULT_NOTIFICATION,
57             'CLIENT_TITLE' => CLIENT_DEFAULT_TITLE,
58             'CLIENT_MESSAGE' => CLIENT_DEFAULT_MESSAGE,
59             'CLIENT_PRIORITY' => CLIENT_DEFAULT_PRIORITY,
60             @_
61             };
62              
63 1         3 bless($self, $class);
64 1         5 &_init($self);
65 1 50       1323 &_register($self) unless $self->{'CLIENT_SKIP_REGISTER'};
66 1         4 return $self;
67             }
68              
69             ##############################################
70             ## INITIALIZE UDP SOCKET
71             ##############################################
72             sub _init
73             {
74 1     1   1 my $self = shift;
75 1 50 50     154 $self->{'CLIENT_SOCKET'} = IO::Socket::INET->new
      50        
76             (
77             PeerPort => $self->{'CLIENT_PEER_PORT'} || GROWL_UDP_PORT,
78             PeerHost => $self->{'CLIENT_PEER_HOST'} || 'localhost',
79             Proto => 'udp',
80             Type => SOCK_DGRAM,
81             ReuseAddr => 1
82             ) or croak __PACKAGE__." --> $@";
83             }
84              
85             ##############################################
86             ## REGISTER
87             ##############################################
88             sub _register
89             {
90 1     1   1300 use bytes;
  1         10  
  1         6  
91 0     0     my $self = shift;
92 0           my ($data, $ckecksum, $notification_pack, $default_pack, $packet, $checksum);
93 0           my $notification_list_ptr = $self->{'CLIENT_NOTIFICATION_LIST'};
94 0           my $application_name = $self->{'CLIENT_APPLICATION_NAME'};
95 0           my $password = $self->{'CLIENT_PASSWORD'};
96 0           utf8::encode($password);
97 0           utf8::encode($application_name);
98              
99 0           foreach my $notification ( @$notification_list_ptr )
100             {
101 0           utf8::encode($notification);
102 0           $notification_pack .= pack (
103             'na*',
104             bytes::length($notification),
105             $notification
106             );
107             }
108 0           foreach my $default_notification (0..scalar @$notification_list_ptr - 1)
109             {
110 0           $default_pack .= pack('C', $default_notification);
111             }
112              
113 0           $data = pack (
114             'CCnCC',
115             GROWL_PROTOCOL_VERSION,
116             $self->{'CLIENT_TYPE_REGISTRATION'},
117             bytes::length($application_name),
118             (scalar @$notification_list_ptr),
119             (scalar @$notification_list_ptr)
120             );
121 0           $data .= pack ('a*', $application_name);
122 0           $data .= $notification_pack . $default_pack;
123              
124 0 0         if ($self->{'CLIENT_TYPE_REGISTRATION'} eq GROWL_TYPE_REGISTRATION)
    0          
125             {
126 0           $checksum = pack ('H32', md5_hex($data . $password));
127 0           $packet = $data . $checksum;
128             }
129             elsif ($self->{'CLIENT_TYPE_REGISTRATION'} eq GROWL_TYPE_REGISTRATION_SHA256)
130             {
131 0           $checksum = pack ('H64', sha256_hex($data . $password));
132 0           $packet = $data . $checksum;
133             }
134             else
135             {
136 0           $packet = $data;
137             }
138              
139 0           &_sender($self, $packet);
140             }
141              
142             ##############################################
143             ## NOTIFY
144             ##############################################
145             sub notify
146             {
147 1     1   431 use bytes;
  1         3  
  1         6  
148 0     0 1   my $self = shift;
149 0           my %notify_args = @_;
150 0           my ($notification_name, $title, $message, $application_name, $checksum, $data, $packet, $priority, $flags, $sticky, $password);
151 0           my %priority = (
152             "-2" => "011",
153             "Low" => "011",
154             "Very Low" => "011",
155             "-1" => "111",
156             "Moderate" => "111",
157             "0" => "000",
158             "Normal" => "000",
159             "1" => "100",
160             "High" => "100",
161             "2" => "010",
162             "Emergency" => "010"
163             );
164            
165 0           $password = $self->{'CLIENT_PASSWORD'};utf8::encode($password);
  0            
166 0   0       $application_name = $notify_args{'application'} || $self->{'CLIENT_APPLICATION_NAME'};utf8::encode($application_name);
  0            
167 0   0       $notification_name = $notify_args{'notification'} || $self->{'CLIENT_NOTIFICATION'};utf8::encode($notification_name);
  0            
168 0   0       $title = $notify_args{'title'} || $self->{'CLIENT_TITLE'};utf8::encode($title);
  0            
169 0   0       $message = $notify_args{'message'} || $self->{'CLIENT_MESSAGE'};utf8::encode($message);
  0            
170 0   0       $sticky = $notify_args{'sticky'} || $self->{'CLIENT_STICKY'};
171            
172 0 0 0       if (($notify_args{'priority'}) and (grep (/^$notify_args{'priority'}$/, keys %priority)))
173             {
174 0           $priority = $priority{$notify_args{'priority'}};
175             }
176             else
177             {
178 0           $priority = $self->{'CLIENT_PRIORITY'};
179 0 0         carp __PACKAGE__." --> Unknown Priority \'$notify_args{'priority'}\'" if ($notify_args{'priority'});
180             }
181            
182 0           $flags = '0'x12 .$priority.$sticky;
183            
184 0           $data = pack ( 'CCb[16]nnnna*',
185             GROWL_PROTOCOL_VERSION,
186             $self->{'CLIENT_TYPE_NOTIFICATION'},
187             $flags,
188             bytes::length($notification_name),
189             bytes::length($title),
190             bytes::length($message),
191             bytes::length($application_name),
192             "$notification_name$title$message$application_name"
193             );
194              
195 0 0         if ($self->{'CLIENT_TYPE_NOTIFICATION'} eq GROWL_TYPE_NOTIFICATION)
    0          
196             {
197 0           $checksum = pack ('H32', md5_hex($data . $password));
198 0           $packet = $data . $checksum;
199             }
200             elsif ($self->{'CLIENT_TYPE_NOTIFICATION'} eq GROWL_TYPE_NOTIFICATION_SHA256)
201             {
202 0           $checksum = pack ('H64', sha256_hex($data . $password));
203 0           $packet = $data . $checksum;
204             }
205             else
206             {
207 0           $packet = $data;
208             }
209              
210 0           &_sender($self, $packet);
211             }
212             ##############################################
213             ## DATA CRYPT (AES128)
214             ##############################################
215             sub _aescrypt
216             {
217 0     0     my $self=shift;
218 0           my $packet;
219 0           return $packet;
220             }
221             ##############################################
222             ## SOCKET PRINTER
223             ##############################################
224             sub _sender
225             {
226 0     0     my $self = shift;
227 0           my $packet = shift;
228 0 0         $self->{'CLIENT_SOCKET'}->send($packet) or carp __PACKAGE__." --> $@";
229             }
230              
231             ##############################################
232             ## DESTRUCTOR
233             ###############################################
234             sub DESTROY
235 0     0     {
236             #Still thinking about what I could insert here;
237             }
238             1;
239              
240             __END__