From: Tollef Fog Heen Date: Wed, 2 Jan 2019 17:28:31 +0000 (+0100) Subject: Fix a whole lot of pep8 errors X-Git-Tag: userdir-ldap-0.3.93~4 X-Git-Url: https://git.adam-barratt.org.uk/?p=mirror%2Fuserdir-ldap.git;a=commitdiff_plain;h=785a87e2db8c16d8aa2cd8d6227240e37f64f440 Fix a whole lot of pep8 errors --- diff --git a/ud-mailgate b/ud-mailgate index 6abeace..6ee9408 100755 --- a/ud-mailgate +++ b/ud-mailgate @@ -7,10 +7,19 @@ # Copyright (c) 2008 Joerg Jaspert # Copyright (c) 2010 Helmut Grohne -import userdir_gpg, userdir_ldap, sys, traceback, time, ldap, os, commands -import pwd, tempfile +import userdir_gpg +import userdir_ldap +import sys +import traceback +import time +import ldap +import os +import commands +import pwd +import tempfile import subprocess -import email, email.parser +import email +import email.parser import binascii from userdir_gpg import * @@ -26,6 +35,7 @@ ReplayCacheFile = ConfModule.replaycachefile SSHFingerprintFile = ConfModule.fingerprintfile UUID_FORMAT = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' +machine_regex = re.compile("^[0-9a-zA-Z.-]+$") EX_TEMPFAIL = 75 EX_PERMFAIL = 65 # EX_DATAERR @@ -37,18 +47,18 @@ mailRHSBL = {} mailWhitelist = {} SeenList = {} DNS = {} -ValidHostNames = [] # will be initialized in later +ValidHostNames = [] # will be initialized in later -SSHFingerprint = re.compile('^(\d+) ([0-9a-f\:]{47}|SHA256:[0-9A-Za-z/+]{43}) (.+)$') -SSHRSA1Match = re.compile('^^(.* )?\d+ \d+ \d+') +SSHFingerprint = re.compile(r'^(\d+) ([0-9a-f\:]{47}|SHA256:[0-9A-Za-z/+]{43}) (.+)$') +SSHRSA1Match = re.compile(r'^^(.* )?\d+ \d+ \d+') ArbChanges = {"c": "..", - "l": ".*", - "facsimileTelephoneNumber": ".*", - "telephoneNumber": ".*", - "postalAddress": ".*", - "bATVToken": ".*", - "postalCode": ".*", + "l": ".*", + "facsimileTelephoneNumber": ".*", + "telephoneNumber": ".*", + "postalAddress": ".*", + "bATVToken": ".*", + "postalCode": ".*", "loginShell": ".*", "emailForward": "^([^<>@]+@.+)?$", "jabberJID": "^([^<>@]+@.+)?$", @@ -58,12 +68,11 @@ ArbChanges = {"c": "..", "labeledURI": ".*", "birthDate": "^([0-9]{4})([01][0-9])([0-3][0-9])$", "mailDisableMessage": ".*", - "mailGreylisting": "^(TRUE|FALSE)$", - "mailCallout": "^(TRUE|FALSE)$", - "mailDefaultOptions": "^(TRUE|FALSE)$", - "VoIP": ".*", - "mailContentInspectionAction": "^(reject|blackhole|markup)$", -} + "mailGreylisting": "^(TRUE|FALSE)$", + "mailCallout": "^(TRUE|FALSE)$", + "mailDefaultOptions": "^(TRUE|FALSE)$", + "VoIP": ".*", + "mailContentInspectionAction": "^(reject|blackhole|markup)$", } DelItems = {"c": None, "l": None, @@ -76,14 +85,14 @@ DelItems = {"c": None, "ircNick": None, "onVacation": None, "labeledURI": None, - "latitude": None, - "longitude": None, + "latitude": None, + "longitude": None, "icqUin": None, "jabberJID": None, "jpegPhoto": None, "dnsZoneEntry": None, - "sshRSAAuthKey": None, - "birthDate" : None, + "sshRSAAuthKey": None, + "birthDate": None, "mailGreylisting": None, "mailCallout": None, "mailRBL": None, @@ -97,80 +106,81 @@ DelItems = {"c": None, # Decode a GPS location from some common forms -def LocDecode(Str,Dir): - # Check for Decimal degrees, DGM, or DGMS - if re.match("^[+-]?[\d.]+$",Str) != None: - return Str - - Deg = '0' - Min = None - Sec = None - Dr = Dir[0] - - # Check for DDDxMM.MMMM where x = [nsew] - Match = re.match("^(\d+)(["+Dir+"])([\d.]+)$",Str) - if Match != None: - G = Match.groups() - Deg = G[0] - Min = G[2] - Dr = G[1] - - # Check for DD.DD x - Match = re.match("^([\d.]+) ?(["+Dir+"])$",Str) - if Match != None: - G = Match.groups() - Deg = G[0] - Dr = G[1] - - # Check for DD:MM.MM x - Match = re.match("^(\d+):([\d.]+) ?(["+Dir+"])$",Str) - if Match != None: - G = Match.groups() - Deg = G[0] - Min = G[1] - Dr = G[2] - - # Check for DD:MM:SS.SS x - Match = re.match("^(\d+):(\d+):([\d.]+) ?(["+Dir+"])$",Str) - if Match != None: - G = Match.groups() - Deg = G[0] - Min = G[1] - Sec = G[2] - Dr = G[3] - - # Some simple checks - if float(Deg) > 180: - raise UDFormatError, "Bad degrees" - if Min != None and float(Min) > 60: - raise UDFormatError, "Bad minutes" - if Sec != None and float(Sec) > 60: - raise UDFormatError, "Bad seconds" - - # Pad on an extra leading 0 to disambiguate small numbers - if len(Deg) <= 1 or Deg[1] == '.': - Deg = '0' + Deg - if Min != None and (len(Min) <= 1 or Min[1] == '.'): - Min = '0' + Min - if Sec != None and (len(Sec) <= 1 or Sec[1] == '.'): - Sec = '0' + Sec - - # Construct a DGM/DGMS type value from the components. - Res = "+" - if Dr == Dir[1]: - Res = "-" - Res = Res + Deg - if Min != None: - Res = Res + Min - if Sec != None: - Res = Res + Sec - return Res - +def LocDecode(Str, Dir): + # Check for Decimal degrees, DGM, or DGMS + if re.match(r"^[+-]?[\d.]+$", Str) is not None: + return Str + + Deg = '0' + Min = None + Sec = None + Dr = Dir[0] + + # Check for DDDxMM.MMMM where x = [nsew] + Match = re.match(r"^(\d+)([" + Dir + r"])([\d.]+)$", Str) + if Match: + G = Match.groups() + Deg = G[0] + Min = G[2] + Dr = G[1] + + # Check for DD.DD x + Match = re.match(r"^([\d.]+) ?([" + Dir + r"])$", Str) + if Match: + G = Match.groups() + Deg = G[0] + Dr = G[1] + + # Check for DD:MM.MM x + Match = re.match(r"^(\d+):([\d.]+) ?([" + Dir + "])$", Str) + if Match: + G = Match.groups() + Deg = G[0] + Min = G[1] + Dr = G[2] + + # Check for DD:MM:SS.SS x + Match = re.match(r"^(\d+):(\d+):([\d.]+) ?([" + Dir + "])$", Str) + if Match: + G = Match.groups() + Deg = G[0] + Min = G[1] + Sec = G[2] + Dr = G[3] + + # Some simple checks + if float(Deg) > 180: + raise UDFormatError("Bad degrees") + if Min is not None and float(Min) > 60: + raise UDFormatError("Bad minutes") + if Sec is not None and float(Sec) > 60: + raise UDFormatError("Bad seconds") + + # Pad on an extra leading 0 to disambiguate small numbers + if len(Deg) <= 1 or Deg[1] == '.': + Deg = '0' + Deg + if Min is not None and (len(Min) <= 1 or Min[1] == '.'): + Min = '0' + Min + if Sec is not None and (len(Sec) <= 1 or Sec[1] == '.'): + Sec = '0' + Sec + + # Construct a DGM/DGMS type value from the components. + Res = "+" + if Dr == Dir[1]: + Res = "-" + Res = Res + Deg + if Min is not None: + Res = Res + Min + if Sec is not None: + Res = Res + Sec + return Res + + # Handle changing a set of arbitary fields # : value -def DoArbChange(Str,Attrs): - Match = re.match("^([^ :]+): (.*)$",Str) - if Match == None: +def DoArbChange(Str, Attrs): + Match = re.match("^([^ :]+): (.*)$", Str) + if Match is None: return None G = Match.groups() @@ -179,44 +189,21 @@ def DoArbChange(Str,Attrs): if i.lower() == attrName: attrName = i break - if ArbChanges.has_key(attrName) == 0: + if attrName in ArbChanges: return None - if re.match(ArbChanges[attrName],G[1]) == None: - raise UDFormatError, "Item does not match the required format"+ArbChanges[attrName] - - -# if attrName == 'birthDate': -# (re.match("^([0-9]{4})([01][0-9])([0-3][0-9])$",G[1]) { -# $bd_yr = $1; $bd_mo = $2; $bd_day = $3; -# if ($bd_mo > 0 and $bd_mo <= 12 and $bd_day > 0) { -# if ($bd_mo == 2) { -# if ($bd_day == 29 and ($bd_yr == 0 or ($bd_yr % 4 == 0 && ($bd_yr % 100 != 0 || $bd_yr % 400 == 0)))) { -# $bd_ok = 1; -# } elsif ($bd_day <= 28) { -# $bd_ok = 1; -# } -# } elsif ($bd_mo == 4 or $bd_mo == 6 or $bd_mo == 9 or $bd_mo == 11) { -# if ($bd_day <= 30) { -# $bd_ok = 1; -# } -# } else { -# if ($bd_day <= 31) { -# $bd_ok = 1; -# } -# } -# } -# } elsif (not defined($query->param('birthdate')) or $query->param('birthdate') =~ /^\s*$/) { -# $bd_ok = 1; -# } - Attrs.append((ldap.MOD_REPLACE,attrName,value)) - return "Changed entry %s to %s"%(attrName,value) + if re.match(ArbChanges[attrName], G[1]) is None: + raise UDFormatError("Item does not match the required format" + ArbChanges[attrName]) + + Attrs.append((ldap.MOD_REPLACE, attrName, value)) + return "Changed entry %s to %s" % (attrName, value) + # Handle changing a set of arbitary fields # : value -def DoDel(Str,Attrs): - Match = re.match("^del (.*)$",Str) - if Match == None: +def DoDel(Str, Attrs): + Match = re.match("^del (.*)$", Str) + if Match is None: return None G = Match.groups() @@ -225,37 +212,39 @@ def DoDel(Str,Attrs): if i.lower() == attrName: attrName = i break - if DelItems.has_key(attrName) == 0: - return "Cannot erase entry %s"%(attrName) + if attrName not in DelItems: + return "Cannot erase entry %s" % (attrName,) + + Attrs.append((ldap.MOD_DELETE, attrName, None)) + return "Removed entry %s" % (attrName) - Attrs.append((ldap.MOD_DELETE,attrName,None)) - return "Removed entry %s"%(attrName) # Handle a position change message, the line format is: # Lat: -12412.23 Long: +12341.2342 -def DoPosition(Str,Attrs): - Match = re.match("^lat: ([+\-]?[\d:.ns]+(?: ?[ns])?) long: ([+\-]?[\d:.ew]+(?: ?[ew])?)$", Str.lower()) - if Match == None: +def DoPosition(Str, Attrs): + Match = re.match(r"^lat: ([+\-]?[\d:.ns]+(?: ?[ns])?) long: ([+\-]?[\d:.ew]+(?: ?[ew])?)$", Str.lower()) + if Match is None: return None G = Match.groups() try: - sLat = LocDecode(G[0],"ns") - sLong = LocDecode(G[1],"ew") - Lat = DecDegree(sLat,1) - Long = DecDegree(sLong,1) - except: - raise UDFormatError, "Positions were found, but they are not correctly formed" + sLat = LocDecode(G[0], "ns") + sLong = LocDecode(G[1], "ew") + Lat = DecDegree(sLat, 1) + Long = DecDegree(sLong, 1) + except Exception: + raise UDFormatError("Positions were found, but they are not correctly formed") + + Attrs.append((ldap.MOD_REPLACE, "latitude", sLat)) + Attrs.append((ldap.MOD_REPLACE, "longitude", sLong)) + return "Position set to %s/%s (%s/%s decimal degrees)" % (sLat, sLong, Lat, Long) - Attrs.append((ldap.MOD_REPLACE,"latitude",sLat)) - Attrs.append((ldap.MOD_REPLACE,"longitude",sLong)) - return "Position set to %s/%s (%s/%s decimal degrees)"%(sLat,sLong,Lat,Long) # Load bad ssh fingerprints def LoadBadSSH(): f = open(SSHFingerprintFile, "r") bad = [] - FingerprintLine = re.compile('^([0-9a-f\:]{47}).*$') + FingerprintLine = re.compile(r'^([0-9a-f:]{47}).*$') for line in f.readlines(): Match = FingerprintLine.match(line) if Match is not None: @@ -263,18 +252,18 @@ def LoadBadSSH(): bad.append(g[0]) return bad + # Handle an SSH authentication key, the line format is: # [options] 1024 35 13188913666680[..] [comment] # maybe it really should be: # [allowed_hosts=machine1,machine2 ][options ]ssh-rsa keybytes [comment] -machine_regex = re.compile("^[0-9a-zA-Z.-]+$") def DoSSH(Str, Attrs, badkeys, uid): Match = SSH2AuthSplit.match(Str) - if Match == None: + if Match is None: return None g = Match.groups() typekey = g[1] - if Match == None: + if Match is None: Match = SSHRSA1Match.match(Str) if Match is not None: return "RSA1 keys not supported anymore" @@ -304,16 +293,15 @@ def DoSSH(Str, Attrs, badkeys, uid): (result, output) = commands.getstatusoutput(cmd) os.remove(path) if (result != 0): - raise UDExecuteError, "ssh-keygen -l invocation failed!\n%s\n" % (output) + raise UDExecuteError("ssh-keygen -l invocation failed!\n%s\n" % (output)) # format the string again for ldap: if machines: Str = "allowed_hosts=%s %s" % (",".join(machines), Str) - # Head - Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time())) - ErrReplyHead = "From: %s\nCc: %s\nReply-To: %s\nDate: %s\n" % (os.environ['SENDER'],os.environ['SENDER'],ReplyTo,Date) + Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(time.time())) + ErrReplyHead = "From: %s\nCc: %s\nReply-To: %s\nDate: %s\n" % (os.environ['SENDER'], os.environ['SENDER'], ReplyTo, Date) Subst = {} Subst["__ADMIN__"] = ReplyTo Subst["__USER__"] = uid @@ -338,40 +326,40 @@ def DoSSH(Str, Attrs, badkeys, uid): try: # Body Subst["__ERROR__"] = "SSH key with fingerprint %s known as bad key" % (g[1]) - ErrReply = TemplateSubst(Subst,open(TemplatesDir+"admin-info","r").read()) + ErrReply = TemplateSubst(Subst, open(TemplatesDir + "admin-info", "r").read()) - Child = os.popen("/usr/sbin/sendmail -t","w") + Child = os.popen("/usr/sbin/sendmail -t", "w") Child.write(ErrReplyHead) Child.write(ErrReply) - if Child.close() != None: - raise UDExecuteError, "Sendmail gave a non-zero return code" - except: + if Child.close() is not None: + raise UDExecuteError("Sendmail gave a non-zero return code") + except Exception: sys.exit(EX_TEMPFAIL) # And now break and stop processing input, which sends a reply to the user. - raise UDFormatError, "Submitted SSH Key known to be bad and insecure, processing halted, NOTHING MODIFIED AT ALL" + raise UDFormatError("Submitted SSH Key known to be bad and insecure, processing halted, NOTHING MODIFIED AT ALL") global SeenKey if SeenKey: - Attrs.append((ldap.MOD_ADD,"sshRSAAuthKey",Str)) - return "SSH Key added: %s %s [%s]"%(key_size, fingerprint, FormatSSHAuth(Str)) + Attrs.append((ldap.MOD_ADD, "sshRSAAuthKey", Str)) + return "SSH Key added: %s %s [%s]" % (key_size, fingerprint, FormatSSHAuth(Str)) - Attrs.append((ldap.MOD_REPLACE,"sshRSAAuthKey",Str)) + Attrs.append((ldap.MOD_REPLACE, "sshRSAAuthKey", Str)) SeenKey = 1 - return "SSH Keys replaced with: %s %s [%s]"%(key_size, fingerprint, FormatSSHAuth(Str)) + return "SSH Keys replaced with: %s %s [%s]" % (key_size, fingerprint, FormatSSHAuth(Str)) + # Handle changing a dns entry # host IN A 12.12.12.12 # host IN AAAA 1234::5678 # host IN CNAME foo.bar. <- Trailing dot is required # host IN MX foo.bar. <- Trailing dot is required -def DoDNS(Str,Attrs,DnRecord): - cnamerecord = re.match("^[-\w]+\s+IN\s+CNAME\s+([-\w.]+\.)$",Str,re.IGNORECASE) - arecord = re.match('^[-\w]+\s+IN\s+A\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$',Str,re.IGNORECASE) - mxrecord = re.match("^[-\w]+\s+IN\s+MX\s+(\d{1,3})\s+([-\w.]+\.)$",Str,re.IGNORECASE) - txtrecord = re.match("^[-\w]+\s+IN\s+TXT\s+([-\d. a-z\t<>@:]+)", Str, re.IGNORECASE) - #aaaarecord = re.match('^[-\w]+\s+IN\s+AAAA\s+((?:[0-9a-f]{1,4})(?::[0-9a-f]{1,4})*(?::(?:(?::[0-9a-f]{1,4})*|:))?)$',Str,re.IGNORECASE) - aaaarecord = re.match('^[-\w]+\s+IN\s+AAAA\s+([A-F0-9:]{2,39})$',Str,re.IGNORECASE) +def DoDNS(Str, Attrs, DnRecord): + cnamerecord = re.match(r"^[-\w]+\s+IN\s+CNAME\s+([-\w.]+\.)$", Str, re.IGNORECASE) + arecord = re.match(r'^[-\w]+\s+IN\s+A\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$', Str, re.IGNORECASE) + mxrecord = re.match(r"^[-\w]+\s+IN\s+MX\s+(\d{1,3})\s+([-\w.]+\.)$", Str, re.IGNORECASE) + txtrecord = re.match(r"^[-\w]+\s+IN\s+TXT\s+([-\d. a-z\t<>@:]+)", Str, re.IGNORECASE) + aaaarecord = re.match(r'^[-\w]+\s+IN\s+AAAA\s+([A-F0-9:]{2,39})$', Str, re.IGNORECASE) if cnamerecord is None and\ arecord is None and\ @@ -381,34 +369,34 @@ def DoDNS(Str,Attrs,DnRecord): return None # Check if the name is already taken - G = re.match('^([-\w+]+)\s',Str) + G = re.match(r'^([-\w+]+)\s', Str) if G is None: - raise UDFormatError, "Hostname not found although we already passed record syntax checks" + raise UDFormatError("Hostname not found although we already passed record syntax checks") hostname = G.group(1) # Check for collisions - global l + global lc # [JT 20070409 - search for both tab and space suffixed hostnames # since we accept either. It'd probably be better to parse the # incoming string in order to construct what we feed LDAP rather # than just passing it through as is.] filter = "(|(dnsZoneEntry=%s *)(dnsZoneEntry=%s *))" % (hostname, hostname) - Rec = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,filter,["uid"]) + Rec = lc.search_s(BaseDn, ldap.SCOPE_ONELEVEL, filter, ["uid"]) for x in Rec: - if GetAttr(x,"uid") != GetAttr(DnRecord,"uid"): - return "DNS entry is already owned by " + GetAttr(x,"uid") + if GetAttr(x, "uid") != GetAttr(DnRecord, "uid"): + return "DNS entry is already owned by " + GetAttr(x, "uid") global SeenDNS global DNS if cnamerecord: - if DNS.has_key(hostname): - return "CNAME and other RR types not allowed: "+Str + if hostname in DNS: + return "CNAME and other RR types not allowed: " + Str else: DNS[hostname] = 2 else: - if DNS.has_key(hostname) and DNS[hostname] == 2: - return "CNAME and other RR types not allowed: "+Str + if DNS.get(hostname) == 2: + return "CNAME and other RR types not allowed: " + Str else: DNS[hostname] = 1 @@ -419,9 +407,9 @@ def DoDNS(Str,Attrs,DnRecord): elif arecord is not None: ipaddress = arecord.group(1) for quad in ipaddress.split('.'): - if not (int(quad) >=0 and int(quad) <= 255): - return "Invalid quad %s in IP address %s in line %s" %(quad, ipaddress, Str) - sanitized = "%s IN A %s"% (hostname, ipaddress) + if not (int(quad) >= 0 and int(quad) <= 255): + return "Invalid quad %s in IP address %s in line %s" % (quad, ipaddress, Str) + sanitized = "%s IN A %s" % (hostname, ipaddress) elif mxrecord is not None: priority = mxrecord.group(1) mx = mxrecord.group(2) @@ -430,9 +418,9 @@ def DoDNS(Str,Attrs,DnRecord): ipv6address = aaaarecord.group(1) parts = ipv6address.split(':') if len(parts) > 8: - return "Invalid IPv6 address (%s): too many parts"%(ipv6address) + return "Invalid IPv6 address (%s): too many parts" % (ipv6address) if len(parts) <= 2: - return "Invalid IPv6 address (%s): too few parts"%(ipv6address) + return "Invalid IPv6 address (%s): too few parts" % (ipv6address) if parts[0] == "": parts.pop(0) if parts[-1] == "": @@ -440,29 +428,30 @@ def DoDNS(Str,Attrs,DnRecord): seenEmptypart = False for p in parts: if len(p) > 4: - return "Invalid IPv6 address (%s): part %s is longer than 4 characters"%(ipv6address, p) + return "Invalid IPv6 address (%s): part %s is longer than 4 characters" % (ipv6address, p) if p == "": if seenEmptypart: - return "Invalid IPv6 address (%s): more than one :: (nothing in between colons) is not allowed"%(ipv6address) + return "Invalid IPv6 address (%s): more than one :: (nothing in between colons) is not allowed" % (ipv6address) seenEmptypart = True sanitized = "%s IN AAAA %s" % (hostname, ipv6address) else: - raise UDFormatError, "None of the types I recognize was it. I shouldn't be here. confused." + raise UDFormatError("None of the types I recognize was it. I shouldn't be here. confused.") if SeenDNS: - Attrs.append((ldap.MOD_ADD,"dnsZoneEntry",sanitized)) + Attrs.append((ldap.MOD_ADD, "dnsZoneEntry", sanitized)) return "DNS Entry added " + sanitized - Attrs.append((ldap.MOD_REPLACE,"dnsZoneEntry",sanitized)) + Attrs.append((ldap.MOD_REPLACE, "dnsZoneEntry", sanitized)) SeenDNS = 1 - return "DNS Entry replaced with "+sanitized + return "DNS Entry replaced with " + sanitized + # Handle an RBL list (mailRBL, mailRHSBL, mailWhitelist) -def DoRBL(Str,Attrs): +def DoRBL(Str, Attrs): Match = re.compile('^mail(rbl|rhsbl|whitelist) ([-a-z0-9.]+)$').match(Str.lower()) - if Match == None: + if Match is None: return None - + if Match.group(1) == "rbl": Key = "mailRBL" if Match.group(1) == "rhsbl": @@ -472,18 +461,19 @@ def DoRBL(Str,Attrs): Host = Match.group(2) global SeenList - if SeenList.has_key(Key): - Attrs.append((ldap.MOD_ADD,Key,Host)) - return "%s added %s" % (Key,Host) - - Attrs.append((ldap.MOD_REPLACE,Key,Host)) + if Key in SeenList: + Attrs.append((ldap.MOD_ADD, Key, Host)) + return "%s added %s" % (Key, Host) + + Attrs.append((ldap.MOD_REPLACE, Key, Host)) SeenList[Key] = 1 - return "%s replaced with %s" % (Key,Host) + return "%s replaced with %s" % (Key, Host) + # Handle a ConfirmSudoPassword request def DoConfirmSudopassword(Str, SudoPasswd): - Match = re.compile('^confirm sudopassword ('+UUID_FORMAT+') ([a-z0-9.,*-]+) ([0-9a-f]{40})$').match(Str) - if Match == None: + Match = re.compile('^confirm sudopassword (' + UUID_FORMAT + ') ([a-z0-9.,*-]+) ([0-9a-f]{40})$').match(Str) + if Match is None: return None uuid = Match.group(1) @@ -491,7 +481,8 @@ def DoConfirmSudopassword(Str, SudoPasswd): hmac = Match.group(3) SudoPasswd[uuid] = (hosts, hmac) - return "got confirm for sudo password %s on host(s) %s, auth code %s" % (uuid,hosts, hmac) + return "got confirm for sudo password %s on host(s) %s, auth code %s" % (uuid, hosts, hmac) + def FinishConfirmSudopassword(l, uid, Attrs, SudoPasswd): result = "\n" @@ -499,131 +490,132 @@ def FinishConfirmSudopassword(l, uid, Attrs, SudoPasswd): if len(SudoPasswd) == 0: return None - res = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid="+uid, ['sudoPassword']) + res = lc.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "uid=" + uid, ['sudoPassword']) if len(res) != 1: - raise UDFormatError, "Not exactly one hit when searching for user" - if res[0][1].has_key('sudoPassword'): + raise UDFormatError("Not exactly one hit when searching for user") + if sudoPassword in res[0][1]: inldap = res[0][1]['sudoPassword'] else: inldap = [] newldap = [] for entry in inldap: - Match = re.compile('^('+UUID_FORMAT+') (confirmed:[0-9a-f]{40}|unconfirmed) ([a-z0-9.,*-]+) ([^ ]+)$').match(entry) - if Match == None: - raise UDFormatError, "Could not parse existing sudopasswd entry" + Match = re.compile('^(' + UUID_FORMAT + ') (confirmed:[0-9a-f]{40}|unconfirmed) ([a-z0-9.,*-]+) ([^ ]+)$').match(entry) + if Match is None: + raise UDFormatError("Could not parse existing sudopasswd entry") uuid = Match.group(1) status = Match.group(2) hosts = Match.group(3) cryptedpass = Match.group(4) - if SudoPasswd.has_key(uuid): + if uuid in SudoPasswd: confirmedHosts = SudoPasswd[uuid][0] confirmedHmac = SudoPasswd[uuid][1] if status.startswith('confirmed:'): - if status == 'confirmed:'+make_passwd_hmac('password-is-confirmed', 'sudo', uid, uuid, hosts, cryptedpass): - result = result + "Entry %s for sudo password on hosts %s already confirmed.\n"%(uuid, hosts) + if status == 'confirmed:' + make_passwd_hmac('password-is-confirmed', 'sudo', uid, uuid, hosts, cryptedpass): + result += "Entry %s for sudo password on hosts %s already confirmed.\n" % (uuid, hosts) else: - result = result + "Entry %s for sudo password on hosts %s is listed as confirmed, but HMAC does not verify.\n"%(uuid, hosts) + result += "Entry %s for sudo password on hosts %s is listed as confirmed, but HMAC does not verify.\n" % (uuid, hosts) elif confirmedHosts != hosts: - result = result + "Entry %s hostlist mismatch (%s vs. %s).\n"%(uuid, hosts, confirmedHosts) + result += "Entry %s hostlist mismatch (%s vs. %s).\n" % (uuid, hosts, confirmedHosts) elif make_passwd_hmac('confirm-new-password', 'sudo', uid, uuid, hosts, cryptedpass) == confirmedHmac: - result = result + "Entry %s for sudo password on hosts %s now confirmed.\n"%(uuid, hosts) - status = 'confirmed:'+make_passwd_hmac('password-is-confirmed', 'sudo', uid, uuid, hosts, cryptedpass) + result += "Entry %s for sudo password on hosts %s now confirmed.\n" % (uuid, hosts) + status = 'confirmed:' + make_passwd_hmac('password-is-confirmed', 'sudo', uid, uuid, hosts, cryptedpass) else: - result = result + "Entry %s for sudo password on hosts %s HMAC verify failed.\n"%(uuid, hosts) + result += "Entry %s for sudo password on hosts %s HMAC verify failed.\n" % (uuid, hosts) del SudoPasswd[uuid] newentry = " ".join([uuid, status, hosts, cryptedpass]) if len(newldap) == 0: - newldap.append((ldap.MOD_REPLACE,"sudoPassword",newentry)) + newldap.append((ldap.MOD_REPLACE, "sudoPassword", newentry)) else: - newldap.append((ldap.MOD_ADD,"sudoPassword",newentry)) + newldap.append((ldap.MOD_ADD, "sudoPassword", newentry)) for entry in SudoPasswd: - result = result + "Entry %s that you confirm is not listed in ldap."%(entry) + result += "Entry %s that you confirm is not listed in ldap." % (entry,) for entry in newldap: Attrs.append(entry) return result + def connect_to_ldap_and_check_if_locked(DnRecord): # Connect to the ldap server - l = connectLDAP() - F = open(PassDir+"/pass-"+pwd.getpwuid(os.getuid())[0],"r") + lc = connectLDAP() + F = open(PassDir + "/pass-" + pwd.getpwuid(os.getuid())[0], "r") AccessPass = F.readline().strip().split(" ") F.close() - l.simple_bind_s("uid="+AccessPass[0]+","+BaseDn,AccessPass[1]) + lc.simple_bind_s("uid={},{}".format(AccessPass[0], BaseDn), AccessPass[1]) # Check for a locked account - Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid="+GetAttr(DnRecord,"uid")) - if (GetAttr(Attrs[0],"userPassword").find("*LK*") != -1) \ - or GetAttr(Attrs[0],"userPassword").startswith("!"): - raise UDNotAllowedError, "This account is locked" - + Attrs = lc.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "uid=" + GetAttr(DnRecord, "uid")) + if (GetAttr(Attrs[0], "userPassword").find("*LK*") != -1) \ + or GetAttr(Attrs[0], "userPassword").startswith("!"): + raise UDNotAllowedError("This account is locked") return l + # Handle an [almost] arbitary change -def HandleChange(Reply,DnRecord,Key): +def HandleChange(Reply, DnRecord, Key): global PlainText - Lines = re.split("\n *\r?",PlainText) + Lines = re.split("\n *\r?", PlainText) Result = "" Attrs = [] SudoPasswd = {} Show = 0 CommitChanges = 1 - for Line in Lines: + for Line in Lines: Line = Line.strip() if Line == "": continue # Try to process a command line - Result = Result + "> "+Line+"\n" + Result += "> " + Line + "\n" try: if Line == "show": Show = 1 Res = "OK" else: badkeys = LoadBadSSH() - Res = DoPosition(Line,Attrs) or DoDNS(Line,Attrs,DnRecord) or \ - DoArbChange(Line,Attrs) or DoSSH(Line,Attrs,badkeys,GetAttr(DnRecord,"uid")) or \ - DoDel(Line,Attrs) or DoRBL(Line,Attrs) or DoConfirmSudopassword(Line, SudoPasswd) - except: + Res = DoPosition(Line, Attrs) or DoDNS(Line, Attrs, DnRecord) or \ + DoArbChange(Line, Attrs) or DoSSH(Line, Attrs, badkeys, GetAttr(DnRecord, "uid")) or \ + DoDel(Line, Attrs) or DoRBL(Line, Attrs) or DoConfirmSudopassword(Line, SudoPasswd) + except Exception: Res = None - Result = Result + "==> %s: %s\n" %(sys.exc_type,sys.exc_value) + Result += "==> %s: %s\n" % (sys.exc_type, sys.exc_value) # Fail, if someone tries to send someone elses signed email to the # daemon then we want to abort ASAP. - if Res == None: + if Res is None: CommitChanges = 0 Result = Result + "Command is not understood. Halted - no changes committed\n" break Result += Res + "\n" # Connect to the ldap server - l = connect_to_ldap_and_check_if_locked(DnRecord) + lc = connect_to_ldap_and_check_if_locked(DnRecord) - if CommitChanges == 1 and len(SudoPasswd) > 0: # only if we are still good to go + if CommitChanges == 1 and len(SudoPasswd) > 0: # only if we are still good to go try: - Res = FinishConfirmSudopassword(l, GetAttr(DnRecord,"uid"), Attrs, SudoPasswd) + Res = FinishConfirmSudopassword(l, GetAttr(DnRecord, "uid"), Attrs, SudoPasswd) if Res is not None: - Result = Result + Res + "\n" + Result += Res + "\n" except Error, e: CommitChanges = 0 - Result = Result + "FinishConfirmSudopassword raised an error (%s) - no changes committed\n"%(e) + Result = Result + "FinishConfirmSudopassword raised an error (%s) - no changes committed\n" % (e) if CommitChanges == 1 and len(Attrs) > 0: - Dn = "uid=" + GetAttr(DnRecord,"uid") + "," + BaseDn - l.modify_s(Dn,Attrs) + Dn = "uid=" + GetAttr(DnRecord, "uid") + "," + BaseDn + lc.modify_s(Dn, Attrs) Attribs = "" if Show == 1: - Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid="+GetAttr(DnRecord,"uid")) + Attrs = lc.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "uid=" + GetAttr(DnRecord, "uid")) if len(Attrs) == 0: - raise UDNotAllowedError, "User not found" - Attribs = GPGEncrypt(PrettyShow(Attrs[0])+"\n","0x"+Key[1],Key[4]) + raise UDNotAllowedError("User not found") + Attribs = GPGEncrypt(PrettyShow(Attrs[0]) + "\n", "0x" + Key[1], Key[4]) Subst = {} Subst["__FROM__"] = ChangeFrom @@ -632,19 +624,19 @@ def HandleChange(Reply,DnRecord,Key): Subst["__RESULT__"] = Result Subst["__ATTR__"] = Attribs - return Reply + TemplateSubst(Subst,open(TemplatesDir+"change-reply","r").read()) - + return Reply + TemplateSubst(Subst, open(TemplatesDir + "change-reply", "r").read()) + + # Handle ping handles an email sent to the 'ping' address (ie this program # called with a ping argument) It replies with a dump of the public records. -def HandlePing(Reply,DnRecord,Key): +def HandlePing(Reply, DnRecord, Key): Subst = {} Subst["__FROM__"] = PingFrom Subst["__EMAIL__"] = EmailAddress(DnRecord) Subst["__LDAPFIELDS__"] = PrettyShow(DnRecord) Subst["__ADMIN__"] = ReplyTo - return Reply + TemplateSubst(Subst,open(TemplatesDir+"ping-reply","r").read()) - + return Reply + TemplateSubst(Subst, open(TemplatesDir + "ping-reply", "r").read()) def get_crypttype_preamble(key): @@ -655,20 +647,20 @@ def get_crypttype_preamble(key): "mode, without IDEA. This message cannot be decoded using PGP 2.x" return type + # Handle a change password email sent to the change password address # (this program called with the chpass argument) -def HandleChPass(Reply,DnRecord,Key): +def HandleChPass(Reply, DnRecord, Key): # Generate a random password Password = GenPass() Pass = HashPass(Password) - # Use GPG to encrypt it - Message = GPGEncrypt("Your new password is '" + Password + "'\n",\ - "0x"+Key[1],Key[4]) + # Use GPG to encrypt it + Message = GPGEncrypt("Your new password is '" + Password + "'\n", "0x" + Key[1], Key[4]) Password = None - if Message == None: - raise UDFormatError, "Unable to generate the encrypted reply, gpg failed." + if Message is None: + raise UDFormatError("Unable to generate the encrypted reply, gpg failed.") Subst = {} Subst["__FROM__"] = ChPassFrom @@ -676,45 +668,47 @@ def HandleChPass(Reply,DnRecord,Key): Subst["__CRYPTTYPE__"] = get_crypttype_preamble(Key) Subst["__PASSWORD__"] = Message Subst["__ADMIN__"] = ReplyTo - Reply = Reply + TemplateSubst(Subst,open(TemplatesDir+"passwd-changed","r").read()) + Reply = Reply + TemplateSubst(Subst, open(TemplatesDir + "passwd-changed", "r").read()) - l = connect_to_ldap_and_check_if_locked(DnRecord) + lc = connect_to_ldap_and_check_if_locked(DnRecord) # Modify the password - Rec = [(ldap.MOD_REPLACE,"userPassword","{crypt}"+Pass), - (ldap.MOD_REPLACE,"shadowLastChange",str(int(time.time()/24/60/60)))] - Dn = "uid=" + GetAttr(DnRecord,"uid") + "," + BaseDn - l.modify_s(Dn,Rec) + Rec = [(ldap.MOD_REPLACE, "userPassword", "{crypt}" + Pass), + (ldap.MOD_REPLACE, "shadowLastChange", str(int(time.time() / (24 * 60 * 60))))] + Dn = "uid=" + GetAttr(DnRecord, "uid") + "," + BaseDn + lc.modify_s(Dn, Rec) return Reply + def HandleChTOTPSeed(Reply, DnRecord, Key): # Generate a random seed seed = binascii.hexlify(open("/dev/urandom", "r").read(32)) - msg = GPGEncrypt("Your new TOTP seed is '%s'\n" % (seed,), "0x"+Key[1],Key[4]) + msg = GPGEncrypt("Your new TOTP seed is '%s'\n" % (seed,), "0x" + Key[1], Key[4]) if msg is None: - raise UDFormatError, "Unable to generate the encrypted reply, gpg failed." + raise UDFormatError("Unable to generate the encrypted reply, gpg failed.") Subst = {} Subst["__FROM__"] = ChPassFrom Subst["__EMAIL__"] = EmailAddress(DnRecord) Subst["__PASSWORD__"] = msg Subst["__ADMIN__"] = ReplyTo - Reply = Reply + TemplateSubst(Subst, open(TemplatesDir+"totp-seed-changed", "r").read()) + Reply += TemplateSubst(Subst, open(TemplatesDir + "totp-seed-changed", "r").read()) - l = connect_to_ldap_and_check_if_locked(DnRecord) + lc = connect_to_ldap_and_check_if_locked(DnRecord) # Modify the password Rec = [(ldap.MOD_REPLACE, "totpSeed", seed)] - Dn = "uid=" + GetAttr(DnRecord,"uid") + "," + BaseDn - l.modify_s(Dn,Rec) + Dn = "uid=" + GetAttr(DnRecord, "uid") + "," + BaseDn + lc.modify_s(Dn, Rec) return Reply -def HandleChKrbPass(Reply,DnRecord,Key): + +def HandleChKrbPass(Reply, DnRecord, Key): # Connect to the ldap server, will throw an exception if account locked. - l = connect_to_ldap_and_check_if_locked(DnRecord) + lc = connect_to_ldap_and_check_if_locked(DnRecord) - user = GetAttr(DnRecord,"uid") - krb_proc = subprocess.Popen( ('ud-krb-reset', user), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + user = GetAttr(DnRecord, "uid") + krb_proc = subprocess.Popen(('ud-krb-reset', user), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) krb_proc.stdin.close() out = krb_proc.stdout.readlines() krb_proc.wait() @@ -726,16 +720,16 @@ def HandleChKrbPass(Reply,DnRecord,Key): m += "The exitcode of the reset script was zero, indicating that everything\n" m += "worked. However, this being software who knows. Script's output below." else: - m += "The exitcode of the reset script was %d, indicating that something\n"%(exitcode) + m += "The exitcode of the reset script was %d, indicating that something\n" % (exitcode,) m += "went terribly, terribly wrong. Please consult the script's output below\n" m += "for more information. Contact the admins if you have any questions or\n" m += "require assitance." - m += "\n"+''.join( map(lambda x: "| "+x, out) ) + m += "\n" + ''.join(map(lambda x: "| " + x, out)) - Message = GPGEncrypt(m, "0x"+Key[1],Key[4]) - if Message == None: - raise UDFormatError, "Unable to generate the encrypted reply, gpg failed." + Message = GPGEncrypt(m, "0x" + Key[1], Key[4]) + if Message is None: + raise UDFormatError("Unable to generate the encrypted reply, gpg failed.") Subst = {} Subst["__FROM__"] = ChPassFrom @@ -743,14 +737,15 @@ def HandleChKrbPass(Reply,DnRecord,Key): Subst["__CRYPTTYPE__"] = get_crypttype_preamble(Key) Subst["__PASSWORD__"] = Message Subst["__ADMIN__"] = ReplyTo - Reply = Reply + TemplateSubst(Subst,open(TemplatesDir+"passwd-changed","r").read()) + Reply += TemplateSubst(Subst, open(TemplatesDir + "passwd-changed", "r").read()) return Reply # Start of main program + # Drop messages from a mailer daemon. -if os.environ.has_key('SENDER') == 0 or len(os.environ['SENDER']) == 0: +if not os.environ.get('SENDER'): sys.exit(0) ErrMsg = "Indeterminate Error" @@ -760,7 +755,7 @@ try: ErrType = EX_TEMPFAIL ErrMsg = "Failed to initialize the replay cache:" - # Get the email + # Get the email ErrType = EX_PERMFAIL ErrMsg = "Failed to understand the email or find a signature:" mail = email.parser.Parser().parse(sys.stdin) @@ -769,17 +764,17 @@ try: ErrMsg = "Message is not PGP signed:" if Msg[0].find("-----BEGIN PGP SIGNED MESSAGE-----") == -1 and \ Msg[0].find("-----BEGIN PGP MESSAGE-----") == -1: - raise UDFormatError, "No PGP signature" - + raise UDFormatError("No PGP signature") + # Check the signature ErrMsg = "Unable to check the signature or the signature was invalid:" pgp = GPGCheckSig2(Msg[0]) if not pgp.ok: - raise UDFormatError, pgp.why - + raise UDFormatError(pgp.why) + if pgp.text is None: - raise UDFormatError, "Null signature text" + raise UDFormatError("Null signature text") # Extract the plain message text in the event of mime encoding global PlainText @@ -793,19 +788,18 @@ try: # Connect to the ldap server ErrType = EX_TEMPFAIL ErrMsg = "An error occured while performing the LDAP lookup" - global l - l = connectLDAP() - l.simple_bind_s("","") + global lc + lc = connectLDAP() + lc.simple_bind_s("", "") # Search for the matching key fingerprint - Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=" + pgp.key_fpr) + Attrs = lc.search_s(BaseDn, ldap.SCOPE_ONELEVEL, "keyFingerPrint={}".format(pgp.key_fpr)) ErrType = EX_PERMFAIL if len(Attrs) == 0: - raise UDFormatError, "Key not found" + raise UDFormatError("Key not found") if len(Attrs) != 1: - raise UDFormatError, "Oddly your key fingerprint is assigned to more than one account.." - + raise UDFormatError("Oddly your key fingerprint is assigned to more than one account..") # Check the signature against the replay cache RC = ReplayCache(ReplayCacheFile) @@ -813,81 +807,81 @@ try: # Determine the sender address ErrMsg = "A problem occured while trying to formulate the reply" - Sender = mail['Reply-To'] - if not Sender: Sender = mail['From'] - if not Sender: raise UDFormatError, "Unable to determine the sender's address" + Sender = mail.get('Reply-To', mail.get('From')) + if not Sender: + raise UDFormatError("Unable to determine the sender's address") # Formulate a reply - Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time())) - Reply = "To: %s\nReply-To: %s\nDate: %s\n" % (Sender,ReplyTo,Date) + Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(time.time())) + Reply = "To: %s\nReply-To: %s\nDate: %s\n" % (Sender, ReplyTo, Date) - Res = l.search_s(HostBaseDn, ldap.SCOPE_SUBTREE, '(objectClass=debianServer)', ['hostname'] ) + Res = lc.search_s(HostBaseDn, ldap.SCOPE_SUBTREE, '(objectClass=debianServer)', ['hostname']) # Res is a list of tuples. # The tuples contain a dn (str) and a dictionary. # The dictionaries map the key "hostname" to a list. # These lists contain a single hostname (str). - ValidHostNames = reduce(lambda a,b: a+b, [value.get("hostname", []) for (dn, value) in Res], []) + ValidHostNames = reduce(lambda a, b: a + b, [value.get("hostname", []) for (dn, value) in Res], []) # Dispatch if sys.argv[1] == "ping": - Reply = HandlePing(Reply,Attrs[0],pgp.key_info) + Reply = HandlePing(Reply, Attrs[0], pgp.key_info) elif sys.argv[1] == "chpass": if PlainText.strip().find("Please change my Debian password") >= 0: - Reply = HandleChPass(Reply,Attrs[0],pgp.key_info) + Reply = HandleChPass(Reply, Attrs[0], pgp.key_info) elif PlainText.strip().find("Please change my Kerberos password") >= 0: - Reply = HandleChKrbPass(Reply,Attrs[0],pgp.key_info) + Reply = HandleChKrbPass(Reply, Attrs[0], pgp.key_info) elif PlainText.strip().find("Please change my TOTP seed") >= 0: Reply = HandleChTOTPSeed(Reply, Attrs[0], pgp.key_info) else: - raise UDFormatError,"Please send a signed message where the first line of text is the string 'Please change my Debian password' or some other string we accept here." + raise UDFormatError("Please send a signed message where the first line of text is the string 'Please change my Debian password' or some other string we accept here.") elif sys.argv[1] == "change": - Reply = HandleChange(Reply,Attrs[0],pgp.key_info) + Reply = HandleChange(Reply, Attrs[0], pgp.key_info) else: print sys.argv - raise UDFormatError, "Incorrect Invokation" + raise UDFormatError("Incorrect Invokation") - # Send the message through sendmail + # Send the message through sendmail ErrMsg = "A problem occured while trying to send the reply" - Child = os.popen("/usr/sbin/sendmail -t","w") + Child = os.popen("/usr/sbin/sendmail -t", "w") # Child = os.popen("cat","w") Child.write(Reply) - if Child.close() != None: - raise UDExecuteError, "Sendmail gave a non-zero return code" - -except: - # Error Reply Header - Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time())) - ErrReplyHead = "To: %s\nReply-To: %s\nDate: %s\n" % (os.environ['SENDER'],ReplyTo,Date) - - # Error Body - Subst = {} - Subst["__ERROR__"] = ErrMsg - Subst["__ADMIN__"] = ReplyTo - - Trace = "==> %s: %s\n" %(sys.exc_type,sys.exc_value) - List = traceback.extract_tb(sys.exc_traceback) - if len(List) > 1: - Trace = Trace + "Python Stack Trace:\n" - for x in List: - Trace = Trace + " %s %s:%u: %s\n" %(x[2],x[0],x[1],x[3]) - - Subst["__TRACE__"] = Trace - - # Try to send the bounce - try: - ErrReply = TemplateSubst(Subst,open(TemplatesDir+"error-reply","r").read()) - - Child = os.popen("/usr/sbin/sendmail -t -oi -f ''","w") - Child.write(ErrReplyHead) - Child.write(ErrReply) - if Child.close() != None: - raise UDExecuteError, "Sendmail gave a non-zero return code" - except: - sys.exit(EX_TEMPFAIL) - - if ErrType != EX_PERMFAIL: - sys.exit(ErrType) - sys.exit(0) + if Child.close() is not None: + raise UDExecuteError("Sendmail gave a non-zero return code") + +except Exception: + # Error Reply Header + Date = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(time.time())) + ErrReplyHead = "To: %s\nReply-To: %s\nDate: %s\n" % (os.environ['SENDER'], ReplyTo, Date) + + # Error Body + Subst = {} + Subst["__ERROR__"] = ErrMsg + Subst["__ADMIN__"] = ReplyTo + + Trace = "==> %s: %s\n" % (sys.exc_type, sys.exc_value) + List = traceback.extract_tb(sys.exc_traceback) + if len(List) > 1: + Trace = Trace + "Python Stack Trace:\n" + for x in List: + Trace = Trace + " %s %s:%u: %s\n" % (x[2], x[0], x[1], x[3]) + + Subst["__TRACE__"] = Trace + + # Try to send the bounce + try: + ErrReply = TemplateSubst(Subst, open(TemplatesDir + "error-reply", "r").read()) + + Child = os.popen("/usr/sbin/sendmail -t -oi -f ''", "w") + Child.write(ErrReplyHead) + Child.write(ErrReply) + if Child.close() is not None: + raise UDExecuteError("Sendmail gave a non-zero return code") + except Exception: + sys.exit(EX_TEMPFAIL) + + if ErrType != EX_PERMFAIL: + sys.exit(ErrType) + sys.exit(0) # vim:set et: # vim:set ts=3: