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   99097 use strict;
  1         9  
  1         28  
2 1     1   4 use warnings;
  1         2  
  1         48  
3             package Net::Finger::Server 0.005;
4             # ABSTRACT: a simple finger server
5              
6 1     1   958 use Package::Generator;
  1         778  
  1         45  
7 1         8 use Sub::Exporter -setup => {
8             collectors => [ '-run' => \'_run_server' ]
9 1     1   738 };
  1         14306  
10              
11             my %already;
12             sub _run_server {
13 0     0   0 my ($class, $value) = @_;
14 0   0     0 $value ||= {};
15              
16 0         0 my %config = %$value;
17              
18 0   0     0 $config{port} ||= 79;
19              
20 0         0 my $pkg = $class;
21 0 0       0 if (my $isa = delete $config{isa}) {
22 0 0       0 eval "require $isa; 1" or die;
23 0   0     0 $pkg = $already{ $class, $isa } ||= Package::Generator->new_package({
24             base => $class,
25             isa => [ $class, $isa ],
26             });
27             }
28              
29 0         0 my $server = $pkg->new(%config);
30 0         0 $server->run;
31             }
32              
33             #pod =head1 SYNOPSIS
34             #pod
35             #pod use Net::Finger::Server -run;
36             #pod
37             #pod That's it! You might need to run with privs, since by default it will bind to
38             #pod port 79.
39             #pod
40             #pod You can also:
41             #pod
42             #pod use Net::Finger::Server -run => { port => 1179 };
43             #pod
44             #pod ...if you want.
45             #pod
46             #pod Actually, both of these are sort of moot unless you also provide an C<isa>
47             #pod argument, which sets the base class for the created server.
48             #pod Net::Finger::Server is, for now, written to work as a Net::Server subclass.
49             #pod
50             #pod =head1 DESCRIPTION
51             #pod
52             #pod How can there be no F<finger> servers on the CPAN in 2008? Probably because
53             #pod there weren't any in 1999, and by then it was already too late. Finger might
54             #pod be dead, but it's fun for playing around.
55             #pod
56             #pod Right now Net::Finger::Server uses L<Net::Server|Net::Server>, but that might
57             #pod not last. Stick to the documented interface.
58             #pod
59             #pod Speaking of the documented interface, you'll almost certainly want to subclass
60             #pod Net::Finger::Server to make it do something useful.
61             #pod
62             #pod =cut
63              
64             # {Q1} ::= [{W}|{W}{S}{U}]{C}
65             # {Q2} ::= [{W}{S}][{U}]{H}{C}
66             # {U} ::= username
67             # {H} ::= @hostname | @hostname{H}
68             # {W} ::= /W
69             # {S} ::= <SP> | <SP>{S}
70             # {C} ::= <CRLF>
71              
72             #pod =method username_regex
73             #pod
74             #pod =method hostname_regex
75             #pod
76             #pod The C<username_regex> and C<hostname_regex> methods return regex used to match
77             #pod usernames and hostnames in query strings. They're fairly reasonable, and
78             #pod suggestions for change are welcome. You can replace them, though, without
79             #pod breaking compliance with RFC 1288, since it doesn't define what a hostname or
80             #pod username is.
81             #pod
82             #pod =cut
83              
84 5     5 1 493 sub username_regex { qr{[a-z0-9.]+}i }
85 5     5 1 12 sub hostname_regex { qr{[-_a-z0-9.]+}i }
86              
87             #pod =method listing_reply
88             #pod
89             #pod This method is called when a C<{C}> query is received -- in other words, an
90             #pod empty query, used to request a listing of all users. It is passed a hashref of
91             #pod arguments, of where there is only one right now:
92             #pod
93             #pod verbose - boolean; did client request a verbose reply?
94             #pod
95             #pod The default reply is a rejection notice.
96             #pod
97             #pod =cut
98              
99 1     1 1 5 sub listing_reply { return "listing of users rejected\n"; }
100              
101             #pod =method user_reply
102             #pod
103             #pod This method is called when a C<{Q1}> query is received -- in other words, a
104             #pod request for information about a named user. It is passed the username and a
105             #pod hashref of arguments, of where there is only one right now:
106             #pod
107             #pod verbose - boolean; did client request a verbose reply?
108             #pod
109             #pod The default reply is a rejection notice.
110             #pod
111             #pod =cut
112              
113             sub user_reply {
114 1     1 1 3 my ($self, $username, $arg) = @_;
115 1         6 return "query for information on alleged user <$username> rejected\n";
116             }
117              
118             #pod =method forward_reply
119             #pod
120             #pod This method is called when a C<{Q2}> query is received -- in other words, a
121             #pod request for the server to relay a request to another host. It is passed a
122             #pod hashref of arguments:
123             #pod
124             #pod username - the user named in the query (if any)
125             #pod hosts - an arrayref of the hosts in the query, left to right
126             #pod verbose - boolean; did client request a verbose reply?
127             #pod
128             #pod The default reply is a rejection notice.
129             #pod
130             #pod =cut
131              
132             sub forward_reply {
133 0     0 1 0 my ($self, $arg) = @_;
134 0         0 return "finger forwarding service denied\n";
135             }
136              
137             #pod =method unknown_reply
138             #pod
139             #pod This method is called when the request can't be understood. It is passed the
140             #pod query string.
141             #pod
142             #pod =cut
143              
144             sub unknown_reply {
145 1     1 1 10 my ($self, $query) = @_;
146 1         4 return "could not understand query\n";
147             }
148              
149 0     0   0 sub _read_input_line { return scalar <STDIN> }
150              
151 0     0   0 sub _reply { print $_[1] }
152              
153             sub process_request {
154 4     4 0 2845 my ($self) = @_;
155 4         12 my $query = $self->_read_input_line;
156              
157 4         23 $query =~ s/[\x0d|\x0a]+\z//g;
158              
159 4         8 my $original = $query;
160              
161 4         6 my $verbose = $query =~ s{\A/W\s*}{};
162 4         11 my $u_regex = $self->username_regex;
163 4         10 my $h_regex = $self->hostname_regex;
164              
165 4 100       101 if ($query eq '') {
    100          
    100          
166 1         7 $self->_reply( $self->listing_reply({ verbose => $verbose }));
167 1         6 return;
168             } elsif ($query =~ /\A$u_regex\z/) {
169 1         8 $self->_reply($self->user_reply($query, { verbose => $verbose }));
170 1         5 return;
171             } elsif ($query =~ /\A($u_regex)?((?:\@$h_regex)+)\z/) {
172 1         6 my ($username, $host_string) = ($1, $2);
173 1         4 my @hosts = split /@/, $host_string;
174 1         3 shift @hosts;
175              
176 1         7 $self->_reply(
177             $self->forward_reply({
178             username => $username,
179             hosts => \@hosts,
180             verbose => $verbose,
181             }),
182             );
183 1         23 return;
184             }
185              
186 1         7 $self->_reply( $self->unknown_reply($original) );
187 1         4 return;
188             }
189              
190             1;
191              
192             __END__
193              
194             =pod
195              
196             =encoding UTF-8
197              
198             =head1 NAME
199              
200             Net::Finger::Server - a simple finger server
201              
202             =head1 VERSION
203              
204             version 0.005
205              
206             =head1 SYNOPSIS
207              
208             use Net::Finger::Server -run;
209              
210             That's it! You might need to run with privs, since by default it will bind to
211             port 79.
212              
213             You can also:
214              
215             use Net::Finger::Server -run => { port => 1179 };
216              
217             ...if you want.
218              
219             Actually, both of these are sort of moot unless you also provide an C<isa>
220             argument, which sets the base class for the created server.
221             Net::Finger::Server is, for now, written to work as a Net::Server subclass.
222              
223             =head1 DESCRIPTION
224              
225             How can there be no F<finger> servers on the CPAN in 2008? Probably because
226             there weren't any in 1999, and by then it was already too late. Finger might
227             be dead, but it's fun for playing around.
228              
229             Right now Net::Finger::Server uses L<Net::Server|Net::Server>, but that might
230             not last. Stick to the documented interface.
231              
232             Speaking of the documented interface, you'll almost certainly want to subclass
233             Net::Finger::Server to make it do something useful.
234              
235             =head1 PERL VERSION
236              
237             This library should run on perls released even a long time ago. It should work
238             on any version of perl released in the last five years.
239              
240             Although it may work on older versions of perl, no guarantee is made that the
241             minimum required version will not be increased. The version may be increased
242             for any reason, and there is no promise that patches will be accepted to lower
243             the minimum required perl.
244              
245             =head1 METHODS
246              
247             =head2 username_regex
248              
249             =head2 hostname_regex
250              
251             The C<username_regex> and C<hostname_regex> methods return regex used to match
252             usernames and hostnames in query strings. They're fairly reasonable, and
253             suggestions for change are welcome. You can replace them, though, without
254             breaking compliance with RFC 1288, since it doesn't define what a hostname or
255             username is.
256              
257             =head2 listing_reply
258              
259             This method is called when a C<{C}> query is received -- in other words, an
260             empty query, used to request a listing of all users. It is passed a hashref of
261             arguments, of where there is only one right now:
262              
263             verbose - boolean; did client request a verbose reply?
264              
265             The default reply is a rejection notice.
266              
267             =head2 user_reply
268              
269             This method is called when a C<{Q1}> query is received -- in other words, a
270             request for information about a named user. It is passed the username and a
271             hashref of arguments, of where there is only one right now:
272              
273             verbose - boolean; did client request a verbose reply?
274              
275             The default reply is a rejection notice.
276              
277             =head2 forward_reply
278              
279             This method is called when a C<{Q2}> query is received -- in other words, a
280             request for the server to relay a request to another host. It is passed a
281             hashref of arguments:
282              
283             username - the user named in the query (if any)
284             hosts - an arrayref of the hosts in the query, left to right
285             verbose - boolean; did client request a verbose reply?
286              
287             The default reply is a rejection notice.
288              
289             =head2 unknown_reply
290              
291             This method is called when the request can't be understood. It is passed the
292             query string.
293              
294             =head1 AUTHOR
295              
296             Ricardo SIGNES <cpan@semiotic.systems>
297              
298             =head1 CONTRIBUTOR
299              
300             =for stopwords Ricardo Signes
301              
302             Ricardo Signes <rjbs@semiotic.systems>
303              
304             =head1 COPYRIGHT AND LICENSE
305              
306             This software is copyright (c) 2022 by Ricardo SIGNES.
307              
308             This is free software; you can redistribute it and/or modify it under
309             the same terms as the Perl 5 programming language system itself.
310              
311             =cut