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