File Coverage

blib/lib/Win32/Girder/IEvent/Server.pm
Criterion Covered Total %
statement 79 111 71.1
branch 18 30 60.0
condition 4 10 40.0
subroutine 14 16 87.5
pod 5 9 55.5
total 120 176 68.1


line stmt bran cond sub pod time code
1             package Win32::Girder::IEvent::Server;
2              
3             #==============================================================================#
4              
5             =head1 NAME
6              
7             Win32::Girder::IEvent::Server - Perl API to the Win32 Girder Internet Events Server
8              
9             =head1 SYNOPSIS
10              
11             use Win32::Girder::IEvent::Server;
12             my $gs = Win32::Girder::IEvent::Server->new();
13             my $event = $gs->wait_for_event();
14              
15             =head1 DESCRIPTION
16              
17             Girder is a Windows automation tool, originally designed to receive commands
18             from IR remote controls. The server is used for receiving 'Event Strings' from
19             a Girder instance or a compatible client.
20              
21             =head2 METHODS
22              
23             =over 4
24              
25             =cut
26              
27             #==============================================================================#
28              
29             require 5.6.0;
30              
31 3     3   12679 use strict;
  3         7  
  3         101  
32 3     3   14 use warnings::register;
  3         7  
  3         420  
33 3     3   1161 use IO::Socket;
  3         25233  
  3         15  
34              
35 3         275 use Win32::Girder::IEvent::Common qw(
36             hash_password
37             $def_pass
38             $def_port
39 3     3   2455 );
  3         4  
40              
41 3     3   13 use base qw(IO::Socket::INET);
  3         3  
  3         249  
42              
43             our $VERSION = 0.01;
44              
45 3     3   14 use constant BUFSIZ => 1024;
  3         6  
  3         3716  
46              
47             #==============================================================================#
48              
49             =item Win32::Girder::IEvent::Server->new([ARGS]);
50              
51             Create a new server object. The server object inherits the IO::Socket::INET
52             object and so the constructor can take all the IO::Socket::INET methods.
53             However the only relavent ones are:
54              
55             B<( LocalPort =E $port )>
56              
57             The port on which to run the server. Defaults to 1024 if not specified.
58              
59             Girder specific parameters are:
60              
61             B<( PassWord =E $mypass )>
62              
63             The password needed for access to the server. Defaults to 'NewDefPWD'. Note
64             that passwords are NOT sent plain text accross the wire.
65              
66             =cut
67              
68             sub new {
69 2     2 1 992 my ($pack,%opts) = @_;
70              
71 2   33     14 $opts{'LocalPort'} ||= $def_port;
72 2   50     16 $opts{'Listen'} ||= SOMAXCONN;
73 2   50     20 $opts{'Protot'} ||= 'udp';
74              
75 2   33     34 my $obj = $pack->SUPER::new(%opts) || do {
76             warnings::warn "Could not create server socket: $!";
77             return 0;
78             };
79              
80 2 50       800 if (defined(my $pass = $opts{PassWord})) {
81 2         8 $$obj->{_girder_pass} = $pass;
82             } else {
83 0         0 $$obj->{_girder_pass} = $def_pass;
84             }
85              
86             # Register with global server list
87              
88              
89 2         6 $$obj->{_girder_rin} = '';
90 2         6 $$obj->{_girder_clients} = [];
91 2         8 $$obj->{_girder_authenticated} = {};
92              
93 2         22 vec($$obj->{_girder_rin},$obj->fileno,1) = 1;
94              
95 2         22 return $obj;
96             }
97              
98              
99             #==============================================================================#
100              
101             =item my $event = $gs->wait_for_event([timeout]);
102              
103             Wait for a client to send an event, or until the optional timeout. Returns
104             the event_sting received. Waiting is implemented using select() and will only
105             work on platforms supporting select(). Timeout is in seconds with the same
106             resolution as select().
107              
108             =cut
109              
110             sub wait_for_event {
111 1     1 1 1527 my ($obj,@opts) = @_;
112            
113 1         41 my $timeout = undef;
114 1 50       46 $timeout = $opts[0] if @opts;
115            
116 1         8 my $event_string = 0;
117              
118             LOOP: {
119 1 50       14 if (select(my $rout = $$obj->{_girder_rin}, undef, undef, $timeout)) {
  4         1004785  
120 4 100       66 if (defined(my $event = $obj->handle_events())) {
121 1         15 return $event;
122             } else {
123 3         16 redo LOOP;
124             }
125             } else {
126 0         0 return undef;
127             }
128             }
129             }
130              
131              
132             #==============================================================================#
133              
134             =item $gs->handle_events();
135              
136             This function is usefull if you do not want to block on a wait_for_events()
137             call. You can select() (for read) on the server object, and when there is
138             something to read, you call handle_events(). It returns the $event_sting if
139             there was one, or undef if there was some other activity (usually a client
140             connect or disconnect).
141              
142             =cut
143              
144             sub handle_events {
145 4     4 1 7 my ($obj) = @_;
146              
147             # Repeat select to find out who was talking to us.
148 4 50       47 if (select(my $rout = $$obj->{_girder_rin}, undef, undef, 0)) {
149              
150             # Is it the server
151 4 100       38 if (vec($rout,$obj->fileno,1) == 1) {
152 1         39 $obj->accept();
153 1         6 return undef;
154             } else {
155 3         41 $obj->handle_client_input($rout);
156             }
157              
158             } else {
159 0         0 warnings::warn "Uh - no events to handle although handle_events called";
160 0         0 return undef;
161             }
162             }
163              
164             #==============================================================================#
165             # IUO
166             #
167              
168             sub accept {
169 1     1 1 7 my ($obj) = @_;
170 1         34 accept(my $cli = IO::Socket::INET->new, $obj);
171 1 50       305 if ($obj->valid_client($cli)) {
172 1         21 vec($$obj->{_girder_rin},$cli->fileno,1) = 1;
173 1         18 $$obj->{_girder_authenticated}->{$cli->fileno} = undef;
174 1         23 push @{$$obj->{_girder_clients}}, $cli;
  1         5  
175 1         4 return $cli;
176             } else {
177 0         0 $cli->close;
178 0         0 return undef;
179             }
180             }
181              
182             sub valid_client {
183             #warn "Client connect";
184 1     1 0 16 return 1;
185             }
186              
187              
188             sub drop_client {
189 0     0 0 0 my ($obj,$cli) = @_;
190 0         0 vec($$obj->{_girder_rin},$cli->fileno,1) = 0;
191 0         0 delete $$obj->{_girder_authenticated}->{$cli->fileno};
192              
193 0         0 my $n = 0;
194 0         0 foreach my $gcli (@{$$obj->{_girder_clients}}) {
  0         0  
195 0 0       0 if ($cli == $gcli) {
196 0         0 splice(@{$$obj->{_girder_clients}}, $n, 1);
  0         0  
197 0         0 last;
198             }
199 0         0 $n++;
200             }
201              
202              
203 0         0 $cli->close;
204             }
205              
206             sub handle_client_input {
207 3     3 0 7 my ($obj,$rout) = @_;
208 3         4 foreach my $cli (@{$$obj->{_girder_clients}}) {
  3         16  
209 3 50       9 if (vec($rout,$cli->fileno,1) == 1) {
210 3 50       430 if (defined(my $event = $cli->getline())) {
211 3         967 $event =~ s/\r?\n$//;
212 3         13 return $obj->parse_event($cli,$event);
213             } else {
214 0         0 $obj->drop_client($cli);
215 0         0 return undef;
216             }
217             }
218            
219             }
220 0         0 warnings::warn "Internal error - can't find file handle to read from";
221             }
222              
223             sub parse_event {
224 3     3 0 14 my ($obj,$cli,$event) = @_;
225              
226 3 100       14 if (!defined $$obj->{_girder_authenticated}->{$cli->fileno}) {
    100          
227              
228             # New client - must say 'quintessence' to get a cookie
229 1 50       14 if ($event eq 'quintessence') {
230 1         2 my $cookie = 'abcd';
231 1         4 $$obj->{_girder_authenticated}->{$cli->fileno}=$cookie;
232 1         33 $cli->print($cookie."\n");
233             } else {
234             # Bad dog - no buscuit
235 0         0 $obj->drop_client($cli);
236             }
237              
238             } elsif ($$obj->{_girder_authenticated}->{$cli->fileno} eq 'AUTH') {
239              
240             # Fully authenticated - this is an event string or close
241 1 50       21 if ($event eq "close") {
242 0         0 $obj->drop_client($cli);
243             } else {
244 1         6 return $event;
245             }
246              
247             } else {
248              
249             # Sent cookie - waiting for reply
250 1         22 my $cookie = $$obj->{_girder_authenticated}->{$cli->fileno};
251 1 50       18 if ($event eq hash_password($cookie,$$obj->{_girder_pass})) {
252             # Correctly authenticted
253 1         5 $cli->print("accept\n");
254 1         31 $$obj->{_girder_authenticated}->{$cli->fileno}='AUTH';
255             } else {
256             # Bad password
257 0         0 $obj->drop_client($cli);
258             }
259             }
260 2         89 return undef;
261             }
262              
263             #==============================================================================#
264              
265             =item $gs->close();
266              
267             Politly shut down the server, dropping all active clients first.
268              
269             =cut
270              
271             sub close {
272 0     0 1 0 die "oh shit";
273 0         0 my ($obj,@opts) = @_;
274              
275             # Shutdown all the servers clients
276 0         0 foreach my $cli (@{$$obj->{_girder_clients}}) {
  0         0  
277 0         0 $cli->close();
278             }
279              
280 0         0 $$obj->{_girder_clients} = [];
281 0         0 $$obj->{_girder_rin} = '';
282              
283             # Shutdown the server
284 0         0 $obj->SUPER::close(@opts);
285             }
286            
287              
288             #==============================================================================#
289              
290             sub DESTROY {
291 2     2   544762 my ($obj) = @_;
292 2         10 $$obj->{_girder_pass} = undef;
293 2         9 $$obj->{_girder_rin} = undef;
294 2         6 $$obj->{_girder_clients} = undef;
295 2         166 $obj->SUPER::DESTROY();
296             }
297              
298              
299             #==============================================================================#
300              
301             =back
302              
303             =head1 AUTHOR
304              
305             This module is Copyright (c) 2002 Gavin Brock gbrock@cpan.org. All rights
306             reserved. This program is free software; you can redistribute it and/or
307             modify it under the same terms as Perl itself.
308              
309             The Girder application is Copyright (c) Ron Bessems. Please see the
310             'copying.txt' that came with your copy of Girder or visit http://www.girder.nl
311             for contact information.
312              
313             =head1 SEE ALSO
314              
315             The Girder home page http://www.girder.nl
316              
317             L.
318              
319             L.
320              
321             =cut
322              
323             # That's all folks..
324             #==============================================================================#