File Coverage

blib/lib/Net/Starnet/DataAccounting.pm
Criterion Covered Total %
statement 90 117 76.9
branch 12 26 46.1
condition 8 24 33.3
subroutine 23 28 82.1
pod 5 5 100.0
total 138 200 69.0


line stmt bran cond sub pod time code
1             package Net::Starnet::DataAccounting;
2              
3             =head1 NAME
4              
5             Net::Starnet::DataAccounting - interface to the SDA protocol
6              
7             =head1 SYNOPSIS
8              
9             use constant SDA_UPDATE_TIME 60;
10             my $sda = Net::Starnet::DataAccounting->new(
11             user => $user,
12             pass => $pass,
13             verbose => $VERBOSE,
14             login => \&login,
15             logout => \&logout,
16             update => \&update,
17             (defined($hostname) ? ( host => $hostname ) : ()),
18             (defined($server) ? ( server => $server ) : ()),
19             );
20             my $connected = $sda->login();
21             if ($connected)
22             {
23             $SIG{INT} = $SIG{TERM} = sub {
24             $sda->logout();
25             exit 0;
26             };
27             while ($connected)
28             {
29             sleep SDA_UPDATE_TIME;
30             $connected = $sda->update();
31             }
32             my $disconnected = $sda->logout();
33             }
34              
35             =head1 DESCRIPTION
36              
37             The Net::Starnet::DataAccounting module provides an interface to the
38             protocol used by the Starnet Data Accounting System. It allows simple
39             login, logout and health checking.
40              
41             =cut
42              
43 2     2   40083 use 5.006001;
  2         6  
  2         71  
44 2     2   10 use strict;
  2         4  
  2         62  
45 2     2   19 use warnings;
  2         4  
  2         54  
46              
47 2     2   11 use Carp;
  2         3  
  2         136  
48 2     2   1947 use Socket;
  2         8958  
  2         1229  
49 2     2   2153 use IO::Socket;
  2         43376  
  2         11  
50             $|++;
51              
52 2     2   2294 use constant DEBUG => 0;
  2         4  
  2         137  
53 2     2   11 use constant SDA_HOST => '150.203.223.8:8000';
  2         4  
  2         81  
54              
55 2     2   9 use constant SDA_LOGIN => 1;
  2         4  
  2         84  
56 2     2   18 use constant SDA_LOGOUT => 2;
  2         3  
  2         114  
57 2     2   12 use constant SDA_UPDATE => 3;
  2         5  
  2         80  
58              
59 2     2   9 use constant SDA_LOGIN_YES => 1;
  2         3  
  2         78  
60 2     2   9 use constant SDA_LOGIN_NO => 0;
  2         5  
  2         87  
61 2     2   10 use constant SDA_LOGIN_INCORRECT_USERPASS => 1;
  2         4  
  2         67  
62 2     2   15 use constant SDA_LOGIN_NO_QUOTA => 3;
  2         3  
  2         76  
63 2     2   8 use constant SDA_LOGIN_ALREADY_CONNECTED => 4;
  2         3  
  2         77  
64              
65 2     2   8 use constant SDA_UPDATE_YES => 1;
  2         4  
  2         66  
66 2     2   14 use constant SDA_UPDATE_NO => 0;
  2         3  
  2         65  
67              
68 2     2   1179 use constant SDA_UPDATE_TIME => 60;
  2         3  
  2         2293  
