#!/usr/bin/perl
#
# ldap2nis - Update the GECOS fields for Unix accounts based on information in
# an LDAP server.  This script makes use of the FreeBSD pw(8) command, and the
# Mozilla::LDAP CPAN modules.
# 
# This script was created February, 2001 by Danny Howard, of Tellme Networks.
# The author can be contacted by e-mailing dannyman@toldme.com.
# 
# Tellme Open Source License
# 
# Permission is hereby granted under the copyrights of Tellme, free of charge,
# to any person obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to the
# following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

# Your LDAP server and base DN.
my $ldap_serv = "ldap.your.com";
my $ldap_base = "dc=your,dc=com";

# The UID range you wish to sync against LDAP.
my $nis_minid = 500;
my $nis_maxid = 50000;

# "Special" accounts that you do not wish to attempt to update against LDAP,
# perhaps because they are not in LDAP, and you want to run this script out of
# cron without seeing the same damn error message all the time.  Ideally, you
# filter out accounts with the $nis_minid and $nis_maxid, but who has an ideal
# set-up? :)
my @exclude = ( 'postfix', 'operator' );

# LDAP
use Mozilla::LDAP::Conn;
use Mozilla::LDAP::Utils;
my $conn = new Mozilla::LDAP::Conn("$ldap_serv");
die "Couldn't connect to LDAP server $ldap_serv: $!\n" unless $conn;

for (@exclude) { $excludes{$_}++; } # Array -> Hash makes it easy to
                                    # check for exclusions

# Iterate through your Unix passwd database.
while ( my @nis = getpwent() ) {
	if( $nis[2] >= $nis_minid && $nis[2] <= $nis_maxid &! $excludes{$nis[0]} ) {
	
		# Search ...
		my $ldap = $conn->search($ldap_base, "(ou=People)", "(uid=$nis[0])");

		# Change warn to die if you want this error condition to be
		# fatal.
		if(! $ldap ) {
			warn "*** NIS user $nis[0] not found in LDAP!\n";
		}

		# Aseemble the GECOS field, which is:
		# "name", "office", "office phone", "home phone"
		# We fill this in with these LDAP attributes:
		# "cn", (nothing), "telephonenumber", "homephone"
		# You will probably want to tweak this to match your schema.
		my $gecos = "$ldap->{cn}[0],,$ldap->{telephonenumber}[0],$ldap->{homephone}[0]";
		$gecos =~ s/,://g; # Keep these out of your GECOS

		# Update only if there is a discrepancy.  The only output this
		# script generates are errors and whether it has to actually
		# update anything.  This makes it more cronnable.  If you are
		# not running FreeBSD, you'll have to find a satisfactory way
		# to update your GECOS field.
		if( $nis[6] ne $gecos ) {
			print "$nis[0]: $nis[6] -> $gecos\n";
			system("/usr/sbin/pw", "usermod", $nis[0], '-c', "$gecos");
		}

	}
}
