Moving away from string exceptions
[mirror/userdir-ldap.git] / ud-generate
index d209700..8001012 100755 (executable)
@@ -9,6 +9,8 @@
 #   Copyright (c) 2008 Peter Palfrader <peter@palfrader.org>
 #   Copyright (c) 2008 Andreas Barth <aba@not.so.argh.org>
 #   Copyright (c) 2008 Mark Hymers <mhy@debian.org>
 #   Copyright (c) 2008 Peter Palfrader <peter@palfrader.org>
 #   Copyright (c) 2008 Andreas Barth <aba@not.so.argh.org>
 #   Copyright (c) 2008 Mark Hymers <mhy@debian.org>
+#   Copyright (c) 2008 Luk Claes <luk@debian.org>
+#   Copyright (c) 2008 Thomas Viehmann <tv@beamnet.de>
 #
 #   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
 #
 #   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
 
 import string, re, time, ldap, getopt, sys, os, pwd, posix, socket, base64, sha, shutil, errno, tarfile, grp
 from userdir_ldap import *;
 
 import string, re, time, ldap, getopt, sys, os, pwd, posix, socket, base64, sha, shutil, errno, tarfile, grp
 from userdir_ldap import *;
+from userdir_exceptions import *
 
 global Allowed;
 global CurrentHost;
 
 PasswdAttrs = None;
 
 global Allowed;
 global CurrentHost;
 
 PasswdAttrs = None;
+DisabledUsers = []
+RetiredUsers = []
 GroupIDMap = {};
 GroupIDMap = {};
+SubGroupMap = {};
 Allowed = None;
 CurrentHost = "";
 
 Allowed = None;
 CurrentHost = "";
 
@@ -39,6 +45,7 @@ UUID_FORMAT = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
 
 EmailCheck = re.compile("^([^ <>@]+@[^ ,<>@]+)?$");
 BSMTPCheck = re.compile(".*mx 0 (gluck)\.debian\.org\..*",re.DOTALL);
 
 EmailCheck = re.compile("^([^ <>@]+@[^ ,<>@]+)?$");
 BSMTPCheck = re.compile(".*mx 0 (gluck)\.debian\.org\..*",re.DOTALL);
+PurposeHostField = re.compile(r"\[\[([\*\-]?[a-z0-9.\-]*)(?:\|.*)?\]\]")
 DNSZone = ".debian.net"
 Keyrings = ConfModule.sync_keyrings.split(":")
 
 DNSZone = ".debian.net"
 Keyrings = ConfModule.sync_keyrings.split(":")
 
@@ -68,6 +75,36 @@ def DoLink(From,To,File):
    except: pass;
    posix.link(From+File,To+File);
 
    except: pass;
    posix.link(From+File,To+File);
 
+def IsRetired(DnRecord):
+   """
+   Looks for accountStatus in the LDAP record and tries to
+   match it against one of the known retired statuses
+   """
+
+   status = GetAttr(DnRecord,"accountStatus", None)
+   if status is None:
+      return False
+
+   line = status.split()
+   status = line[0]
+   
+   if status == "inactive":
+      return True
+
+   elif status == "memorial":
+      return True
+
+   elif status == "retiring":
+      # We'll give them a few extra days over what we said
+      age = 6 * 31 * 24 * 60 * 60
+      try:
+         if (time.time() - time.mktime(time.strptime(line[1], "%Y-%m-%d"))) > age:
+            return True
+      except IndexError:
+         return False
+
+   return False
+
 # See if this user is in the group list
 def IsInGroup(DnRecord):
   if Allowed == None:
 # See if this user is in the group list
 def IsInGroup(DnRecord):
   if Allowed == None:
@@ -87,9 +124,10 @@ def IsInGroup(DnRecord):
   if DnRecord[1].has_key("supplementaryGid") == 0:
      return 0;
 
   if DnRecord[1].has_key("supplementaryGid") == 0:
      return 0;
 
