File Coverage

blib/lib/Net/Finger/Server.pm
Criterion Covered Total %
statement 38 52 73.0
branch 6 10 60.0
condition 0 7 0.0
subroutine 10 14 71.4
pod 6 7 85.7
total 60 90 66.6


line stmt bran cond sub pod time code
1 1     1   21209 use strict;
  1         4  
  1         32  
2 1     1   4 use warnings;
  1         1  
  1         46  
3             package Net::Finger::Server;
4             {
5             $Net::Finger::Server::VERSION = '0.004';
6             }
7             # ABSTRACT: a simple finger server
8              
9 1     1   858 use Package::Generator;
  1         787  
  1         41  
10 1         10 use Sub::Exporter -setup => {
11             collectors => [ '-run' => \'_run_server' ]
12 1     1   1001 };
  1         13767  
13              
14             my %already;
15             sub _run_server {
16 0     0   0 my ($class, $value) = @_;
17 0   0     0 $value ||= {};
18              
19 0         0 my %config = %$value;
20              
21 0   0     0 $config{port} ||= 79;
22              
23 0         0 my $pkg = $class;
24 0 0       0 if (my $isa = delete $config{isa}) {
25 0 0       0 eval "require $isa; 1" or die;
26 0   0     0 $pkg = $already{ $class, $isa } ||= Package::Generator->new_package({
27             base => $class,
28             isa => [ $class, $isa ],
29             });
30             }
31              
32 0         0 my $server = $pkg->new(%config);
33 0         0 $server->run;
34             }
35              
36              
37             # {Q1} ::= [{W}|{W}{S}{U}]{C}
38             # {Q2} ::= [{W}{S}][{U}]{H}{C}
39             # {U} ::= username
40             # {H} ::= @hostname | @hostname{H}
41             # {W} ::= /W
42             # {S} ::= <SP> | <SP>{S}
43             # {C} ::= <CRLF>
44              
45              
46 5     5 1 554 sub username_regex { qr{[a-z0-9.]+}i }
47 5     5 1 20 sub hostname_regex { qr{[-_a-z0-9.]+}i }
48              
49              
50 1     1 1 6 sub listing_reply { return "listing of users rejected\n"; }
51              
52              
53             sub user_reply {
54 1     1 1 2 my ($self, $username, $arg) = @_;
55 1         7 return "query for information on alleged user <$username> rejected\n";
56             }
57              
58              
59             sub forward_reply {
60 0     0 1 0 my ($self, $arg) = @_;
61 0         0 return "finger forwarding service denied\n";
62             }
63              
64              
65             sub unknown_reply {
66 1     1 1 3 my ($self, $query) = @_;
67 1         4 return "could not understand query\n";
68             }
69              
70 0     0   0 sub _read_input_line { return scalar <STDIN> }
71              
72 0     0   0 sub _reply { print $_[1] }
73              
74             sub process_request {
75 4     4 0 3196 my ($self) = @_;
76 4         15 my $query = $self->_read_input_line;
77              
78 4         34 $query =~ s/[\x0d|\x0a]+\z//g;
79              
80 4         7 my $original = $query;
81              
82 4         9 my $verbose = $query =~ s{\A/W\s*}{};
83 4         17 my $u_regex = $self->username_regex;
84 4         15 my $h_regex = $self->hostname_regex;
85            
86 4 100       196 if ($query eq '') {
    100          
    100          
87 1         10 $self->_reply( $self->listing_reply({ verbose => $verbose }));
88 1         9 return;
89             } elsif ($query =~ /\A$u_regex\z/) {
90 1         10 $self->_reply($self->user_reply($query, { verbose => $verbose }));
91 1         6 return;
92             } elsif ($query =~ /\A($u_regex)?((?:\@$h_regex)+)\z/) {
93 1         6 my ($username, $host_string) = ($1, $2);
94 1         6 my @hosts = split /@/, $host_string;
95 1         2 shift @hosts;
96              
97 1         9 $self->_reply(
98             $self->forward_reply({
99             username => $username,
100             hosts => \@hosts,
101             verbose => $verbose,
102             }),
103             );
104 1         17 return;
105             }
106              
107 1         10 $self->_reply( $self->unknown_reply($original) );
108 1         6 return;
109             }
110              
111             1;
112              
113             __END__
114              
115             =pod
116              
117             =head1 NAME
118              
119             Net::Finger::Server - a simple finger server
120              
121             =head1 VERSION
122              
123             version 0.004
124              
125             =head1 SYNOPSIS
126              
127             use Net::Finger::Server -run;
128              
129             That's it! You might need to run with privs, since by default it will bind to
130             port 79.
131              
132             You can also:
133              
134             use Net::Finger::Server -run => { port => 1179 };
135              
136             ...if you want.
137              
138             Actually, both of these are sort of moot unless you also provide an C<isa>
139             argument, which sets the base class for the created server.
140             Net::Finger::Server is, for now, written to work as a Net::Server subclass.
141              
142             =head1 DESCRIPTION
143              
144             How can there be no F<finger> servers on the CPAN in 2008? Probably because
145             there weren't any in 1999, and by then it was already too late. Finger might
146             be dead, but it's fun for playing around.
147              
148             Right now Net::Finger::Server uses L<Net::Server|Net::Server>, but that might
149             not last. Stick to the documented interface.
150              
151             Speaking of the documented interface, you'll almost certainly want to subclass
152             Net::Finger::Server to make it do something useful.
153              
154             =head1 METHODS
155              
156             =head2 username_regex
157              
158             =head2 hostname_regex
159              
160             The C<username_regex> and C<hostname_regex> methods return regex used to match
161             usernames and hostnames in query strings. They're fairly reasonable, and
162             suggestions for change are welcome. You can replace them, though, without
163             breaking compliance with RFC 1288, since it doesn't define what a hostname or
164             username is.
165              
166             =head2 listing_reply
167              
168             This method is called when a C<{C}> query is received -- in other words, an
169             empty query, used to request a listing of all users. It is passed a hashref of
170             arguments, of where there is only one right now:
171              
172             verbose - boolean; did client request a verbose reply?
173              
174             The default reply is a rejection notice.
175              
176             =head2 user_reply
177              
178             This method is called when a C<{Q1}> query is received -- in other words, a
179             request for information about a named user. It is passed the username and a
180             hashref of arguments, of where there is only one right now:
181              
182             verbose - boolean; did client request a verbose reply?
183              
184             The default reply is a rejection notice.
185              
186             =head2 forward_reply
187              
188             This method is called when a C<{Q2}> query is received -- in other words, a
189             request for the server to relay a request to another host. It is passed a
190             hashref of arguments:
191              
192             username - the user named in the query (if any)
193             hosts - an arrayref of the hosts in the query, left to right
194             verbose - boolean; did client request a verbose reply?
195              
196             The default reply is a rejection notice.
197              
198             =head2 unknown_reply
199              
200             This method is called when the request can't be understood. It is passed the
201             query string.
202              
203             =head1 AUTHOR
204              
205             Ricardo SIGNES <rjbs@cpan.org>
206              
207             =head1 COPYRIGHT AND LICENSE
208              
209             This software is copyright (c) 2013 by Ricardo SIGNES.
210              
211             This is free software; you can redistribute it and/or modify it under
212             the same terms as the Perl 5 programming language system itself.
213              
214             =cut