69              
70             our @ISA = qw//;
71             our ( $VERSION ) = '$Revision: 1.2 $ ' =~ /\$Revision:\s+([^\s]+)/;
72              
73             my %defaults = (
74             server => '150.203.223.8',
75             port => '8000',
76             client => "Spoon-v$VERSION",
77             );
78              
79              
80             # ========================================================================
81             # Methods
82              
83             =head1 METHODS
84              
85             =over 4
86              
87             =item Net::Starnet::DataAccounting->new(
88              
89             host => $yourhostname,
90             server => $remotehostname,
91             port => $remoteport,
92             user => $username,
93             pass => $password,
94             client => $clientname,
95             login => \&login,
96             logout => \&logout,
97             update => \&update,
98             verbose => $verbose,
99             )
100              
101             Creates a new SDA connection. Host and server should be either IPs or
102             hostnames. Port is a port number, user and pass are the appropriate
103             username and password. Client is a custom client string for the
104             connection to use.
105              
106             Login, logout and update are routines that will be called after an
107             attempt to send the appropriate message. The routines in question will
108             be passed two parameters: the SDA object and the text response from the
109             server (decoded).
110              
111             Verbose determines whether debugging information will be shown.
112              
113             =cut
114              
115             sub new
116             {
117 1     1 1 21 my $class = shift;
118 1   33     6 $class = ref($class) || $class;
119              
120 1         5 my %opts = @_;
121              
122 1         2 my $hostname;
123 1 50       4 if (exists $opts{host})
124             {
125 0         0 $hostname = $opts{host};
126             }
127             else
128             {
129             $hostname = eval
130 1         2 {
131 1         796 require Sys::Hostname;
132 1         1144 Sys::Hostname::hostname();
133             };
134             }
135 1 50       696 my $ip = gethostbyname($hostname) or die "Couldn't resolve $hostname.\n";
136 1         8 $ip = join('.', unpack('C4', $ip));
137              
138 1   33     10 my $server = $opts{server} || $defaults{server};
139 1 50 33     8 die "Invalid server name.\n" unless (defined $server and length $server);
140 1 50       15 $server = gethostbyname($server) or die "Couldn't resolve $server.\n";
141 1         5 $server = join('.', unpack('C4', $server));
142              
143 1   33     6 my $port = $opts{port} || $defaults{port};
144 1 50 33     7 die "Invalid port number.\n" unless ($port < 65536 and $port > 0);
145            
146 1 50 33     11 die "Invalid username.\n" unless (defined $opts{user} and $opts{user} =~ /^\w+$/);
147 1 50 33     7 die "Invalid password.\n" unless (defined $opts{pass} and $opts{pass} =~ /^\d+$/);
148              
149 1 50 33     16 my $self = bless {
150             host => $ip,
151             server => $server,
152             port => $port,
153             client => $opts{client} || $defaults{client},
154             login => $opts{login},
155             logout => $opts{logout},
156             update => $opts{update},
157             user => $opts{user},
158             pass => $opts{pass},
159             verbose => $opts{verbose} ? 1 : 0,
160             }, $class;
161              
162              
163 1         5 return $self;
164             }
165              
166             =item $sda->verbose($value|)
167              
168             If given a parameter, sets the verbosity. Returns the verbosity in all
169             cases.
170              
171             =cut
172              
173             sub verbose
174             {
175 4     4 1 1232 my $self = shift;
176 4 100       12 $self->{verbose} = $_[0] ? 1 : 0 if (@_);
    100          
177 4         12 return $self->{verbose};
178             }
179              
180             =item $sda->login()
181              
182             Directs the SDA object to attempt to connect to the server. Calls the
183             login routine specified on construction after the attempt is made.
184              
185             =cut
186              
187             sub login
188             {
189 0     0 1 0 my $self = shift;
190 0         0 my $response = $self->_sda_send(SDA_LOGIN);
191 0         0 $self->{login}($self,$response);
192             }
193              
194             =item $sda->logout()
195              
196             Directs the SDA object to attempt to disconnect to the server. Calls the
197             logout routine specified on construction after the attempt is made.
198              
199             =cut
200              
201             sub logout
202             {
203 0     0 1 0 my $self = shift;
204 0         0 my $response = $self->_sda_send(SDA_LOGOUT);
205 0         0 $self->{logout}($self,$response);
206             }
207              
208             =item $sda->update()
209              
210             Directs the SDA object to attempt to update the client's status on the
211             server. Calls the update routine specified on construction after the
212             attempt is made. This function should be called every two minutes or so;
213             ideally more frequently.
214              
215             =cut
216              
217             sub update
218             {
219 0     0 1 0 my $self = shift;
220 0         0 my $response = $self->_sda_send(SDA_UPDATE);
221 0         0 $self->{update}($self,$response);
222             }
223              
224             =back
225              
226             =cut
227              
228             # ========================================================================
229             # Private
230              
231             =begin private
232              
233             =head1 PRIVATE METHODS
234              
235             =over 4
236              
237             =cut
238              
239             # ------------------------------------------------------------------------
240             # SDA Server stuff
241             # ------------------------------------------------------------------------
242              
243             =item $sock = $sda->_sda_connect()
244              
245             Connects to the object's server on the given port and returns the
246             socket.
247              
248             =cut
249              
250             sub _sda_connect
251             {
252 0     0   0 my $self = shift;
253 0         0 my ($host,$port) = @{$self}{qw/server port/};
  0         0  
254 0 0       0 my $sock = IO::Socket::INET->new
255             (
256             PeerAddr => $host,
257             PeerPort => $port,
258             Proto => 'tcp',
259             Type => SOCK_STREAM
260             ) or die "Couldn't connect to $host:$port: $@\n";
261              
262 0         0 return $sock;
263             }
264              
265             =item $result = $sda->_sda_send(ACTION)
266              
267             Where ACTION is one of SDA_UPDATE, SDA_LOGIN, SDA_LOGOUT.
268              
269             Sends an appropriately encoded message to the server (does the
270             connection by calling _sda_connect()), waits for a response and returns
271             the result (decoded).
272              
273             =cut
274              
275             sub _sda_send
276             {
277 0     0   0 my ($self, $number) = @_;
278 0         0 my $sock = $self->_sda_connect();
279 0         0 my ($user, $pass, $ip, $client) = @{$self}{qw/user pass host client/};
  0         0  
280 0         0 my $line = "$number $user $pass $ip 0 $client \n";
281 0 0       0 print ">$line" if DEBUG or $self->verbose;
282 0         0 print $sock ''._encode($line);
283 0         0 my $response = <$sock>;
284 0         0 $response = _decode($response);
285 0 0       0 print "<$response\n" if DEBUG or $self->verbose;
286 0         0 $sock->close;
287 0         0 return $response;
288             }
289              
290             # ------------------------------------------------------------------------
291             # Generic Routines
292             # ------------------------------------------------------------------------
293              
294             =item $encoded = _encode($plain)
295              
296             Encodes $plain according to the SDA encoding method.
297              
298             =cut
299              
300             sub _encode
301             {
302 4     4   391 my $i = 0;
303 4         5 my ($s) = (@_);
304 4         12 $s =~ s/(.)/chr ord($1)+$i++%7/eg;
  186         308  
305 4         12 return $s;
306             }
307              
308             =item $plain = _decode($encoded)
309              
310             Decodes $plain according to the SDA encoding method.
311              
312             =cut
313              
314             sub _decode
315             {
316 4     4   12 my $i = 0;
317 4         5 my ($s) = (@_);
318 4         16 $s =~ s/(.)/chr ord($1)-$i++%7/eg;
  186         295  
319 4         11 return $s;
320             }
321              
322             =back
323              
324             =end private
325              
326             =cut
327              
328             1;
329             __END__