#!/usr/bin/env python
# -*- mode: python -*-

#   Copyright (c) 1999       Jason Gunthorpe <jgg@debian.org>
#   Copyright (c) 2003       James Troup <troup@debian.org>
#   Copyright (c) 2004       Joey Schulze <joey@debian.org>
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

# Imports passwd, shadow and group files into the directory.
# You should cleanse the files of anything you do not want to add to the
# directory.
#
# The first step is to call this script to import the passwd file and
# create all the new entries. This should be done on an empty freshly 
# initialized directory with the rootdn/password set in the server.
# The command to execute is
#   ldapimport -a -p ~/passwd
# The -a tells the script to add all the entries it finds, it should be
# used only once.
#
# The next step is to import the shadow file and group, no clensing need be 
# done for 
# this as any entries that do not exist will be ignored (silently)
#  ldapimport -s /etc/shadow -g /etc/group
# 
from __future__ import print_function

import re, time, ldap, getopt, sys
from userdir_ldap import *

DoAdd = 0
WritePasses = 1
Passwd = ""
Shadow = ""
Group = ""

# This parses a gecos field and returns a tuple containing the new normalized
# field and the first, middle and last name of the user. Gecos is formed
# in the standard debian manner with 5 feilds separated by commas
def ParseGecos(Field):
   Gecos = re.split("[,:]",Field)
   cn = ""
   mn = ""
   sn = ""
   if (len(Gecos) >= 1):
      (cn,mn,sn) = NameSplit(Gecos[0])

      # Normalize the gecos field
      if (len(Gecos) > 5):
         Gecos = Gecos[0:4]
      else:
         while (len(Gecos) < 5):
            Gecos.append("")
   else:
      Gecos = ["","","","",""]

   # Reconstruct the gecos after mauling it
   Field = Gecos[0] + "," + Gecos[1] + "," + Gecos[2] + "," + \
           Gecos[3] + "," + Gecos[4]
   return (Field,cn,mn,sn)

# Read the passwd file into the database
def DoPasswd(l,Passwd):
   # Read the passwd file and import it
   Passwd = open(Passwd,"r")
   Outstanding = 0
   while(1):
      Line = Passwd.readline()
      if Line == "":
         break

      Split = re.split("[:\n]",Line)
      (Split[4],cn,mn,sn) = ParseGecos(Split[4])
      # This just tests whether these are integers and throws an
      # exception if not
      int(Split[2])
      int(Split[3])
      Rec = [("uid",Split[0]),
             ("uidNumber",Split[2]),
             ("gidNumber",Split[3]),
             ("gecos",Split[4]),
             ("homeDirectory",Split[5]),
             ("loginShell",Split[6]),
             ("cn",cn),
             ("sn",sn)]

      # Avoid schema check complaints when mn is empty
      if (mn):
          Rec.append(("mn",mn))

      Dn = "uid=" + Split[0] + "," + BaseDn
      print("Importing", Dn)
      sys.stdout.flush()

      DoModify = True

      if (DoAdd == 1):
         try:
            AddRec = Rec[:]
            AddRec.append(("objectClass", UserObjectClasses))
            l.add_s(Dn,AddRec)
            DoModify = False

         except ldap.ALREADY_EXISTS:
            print("exists", end=" ")

      if (DoModify):
          # Send the modify request
          ModRec = [(ldap.MOD_REPLACE, k[0], k[1]) for k in Rec]
          l.modify(Dn,ModRec)
          Outstanding = Outstanding + 1
          Outstanding = FlushOutstanding(l,Outstanding,1)
          print("done")

   FlushOutstanding(l,Outstanding)

# Read the shadow file into the database
def DoShadow(l,Shadow):
   # Read the passwd file and import it
   Shadow = open(Shadow,"r")
   Outstanding = 0
   while(1):
      Line = Shadow.readline()
      if Line == "":
         break

      Split = re.split("[:\n]",Line)
      
      # Ignore system accounts with no password, they do not belong in the
      # directory.
      if (Split[1] == 'x' or Split[1] == '*'):
         print("Ignoring system account,", Split[0])
         continue

      for x in range(2,8):
         int(Split[x])

      Rec = [(ldap.MOD_REPLACE,"shadowLastChange",Split[2]),
             (ldap.MOD_REPLACE,"shadowMin",Split[3]),
             (ldap.MOD_REPLACE,"shadowMax",Split[4]),
             (ldap.MOD_REPLACE,"shadowWarning",Split[5])]

      # Avoid schema violations
      if (Split[6]):
         Rec.append((ldap.MOD_REPLACE,"shadowInactive",Split[6]))

      if (Split[7]):
         Rec.append((ldap.MOD_REPLACE,"shadowExpire",Split[7]))

      if (WritePasses == 1):
         Rec.append((ldap.MOD_REPLACE,"userPassword","{crypt}"+Split[1]))

      Dn = "uid=" + Split[0] + "," + BaseDn
      print("Importing", Dn, end=" ")
      sys.stdout.flush()

      # Send the modify request
      l.modify(Dn,Rec)
      Outstanding = Outstanding + 1
      print("done")
      Outstanding = FlushOutstanding(l,Outstanding,1)
   FlushOutstanding(l,Outstanding)

# Read the group file into the database
def DoGroup(l,Group):
   # Read the passwd file and import it
   Group = open(Group,"r")
   Outstanding = 0
   while(1):
      Line = Group.readline()
      if Line == "":
         break

      # Split up the group information
      Split = re.split("[:\n]",Line)
      Members = re.split("[, ]*",Split[3])
      int(Split[2])

      # Iterate over the membership list and add the membership information
      # To the directory
      Rec = [(ldap.MOD_ADD,"supplementaryGid",Split[0])]
      Counter = 0
      for x in Members:
	 if x == "":
            continue
	    
         Dn = "uid=" + x + "," + BaseDn
         print("Adding", Dn, "to group", Split[0])
	 Counter = Counter+1

         # Send the modify request
         l.modify(Dn,Rec)
         Outstanding = Outstanding + 1
         Outstanding = FlushOutstanding(l,Outstanding,1)
	 
      if Counter == 0:
         continue

      Rec = [(ldap.MOD_REPLACE,"gid",Split[0]),
             (ldap.MOD_REPLACE,"gidNumber",Split[2])]

      Dn = "gid=" + Split[0] + "," + BaseDn
      print("Importing", Dn, end=" ")
      sys.stdout.flush()

      # Unfortunately add_s does not take the same args as modify :|
      if (DoAdd == 1):
         try:
            l.add_s(Dn,[("gid",Split[0]),
                        ("objectClass", GroupObjectClasses)])
         except ldap.ALREADY_EXISTS:
            print("exists", end=" ")

      # Send the modify request
      l.modify(Dn,Rec)
      Outstanding = Outstanding + 1
      print(".")

   FlushOutstanding(l,Outstanding)

# Process options
(options, arguments) = getopt.getopt(sys.argv[1:], "ap:s:g:xu:")
for (switch, val) in options:
   if (switch == '-a'):
      DoAdd = 1
   if (switch == '-x'):
      WritePasses = 0
   elif (switch == '-p'):
      Passwd = val
   elif (switch == '-s'):
      Shadow = val
   elif (switch == '-g'):
      Group = val
   elif (switch == '-u'):
      AdminUser = val

# Main program starts here

# Connect to the ldap server
l = passwdAccessLDAP(BaseDn, AdminUser)

if (Passwd != ""):
   DoPasswd(l,Passwd)

if (Shadow != ""):
   DoShadow(l,Shadow)

if (Group != ""):
   DoGroup(l,Group)
