-
Notifications
You must be signed in to change notification settings - Fork 0
/
Fingerd.pm
112 lines (79 loc) · 2.48 KB
/
Fingerd.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use strict;
use warnings;
package Git::Fingerd;
use Net::Finger::Server 0.003;
BEGIN { our @ISA = qw(Net::Finger::Server); }
# ABSTRACT: let people finger your git server for... some reason
use List::Util qw(max);
use SUPER;
use String::Truncate qw(elide);
use Text::Table;
=head1 DESCRIPTION
This module implements a simple C<finger> server that describes the contents of
a server that hosts git repositories. You can finger C<@servername> for a
listing of repositories and finger C<repo@servername> for information about
a single repository.
This was meant to provide a simple example for Net::Finger::Server, but enough
people asked for the code that I've released it as something reusable. Here's
an example program using Git::Fingerd:
#!/usr/bin/perl
use Git::Fingerd -run => {
isa => 'Net::Server::INET',
basedir => '/var/lib/git',
};
This program could then run out of F<xinetd>.
=cut
sub new {
my ($class, %config) = @_;
my $basedir = delete $config{basedir} || Carp::croak('no basedir supplied');
my $self = $class->SUPER(%config, log_level => 0);
$self->{__PACKAGE__}{basedir} = $basedir;
return $self;
}
sub basedir { $_[0]->{__PACKAGE__}{basedir} }
sub username_regex { qr{[-a-z0-9]+}i }
sub listing_reply {
my $basedir = $_[0]->basedir;
my @dirs = sort <$basedir/*>;
my $table = Text::Table->new('Repository', ' Description');
my %repo;
for my $i (reverse 0 .. $#dirs) {
my $dir = $dirs[$i];
my $mode = (stat $dir)[2];
unless ($mode & 1) {
splice @dirs, $i, 1;
next;
}
my $repo = $dir;
s{\A$basedir/}{}, s{\.git\z}{} for $repo;
my $desc = `cat $dir/description`;
chomp $desc;
$repo{ $repo } = $desc;
}
my $desc_len = 79 - 3 - (List::Util::max map { length } keys %repo);
for my $repo (sort { lc $a cmp lc $b } keys %repo) {
$table->add($repo => ' ' . elide($repo{$repo}, $desc_len));
}
return "$table";
}
sub user_reply {
my ($self, $username, $arg) = @_;
my $basedir = $self->basedir;
my $dir = "$basedir/$username.git";
return "unknown repository\n" unless -d $dir;
my $mode = (stat $dir)[2];
return "unknown repository\n" unless $mode & 1;
my $cloneurl = -f "$dir/cloneurl" ? `cat $dir/cloneurl` : '(none)';
my $desc = -f "$dir/description" ? `cat $dir/description` : '(none)';
chomp($cloneurl, $desc);
my $reply = "Project : $username
Desc. : $desc
Clone URL: $cloneurl
";
return $reply;
}
=begin Pod::Coverage
new
basedir
=end Pod::Coverage
1;