#!/usr/bin/python2
# -*- 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 getopt
import re
import sys

import ldap

from userdir_ldap.ldap import (
    NameSplit, BaseDn, UserObjectClasses, FlushOutstanding, passwdAccessLDAP,
    GroupObjectClasses)

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(lc, 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))
            lc.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]
          lc.modify(Dn, ModRec)
          Outstanding = Outstanding + 1
          Outstanding = FlushOutstanding(lc, Outstanding, 1)
          print("done")

   FlushOutstanding(lc, Outstanding)


# Read the shadow file into the database
def DoShadow(lc, 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
      lc.modify(Dn, Rec)
      Outstanding = Outstanding + 1
      print("done")
      Outstanding = FlushOutstanding(lc, Outstanding, 1)
   FlushOutstanding(lc, Outstanding)


# Read the group file into the database
def DoGroup(lc, 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
         lc.modify(Dn, Rec)
         Outstanding = Outstanding + 1
         Outstanding = FlushOutstanding(lc, 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:
            lc.add_s(Dn, [("gid", Split[0]),
                          ("objectClass", GroupObjectClasses)])
         except ldap.ALREADY_EXISTS:
            print("exists", end=" ")

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

   FlushOutstanding(lc, 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
lc = passwdAccessLDAP(BaseDn, AdminUser)

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

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

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