-  # Check the supplementary groups
-  for I in DnRecord[1]["supplementaryGid"]:
-     if Allowed.has_key(I):
+  supgroups=[]
+  addGroups(supgroups, DnRecord[1]["supplementaryGid"], GetAttr(DnRecord,"uid"))
+  for g in supgroups:
+     if Allowed.has_key(g):
         return 1;
   return 0;
 
         return 1;
   return 0;
 
@@ -120,8 +158,6 @@ def GenPasswd(l,File,HomePrefix,PwdMarker):
    userlist = {}
    # Fetch all the users
    global PasswdAttrs;
    userlist = {}
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    I = 0;
    for x in PasswdAttrs:
 
    I = 0;
    for x in PasswdAttrs:
@@ -164,8 +200,6 @@ def GenShadow(l,File):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    I = 0;
    for x in PasswdAttrs:
 
    I = 0;
    for x in PasswdAttrs:
@@ -212,8 +246,6 @@ def GenShadowSudo(l,File, untrusted):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    for x in PasswdAttrs:
       Pass = '*'
 
    for x in PasswdAttrs:
       Pass = '*'
@@ -262,23 +294,19 @@ def GenSSHShadow(l):
    userfiles = []
 
    global PasswdAttrs;
    userfiles = []
 
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    safe_rmtree(os.path.join(GlobalDir, 'userkeys'))
    safe_makedirs(os.path.join(GlobalDir, 'userkeys'))
 
    for x in PasswdAttrs:
 
    safe_rmtree(os.path.join(GlobalDir, 'userkeys'))
    safe_makedirs(os.path.join(GlobalDir, 'userkeys'))
 
    for x in PasswdAttrs:
-      # If the account is locked, do not write it.
-      # This is a partial stop-gap. The ssh also needs to change this
-      # to ignore ~/.ssh/authorized* files.
-      if (GetAttr(x,"userPassword").find("*LK*") != -1) \
-             or GetAttr(x,"userPassword").startswith("!"):
-         continue;
+
+      if x in DisabledUsers:
+         continue
 
       if x[1].has_key("uidNumber") == 0 or \
          x[1].has_key("sshRSAAuthKey") == 0:
          continue;
 
       if x[1].has_key("uidNumber") == 0 or \
          x[1].has_key("sshRSAAuthKey") == 0:
          continue;
+
       User = GetAttr(x,"uid");
       F = None;
 
       User = GetAttr(x,"uid");
       F = None;
 
@@ -351,6 +379,31 @@ def GenSSHtarballs(userlist, SSHFiles, grouprevmap, target):
    tf.close()
    os.rename(os.path.join(GlobalDir, 'ssh-keys-%s.tar.gz' % CurrentHost), target)
 
    tf.close()
    os.rename(os.path.join(GlobalDir, 'ssh-keys-%s.tar.gz' % CurrentHost), target)
 
+# add a list of groups to existing groups,
+# including all subgroups thereof, recursively.
+# basically this proceduces the transitive hull of the groups in
+# addgroups.
+def addGroups(existingGroups, newGroups, uid):
+   for group in newGroups:
+      # if it's a <group>@host, split it and verify it's on the current host.
+      s = group.split('@', 1)
+      if len(s) == 2 and s[1] != CurrentHost:
+         continue;
+      group = s[0]
+
+      # let's see if we handled this group already
+      if group in existingGroups:
+        continue
+
+      if not GroupIDMap.has_key(group):
+         print "Group", group, "does not exist but", uid, "is in it"
+         continue
+
+      existingGroups.append(group)
+
+      if SubGroupMap.has_key(group):
+         addGroups(existingGroups, SubGroupMap[group], uid)
+
 # Generate the group list
 def GenGroup(l,File):
   grouprevmap = {}
 # Generate the group list
 def GenGroup(l,File):
   grouprevmap = {}
@@ -365,21 +418,19 @@ def GenGroup(l,File):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    # Sort them into a list of groups having a set of users
    for x in PasswdAttrs:
 
    # Sort them into a list of groups having a set of users
    for x in PasswdAttrs:
+      uid = GetAttr(x,"uid")
       if x[1].has_key("uidNumber") == 0 or IsInGroup(x) == 0:
          continue;
       if x[1].has_key("supplementaryGid") == 0:
          continue;
 
       if x[1].has_key("uidNumber") == 0 or IsInGroup(x) == 0:
          continue;
       if x[1].has_key("supplementaryGid") == 0:
          continue;
 
-      for I in x[1]["supplementaryGid"]:
-         if GroupMap.has_key(I):
-            GroupMap[I].append(GetAttr(x,"uid"));
-         else:
-            print "Group does not exist ",I,"but",GetAttr(x,"uid"),"is in it";
+      supgroups=[]
+      addGroups(supgroups, x[1]["supplementaryGid"], uid)
+      for g in supgroups:
+         GroupMap[g].append(uid);
 
    # Output the group file.
    J = 0;
 
    # Output the group file.
    J = 0;
@@ -416,8 +467,6 @@ def GenForward(l,File):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    # Write out the email address for each user
    for x in PasswdAttrs:
 
    # Write out the email address for each user
    for x in PasswdAttrs:
@@ -450,8 +499,6 @@ def GenAllForward(l,File):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    # Write out the email address for each user
    for x in PasswdAttrs:
 
    # Write out the email address for each user
    for x in PasswdAttrs:
@@ -485,8 +532,6 @@ def GenMarkers(l,File):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    # Write out the position for each user
    for x in PasswdAttrs:
 
    # Write out the position for each user
    for x in PasswdAttrs:
@@ -513,19 +558,12 @@ def GenPrivate(l,File):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    # Write out the position for each user
    for x in PasswdAttrs:
       if x[1].has_key("privateSub") == 0:
          continue;
 
 
    # Write out the position for each user
    for x in PasswdAttrs:
       if x[1].has_key("privateSub") == 0:
          continue;
 
-      # If the account is locked, do not write it
-      if (GetAttr(x,"userPassword").find("*LK*") != -1) \
-             or GetAttr(x,"userPassword").startswith("!"):
-         continue;
-
       # If the account has no PGP key, do not write it
       if x[1].has_key("keyFingerPrint") == 0:
          continue;
       # If the account has no PGP key, do not write it
       if x[1].has_key("keyFingerPrint") == 0:
          continue;
@@ -555,8 +593,7 @@ def GenDisabledAccounts(l,File):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
+   global DisabledUsers
 
    I = 0;
    for x in PasswdAttrs:
 
    I = 0;
    for x in PasswdAttrs:
@@ -574,6 +611,8 @@ def GenDisabledAccounts(l,File):
       if Line != "":
          F.write(Sanitize(Line) + "\n")
 
       if Line != "":
          F.write(Sanitize(Line) + "\n")
 
+      DisabledUsers.append(x)
+
   # Oops, something unspeakable happened.
   except:
    Die(File,F,None);
   # Oops, something unspeakable happened.
   except:
    Die(File,F,None);
@@ -588,8 +627,6 @@ def GenMailDisable(l,File):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    for x in PasswdAttrs:
       Reason = None
 
    for x in PasswdAttrs:
       Reason = None
@@ -624,8 +661,6 @@ def GenMailBool(l,File,Key):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    for x in PasswdAttrs:
       Reason = None
 
    for x in PasswdAttrs:
       Reason = None
@@ -661,8 +696,6 @@ def GenMailList(l,File,Key):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    for x in PasswdAttrs:
       Reason = None
 
    for x in PasswdAttrs:
       Reason = None
@@ -705,6 +738,16 @@ def GenMailList(l,File,Key):
    raise;
   Done(File,F,None);
 
    raise;
   Done(File,F,None);
 
+def isRoleAccount(pwEntry):
+   if not pwEntry.has_key("objectClass"):
+      raise "pwEntry has no objectClass"
+   oc =  pwEntry['objectClass']
+   try:
+      i = oc.index('debianRoleAccount')
+      return True
+   except ValueError:
+      return False
+
 # Generate the DNS Zone file
 def GenDNS(l,File,HomePrefix):
   F = None;
 # Generate the DNS Zone file
 def GenDNS(l,File,HomePrefix):
   F = None;
