Added a proper copyright notice
[mirror/userdir-ldap.git] / ud-fingerserv
1 #!/usr/bin/perl
2 # $Id: ud-fingerserv,v 1.19 2004/11/18 19:10:57 joey Exp $
3
4 # (c) 1999 Randolph Chung. Licensed under the GPL. <tausq@debian.org>
5 # (c) 2004 Martin Schulze. Licensed under the GPL. <joey@debian.org>
6
7 use lib '/var/www/userdir-ldap/';
8 #use lib '/home/randolph/projects/userdir-ldap/web';
9 use strict vars;
10 use IO::Handle;
11 use IO::Socket;
12 use POSIX qw(:sys_wait_h);
13 use Getopt::Std;
14 use Util;
15 use Net::LDAP qw(:all);
16
17 # Global settings...
18 my %config = &Util::ReadConfigFile;
19 my %opts;
20 getopts("iqhv", \%opts);
21 my $use_inetd = $config{use_inetd} || $opts{i}; 
22 $| = 1;
23
24 my %attrs = (
25   'cn' => 'First name',
26   'mn' => 'Middle name',
27   'sn' => 'Last name',
28   'email' => 'Email',
29   'keyfingerprint' => 'Fingerprint',
30   'key' => 'Key block',
31   'ircnick' => 'IRC nickname',
32   'icquin' => 'ICQ UIN',
33   'jabberjid' => 'Jabber ID',
34   'labeleduri' => 'URL'
35 );
36
37 my @summarykeys = ('cn', 'mn', 'sn', 'email', 'labeleduri', 'ircnick', 'icquin', 'jabberjid', 'keyfingerprint', 'key');
38
39 $SIG{__DIE__} = \&DieHandler;
40 $SIG{INT} = \&DieHandler;
41 $SIG{CHLD} = \&Reaper;
42
43 &help if (defined($opts{h}));
44 #my $logfh = STDOUT; #TODO
45
46 &log("Binding to LDAP server at $config{ldaphost}") if (defined($opts{v}));
47 my $ldap = Net::LDAP->new($config{ldaphost}) || die $1; 
48 $ldap->bind;
49
50 if (!$use_inetd) {
51   &log("Binding to port 79") if (defined($opts{v}));
52   my $server = IO::Socket::INET->new(Proto => 'tcp', 
53                                      LocalPort => 'finger(79)',
54                                      Listen => SOMAXCONN,
55                                      Reuse => 1);
56
57   die "Cannot listen on finger port" unless $server;
58   &log("[Server listening for connections]");
59
60   my ($pid, $client, $hostinfo);
61
62   while ($client = $server->accept()) {
63     &log("Forking to handle client request") if (defined($opts{v}));
64     next if $pid = fork; # parent
65     die "fork: $!" unless defined $pid;
66   
67     # child
68     $client->autoflush(1);
69     my $hostinfo = gethostbyaddr($client->peeraddr, AF_INET);
70     &log(sprintf("[Connect from %s]", $hostinfo || $client->peerhost));
71     my $query = &readdata($client);
72     &ProcessQuery($client, $query) if (defined($query));
73     $client->close;
74     exit;
75   } continue {
76     $client->close;
77   }
78 } else { # inetd
79   &log("inetd mode");
80   my $sockaddr = getpeername(STDIN);
81   if ($sockaddr) {
82     my ($port, $addr) = unpack_sockaddr_in(getpeername(STDIN));
83     &log(sprintf("[Connect from %s (%s)]", gethostbyaddr($addr, AF_INET), inet_ntoa($addr)));
84   } else {
85     &log("[Connect via terminal]");
86   }
87   my $query = &readdata(\*STDIN);
88   &ProcessQuery(\*STDOUT, $query) if (defined($query));
89   exit;
90 }
91
92 $ldap->unbind;
93
94 sub DieHandler {
95   $ldap->unbind if (defined($ldap));
96   exit 0;
97 }
98
99 sub Reaper {
100   1 until (-1 == waitpid(-1, WNOHANG));
101   $SIG{CHLD} = \&Reaper;
102 }
103
104 sub ProcessQuery {
105   my $client = shift;
106   my $query = shift;
107   
108   my ($uid, $fields, $mesg, $entries, $dn, $key, $pid, $data);
109
110   $query =~ s/[^\/,0-9a-z]//gi; # be paranoid about input
111   my ($uid, $fields) = split(/\//, $query, 2);
112   
113   if (($uid eq "") || ($uid =~ /^help$/i)) {
114     &sendhelp($client);
115     return;
116   }
117   
118   &log("Looking up $uid at $config{basedn}, uid=$uid");
119
120   $mesg = $ldap->search(base  => $config{basedn}, filter => "uid=$uid");
121   $mesg->code && die $mesg->error;
122   $entries = $mesg->as_struct;
123
124   if ($mesg->count == 0) {
125     print $client "$uid not found at db.debian.org\n";
126     exit 0;
127   }
128
129   foreach $dn (sort {$entries->{$a}->{sn}->[0] <=> $entries->{$b}->{sn}->[0]} keys(%$entries)) {
130     $data = $entries->{$dn};
131
132     $data->{email}->[0] = sprintf("%s %s %s <%s>", $data->{cn}->[0],
133                                   $data->{mn}->[0], $data->{sn}->[0],
134                                   $data->{uid}->[0]."\@$config{emailappend}");
135                                   
136     $data->{email}->[0] =~ s/\s+/ /g;                             
137  
138     my @keyfingerprint = ();
139     for (my $i=0; $i <= $#{$data->{'keyfingerprint'}}; $i++) {
140       push (@keyfingerprint, $data->{keyfingerprint}->[$i]);
141       $data->{keyfingerprint}->[$i] = &Util::FormatFingerPrint($data->{keyfingerprint}->[$i]);
142       $data->{keyfingerprint}->[$i] =~ s,&nbsp;, ,;
143     }
144     print $client "$dn\n";
145     if (!$fields) {
146       push (@{$data->{key}}, sprintf ("finger %s/key\@db.debian.org", $uid));
147       foreach $key (@summarykeys) {
148         foreach (@{$data->{$key}}) {
149           print $client "$attrs{$key}: ";
150           print $client "$_\n";
151         }
152       }
153     } else {
154   #     print "$fields\n";
155       foreach $key (split(/,/, $fields)) {
156         if ($key eq 'key') {
157           foreach (@keyfingerprint) {
158             push (@{$data->{key}}, "\n".&Util::FetchKey($_), 0);
159           }
160         }
161         foreach (@{$data->{$key}}) {
162           print $client "$attrs{$key}: ";
163           print $client "$_\n";
164         }
165       }
166     }
167   }
168 }  
169
170 sub help {
171   print "fingerserv [-i | -q | -v | -h]\n";
172   print "-i = inetd mode; otherwise runs standalone\n";
173   print "-q = quiet mode; no output\n";
174   print "-v = verbose mode\n";
175   print "-h = this help message\n";
176   exit 0;
177 }
178
179 sub log {
180   my $msg = shift;
181   return if (defined($opts{q}));
182   
183   my $time = localtime;
184   print STDERR "$time $msg\n";
185 }
186
187 sub readdata {
188   my $fh = shift;
189   my $in = undef;
190   my $out = undef;
191   my $bytesread = 0;
192   my $ret;
193
194   my $flags= fcntl($fh, F_GETFL, 0)
195      or die "Can't get flags for socket: $!\n";
196   fcntl($fh, F_SETFL, $flags | O_NONBLOCK)
197      or die "Can't make socket nonblocking: $!\n";
198                                                 
199   while (($bytesread < 1024) && ($out !~ /\n/)) {
200     $ret = sysread($fh, $in, 1024);
201     return undef if (!defined($ret) || ($ret == 0));
202     $bytesread += $ret;
203     $out .= $in;
204   }
205
206   $out =~ /(.*?)\n/;
207   return $1;
208 }
209
210 sub sendhelp {
211   my $client = shift;
212   
213   print $client "userdir-ldap finger daemon\n";
214   print $client "--------------------------\n";
215   print $client "finger <uid>[/<attributes>]\@db.debian.org\n";
216   print $client "  where uid is the user id of the user\n";
217   print $client "  the optional attributes parameter specifies what to return\n";
218   print $client "    if nothing is specified, all attributes are returned.\n";
219   print $client "    The following attributes are currently supported:\n";
220   foreach (@summarykeys) {
221     print $client "      $_ : $attrs{$_}\n";
222   }
223   print $client "    Multiple attributes can be separated by commas, like this:\n";
224   print $client "    finger tux/email,key\@db.debian.org\n";
225 }