@@ -713,8 +756,6 @@ def GenDNS(l,File,HomePrefix):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    # Write out the zone file entry for each user
    for x in PasswdAttrs:
 
    # Write out the zone file entry for each user
    for x in PasswdAttrs:
@@ -722,7 +763,7 @@ def GenDNS(l,File,HomePrefix):
          continue;
 
       # If the account has no PGP key, do not write it
          continue;
 
       # If the account has no PGP key, do not write it
-      if x[1].has_key("keyFingerPrint") == 0:
+      if x[1].has_key("keyFingerPrint") == 0 and not isRoleAccount(x[1]):
          continue;
       try:
          F.write("; %s\n"%(EmailAddress(x)));
          continue;
       try:
          F.write("; %s\n"%(EmailAddress(x)));
@@ -769,7 +810,7 @@ def GenSSHFP(l,File,HomePrefix):
    # Fetch all the hosts
    global HostAttrs
    if HostAttrs == None:
    # Fetch all the hosts
    global HostAttrs
    if HostAttrs == None:
-      raise "No Hosts"
+      raise UDEmptyList, "No Hosts"
 
    for x in HostAttrs:
       if x[1].has_key("hostname") == 0 or \
 
    for x in HostAttrs:
       if x[1].has_key("hostname") == 0 or \
@@ -803,8 +844,6 @@ def GenBSMTP(l,File,HomePrefix):
 
    # Fetch all the users
    global PasswdAttrs;
 
    # Fetch all the users
    global PasswdAttrs;
-   if PasswdAttrs == None:
-      raise "No Users";
 
    # Write out the zone file entry for each user
    for x in PasswdAttrs:
 
    # Write out the zone file entry for each user
    for x in PasswdAttrs:
@@ -849,9 +888,10 @@ def HostToIP(Host):
         except socket.gaierror, (code):
             if code[0] != -2: raise
         IPAdresses = []
         except socket.gaierror, (code):
             if code[0] != -2: raise
         IPAdresses = []
-        for addr in IPAdressesT:
-            if addr[0] == socket.AF_INET: IPAdresses += [addr[1], "::ffff:"+addr[1]]
-            else: IPAdresses += [addr[1]]
+        if not IPAdressesT is None:
+            for addr in IPAdressesT:
+               if addr[0] == socket.AF_INET: IPAdresses += [addr[1], "::ffff:"+addr[1]]
+               else: IPAdresses += [addr[1]]
         HostToIPCache[Host] = IPAdresses
     return HostToIPCache[Host]
 
         HostToIPCache[Host] = IPAdresses
     return HostToIPCache[Host]
 
@@ -866,7 +906,7 @@ def GenSSHKnown(l,File,mode=None):
 
    global HostAttrs
    if HostAttrs == None:
 
    global HostAttrs
    if HostAttrs == None:
-      raise "No Hosts";
+      raise UDEmptyList, "No Hosts"
 
    for x in HostAttrs:
       if x[1].has_key("hostname") == 0 or \
 
    for x in HostAttrs:
       if x[1].has_key("hostname") == 0 or \
@@ -874,8 +914,27 @@ def GenSSHKnown(l,File,mode=None):
          continue;
       Host = GetAttr(x,"hostname");
       HostNames = [ Host ]
          continue;
       Host = GetAttr(x,"hostname");
       HostNames = [ Host ]
-      SHost = Host.find(".")
-      if SHost != None: HostNames += [Host[0:SHost]]
+      if Host.endswith(HostDomain):
+         HostNames.append(Host[:-(len(HostDomain)+1)])
+
+      # in the purpose field [[host|some other text]] (where some other text is optional)
+      # makes a hyperlink on the web thing. we now also add these hosts to the ssh known_hosts
+      # file.  But so that we don't have to add everything we link we can add an asterisk
+      # and say [[*... to ignore it.  In order to be able to add stuff to ssh without
+      # http linking it we also support [[-hostname]] entries.
+      for i in x[1].get("purpose",[]):
+         m = PurposeHostField.match(i)
+         if m:
+            m = m.group(1)
+            # we ignore [[*..]] entries
+            if m.startswith('*'):
+               continue;
+            if m.startswith('-'):
+               m = m[1:]
+            if m:
+               HostNames.append(m)
+               if m.endswith(HostDomain):
+                  HostNames.append(m[:-(len(HostDomain)+1)])
 
       for I in x[1]["sshRSAHostKey"]:
          if mode and mode == 'authorized_keys':
 
       for I in x[1]["sshRSAHostKey"]:
          if mode and mode == 'authorized_keys':
@@ -893,33 +952,44 @@ def GenSSHKnown(l,File,mode=None):
 
 # Generate the debianhosts file (list of all IP addresses)
 def GenHosts(l,File):
 
 # Generate the debianhosts file (list of all IP addresses)
 def GenHosts(l,File):
-  F = None;
+  F = None
   try:
   try:
-   OldMask = os.umask(0022);
-   F = open(File + ".tmp","w",0644);
-   os.umask(OldMask);
-
-   # Fetch all the hosts
-   HostNames = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"hostname=*",\
-                ["hostname"]);
-
-   if HostNames == None:
-      raise "No Hosts";
-
-   for x in HostNames:
-      if x[1].has_key("hostname") == 0:
-         continue;
-      Host = GetAttr(x,"hostname");
-      try:
-        Addr = socket.gethostbyname(Host);
-        F.write(Addr + "\n");
-      except:
-        pass
+    OldMask = os.umask(0022)
+    F = open(File + ".tmp","w",0644)
+    os.umask(OldMask)
+
+    # Fetch all the hosts
+    hostnames = l.search_s(HostBaseDn, ldap.SCOPE_ONELEVEL, "hostname=*",
+                           ["hostname"])
+
+    if hostnames == None:
+       raise UDEmptyList, "No Hosts"
+
+    seen = set()
+    for x in hostnames:
+      host = GetAttr(x,"hostname", None)
+      if host:
+        addrs = []
+        try:
+          addrs += socket.getaddrinfo(host, None, socket.AF_INET)
+        except socket.error:
+          pass
+        try:
+          addrs += socket.getaddrinfo(host, None, socket.AF_INET6)
+        except socket.error:
+          pass
+
+        for addrinfo in addrs:
+          if addrinfo[0] in (socket.AF_INET, socket.AF_INET6):
+            addr = addrinfo[4][0]
+            if addr not in seen:
+              print >> F, addrinfo[4][0]
+              seen.add(addr)
   # Oops, something unspeakable happened.
   except:
   # Oops, something unspeakable happened.
   except:
-   Die(File,F,None);
-   raise;
-  Done(File,F,None);
+    Die(File,F,None)
+    raise
+  Done(File,F,None)
 
 def GenKeyrings(l,OutDir):
   for k in Keyrings:
 
 def GenKeyrings(l,OutDir):
   for k in Keyrings:
@@ -936,13 +1006,15 @@ l.simple_bind_s("uid="+Pass[0]+","+BaseDn,Pass[1]);
 # Fetch all the groups
 GroupIDMap = {};
 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"gid=*",\
 # Fetch all the groups
 GroupIDMap = {};
 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"gid=*",\
-                  ["gid","gidNumber"]);
+                  ["gid","gidNumber","subGroup"]);
 
 
-# Generate the GroupMap and GroupIDMap
+# Generate the SubGroupMap and GroupIDMap
 for x in Attrs:
    if x[1].has_key("gidNumber") == 0:
       continue;
    GroupIDMap[x[1]["gid"][0]] = int(x[1]["gidNumber"][0]);
 for x in Attrs:
    if x[1].has_key("gidNumber") == 0:
       continue;
    GroupIDMap[x[1]["gid"][0]] = int(x[1]["gidNumber"][0]);
+   if x[1].has_key("subGroup") != 0:
+      SubGroupMap.setdefault(x[1]["gid"][0], []).extend(x[1]["subGroup"]);
 
 # Fetch all the users
 PasswdAttrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=*",\
 
 # Fetch all the users
 PasswdAttrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=*",\
@@ -953,10 +1025,14 @@ PasswdAttrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=*",\
                  "allowedHost","sshRSAAuthKey","dnsZoneEntry","cn","sn",\
                  "keyFingerPrint","privateSub","mailDisableMessage",\
                  "mailGreylisting","mailCallout","mailRBL","mailRHSBL",\
                  "allowedHost","sshRSAAuthKey","dnsZoneEntry","cn","sn",\
                  "keyFingerPrint","privateSub","mailDisableMessage",\
                  "mailGreylisting","mailCallout","mailRBL","mailRHSBL",\
-                 "mailWhitelist", "sudoPassword"]);
+                 "mailWhitelist", "sudoPassword", "objectClass", "accountStatus"])
+
+if PasswdAttrs is None:
+   raise UDEmptyList, "No Users"
+
 # Fetch all the hosts
 HostAttrs    = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"sshRSAHostKey=*",\
 # Fetch all the hosts
 HostAttrs    = l.search_s(HostBaseDn,ldap.SCOPE_ONELEVEL,"sshRSAHostKey=*",\
-                ["hostname","sshRSAHostKey"]);
+                ["hostname","sshRSAHostKey","purpose"]);
 
 # Open the control file
 if len(sys.argv) == 1:
 
 # Open the control file
 if len(sys.argv) == 1:
@@ -966,6 +1042,14 @@ else:
 
 # Generate global things
 GlobalDir = GenerateDir+"/";
 
 # Generate global things
 GlobalDir = GenerateDir+"/";
+GenMailDisable(l,GlobalDir+"mail-disable")
+
+for x in PasswdAttrs:
+   if IsRetired(x):
+      RetiredUsers.append(x)
+
+PasswdAttrs = filter(lambda x: not x in RetiredUsers, PasswdAttrs)
+
 SSHFiles = GenSSHShadow(l);
 GenAllForward(l,GlobalDir+"mail-forward.cdb");
 GenMarkers(l,GlobalDir+"markers");
 SSHFiles = GenSSHShadow(l);
 GenAllForward(l,GlobalDir+"mail-forward.cdb");
 GenMarkers(l,GlobalDir+"markers");
@@ -974,7 +1058,6 @@ GenDisabledAccounts(l,GlobalDir+"disabled-accounts");
 GenSSHKnown(l,GlobalDir+"ssh_known_hosts");
 #GenSSHKnown(l,GlobalDir+"authorized_keys", 'authorized_keys');
 GenHosts(l,GlobalDir+"debianhosts");
 GenSSHKnown(l,GlobalDir+"ssh_known_hosts");
 #GenSSHKnown(l,GlobalDir+"authorized_keys", 'authorized_keys');
 GenHosts(l,GlobalDir+"debianhosts");
-GenMailDisable(l,GlobalDir+"mail-disable");
 GenMailBool(l,GlobalDir+"mail-greylist","mailGreylisting");
 GenMailBool(l,GlobalDir+"mail-callout","mailCallout");
 GenMailList(l,GlobalDir+"mail-rbl","mailRBL");
 GenMailBool(l,GlobalDir+"mail-greylist","mailGreylisting");
 GenMailBool(l,GlobalDir+"mail-callout","mailCallout");
 GenMailList(l,GlobalDir+"mail-rbl","mailRBL");
@@ -985,6 +1068,8 @@ GenKeyrings(l,GlobalDir);
 # Compatibility.
 GenForward(l,GlobalDir+"forward-alias");
 
 # Compatibility.
 GenForward(l,GlobalDir+"forward-alias");
 
+PasswdAttrs = filter(lambda x: not x in DisabledUsers, PasswdAttrs)
+
 while(1):
    Line = F.readline();
    if Line == "":
 while(1):
    Line = F.readline();
    if Line == "":