Index: dassldapsync/dassldapsync.py
===================================================================
--- dassldapsync/dassldapsync.py	(revision 1257)
+++ dassldapsync/dassldapsync.py	(revision 1259)
@@ -4,5 +4,5 @@
 # core modules
 import argparse
-import ConfigParser
+from   configparser import ConfigParser
 import logging
 from   pprint import pprint
@@ -20,5 +20,5 @@
 import ldap.modlist
 from   ldap.syncrepl import SyncreplConsumer
-import ldapurl
+from   ldapurl import LDAPUrl
 import ldif
 
@@ -46,51 +46,4 @@
         self.pwd_max_days = 0
 
-class ConfigParserDefaults(ConfigParser.ConfigParser, object):
-    def get(self, section, option, default=None):
-        try:
-            result = super(self.__class__, self).get(section, option)
-        except ConfigParser.NoOptionError:
-            if default is None:
-                raise
-            else:
-                result = default
-        return result
-
-    def get_section(self, section):
-        if section in self._sections:
-            return self._sections[section]
-
-    def get0(self, section, option, default=None):
-        try:
-            result = super(self.__class__, self).get(section, option)
-        except ConfigParser.NoOptionError:
-            result = default
-        return result
-
-    def getboolean(self, section, option, default=None):
-        try:
-            result = super(self.__class__, self).getboolean(section, option)
-        except ConfigParser.NoOptionError:
-            if default is None:
-                raise
-            else:
-                result = default
-        return result
-
-    def get_ldap_url_obj(self, section):
-        baseurl = 'ldap://{server}:389/{basedn}'.format(**(self.get_section(section)))
-        attrs = None
-        if self.get0(section, 'attributes') is not None:
-            attrs = self.get(section, 'attributes').split(',')
-        return ldapurl.LDAPUrl(
-            baseurl,
-            dn=self.get(section, 'baseDn', ''),
-            who=self.get0(section, 'bindDn'),
-            cred=self.get0(section, 'basePassword'),
-            filterstr=self.get0(section, 'filter'),
-            attrs=attrs
-        )
-
-
 def readLDIFSource(path):
     logger = logging.getLogger()
@@ -105,5 +58,6 @@
     logger = logging.getLogger()
     logger.info("reading LDAP objects from server {}".format(server))
-    con = ldap.open(server, port=389)
+    ldapurl = LDAPUrl(hostport="{}:389".format(self.server))
+    con = ldap.initialize(ldapurl. initializeUrl())
     if starttls:
         con.start_tls_s()
@@ -131,7 +85,13 @@
         self.classmap = {}
 
-        self.junk_attrs = ["authzto", "memberof", "modifiersname", "modifytimestamp", "entryuuid",
-                           "entrycsn", "contextcsn", "creatorsname", "createtimestamp",
-                           "structuralobjectclass", "pwdaccountlockedtime", "pwdchangedtime", "pwdfailuretime" ]
+        #self.junk_objectclasses = [ b"sambaidmapentry" ]
+        #"sambasid", 
+        self.junk_objectclasses = []
+        self.junk_attrs = ["authzto",
+                           "creatorsname", "createtimestamp", "contextcsn", 
+                           "entrycsn", "entryuuid",
+                           "memberof", "modifiersname", "modifytimestamp", 
+                           "pwdaccountlockedtime", "pwdchangedtime", "pwdfailuretime",
+                           "structuralobjectclass"]
 
         self.reset_result()
@@ -149,5 +109,6 @@
         if self.con is None:
             self.logger.info("connect to destination LDAP server {}".format(self.destserver))
-            self.con = ldap.open(self.destserver, port=389)
+            ldapurl = LDAPUrl(hostport="{}:389".format(self.destserver))
+            self.con = ldap.initialize(ldapurl. initializeUrl())
             if self.options.starttls:
                 self.con.start_tls_s()
@@ -267,4 +228,7 @@
         max_age = datetime.timedelta(days=self.options.pwd_max_days)
 
+        objectClasses = srcAttributes['objectClass']
+        srcAttributes['objectClass'] = [oc for oc in objectClasses if oc.lower() not in self.junk_objectclasses]
+
         try:
             destDn, destAttributes = self._get_dest_entry(srcDn, srcAttributes)
@@ -294,13 +258,17 @@
                     self.logger.exception('modify failed')
                     self.notify_modified(srcDn, False)
+            else:
+                self.notify_unchanged(srcDn)
 
         except ldap.NO_SUCH_OBJECT:
             if not self.options.updateonly:
                 try:
-                    self.con.add_s(srcDn, ldap.modlist.addModlist(srcAttributes, self.junk_attrs))
+                    entry = ldap.modlist.addModlist(srcAttributes, self.junk_attrs)
+                    self.con.add_s(srcDn, entry)
                     self.notify_created(srcDn)
                 except (ldap.OBJECT_CLASS_VIOLATION,
                         ldap.NO_SUCH_OBJECT,
-                        ldap.CONSTRAINT_VIOLATION):
+                        ldap.CONSTRAINT_VIOLATION) as e:
+                    #print(e)
                     self.notify_created(srcDn, False)
 
@@ -366,43 +334,46 @@
             ok = len(result[action]['ok'])
             failed = len(result[action]['failed'])
-            print "{} (ok: {}, failed: {}):".format(action, ok, failed)
+            print("{} (ok: {}, failed: {}):".format(action, ok, failed))
 
             if show_ok and ok > 0:
-                print "succeeded:"
-                print "\n".join(result[action]['ok'])
+                print("succeeded:")
+                print("\n".join(result[action]['ok']))
 
             if show_failed and failed > 0:
-                print "failed:"
-                print "\n".join(result[action]['failed'])
+                print("failed:")
+                print("\n".join(result[action]['failed']))
 
     def get_short_dn(self, dn):
         return dn.lower().replace(',' + self.srcbasedn.lower(), '')
 
+    def notify_unchanged(self, dn):
+        logger.debug(u'{} unchanged'.format(self.get_short_dn(dn)))
+
     def notify_created(self, dn, ok=True):
         if ok:
-            logger.debug('{} created'.format(self.get_short_dn(dn)))
+            logger.debug(u'{} created'.format(self.get_short_dn(dn)))
             self.result['add']['ok'].append(dn)
         else:
-            self.logger.warning("failed to add {}".format(dn))
+            self.logger.warning(u"failed to add {}".format(dn))
             self.result['add']['failed'].append(dn)
 
     def notify_modified(self, dn, ok=True):
         if ok:
-            logger.debug('{} modified'.format(self.get_short_dn(dn)))
+            logger.debug(u'{} modified'.format(self.get_short_dn(dn)))
             self.result['update']['ok'].append(dn)
         else:
-            self.logger.error("failed to modify {}".format(dn))
+            self.logger.error(u"failed to modify {}".format(dn))
             self.result['update']['failed'].append(dn)
 
     def notify_deleted(self, dn, ok=True):
         if ok:
-            logger.debug('{} deleted'.format(self.get_short_dn(dn)))
+            logger.debug(u'{} deleted'.format(self.get_short_dn(dn)))
             self.result['delete']['ok'].append(dn)
         else:
-            self.logger.error("failed to delete {}".format(dn))
+            self.logger.error(u"failed to delete {}".format(dn))
             self.result['delete']['failed'].append(dn)
 
     def notify_renamed(self, dn, newdn, uid, newuid, options):
-        print "renamed", dn, newdn
+        print(u"renamed {} -> {}".format(dn, newdn))
         subprocess.check_call(
             "%s %s %s %s %s" % (options.renamecommand, dn, newdn, uid, newuid),
@@ -519,9 +490,9 @@
                     self.source_ldap_connection.simple_bind_s(
                         self.source_ldap_url_obj.who, self.source_ldap_url_obj.cred)
-                except ldap.INVALID_CREDENTIALS, e:
-                    print 'Login to LDAP server failed: ', str(e)
+                except ldap.INVALID_CREDENTIALS as e:
+                    print('Login to LDAP server failed: ', str(e))
                     sys.exit(1)
                 except ldap.SERVER_DOWN:
-                    print 'LDAP server is down, going to retry.'
+                    print('LDAP server is down, going to retry.')
                     time.sleep(5)
                     continue
@@ -539,10 +510,10 @@
             try:
                 while self.source_ldap_connection.syncrepl_poll(all=1, msgid=ldap_search):
-                    print ".",
+                    print(".", end="")
             except KeyboardInterrupt:
                 # User asked to exit
-                print "aborted\n"
+                print("aborted\n")
                 self.shutdown(None, None)
-            except Exception, e:
+            except Exception as e:
                 # Handle any exception
                 if self.watcher_running:
@@ -566,4 +537,18 @@
         self.watcher_running = False
 
+def get_ldap_url_obj(self, configsection):
+    baseurl = 'ldap://{server}:389/{basedn}'.format(server=configsection.get('server'), basedn=configsection.get('basedn'))
+    attrs = None
+    if configsection.get('attributes') is not None:
+        attrs = configsection.get('attributes').split(',')
+    return LDAPUrl(
+        baseurl,
+        dn=configsection.get('baseDn'),
+        who=configsection.get('bindDn'),
+        cred=configsection.get('basePassword'),
+        filterstr=configsection.get('filter'),
+        attrs=attrs
+    )
+
 
 if __name__ == "__main__":
@@ -578,5 +563,5 @@
     exclude = None
 
-    config = ConfigParserDefaults()
+    config = ConfigParser()
     config.read(conffile)
 
@@ -588,5 +573,5 @@
 
     basedn = config.get("source", "baseDn")
-    filterstr = config.get0("source", "filter", None)
+    filterstr = config.get("source", "filter", fallback=None)
 
     if srcfile is None:
@@ -600,5 +585,4 @@
     destadminpw = config.get("destination", "bindPassword")
     destbasedn = config.get("destination", "baseDn")
-    destdelete = config.getboolean("destination", "delete")
     try:
         rdn = config.get("destination", "rdn")
@@ -613,9 +597,10 @@
         pass
 
-    options.updateonly = not config.getboolean("destination", "create", False)
-    options.starttls = config.getboolean("destination", "starttls", False)
-    options.renameattr = config.get0("destination", "detectRename", None)
-    options.renamecommand = config.get0("destination", "detectRename", None)
-    options.pwd_max_days = int(config.get("source", "pwd_max_days", 0))
+    options.updateonly = not config.getboolean("destination", "create", fallback=False)
+    options.delete = config.getboolean("destination", "delete", fallback=False)    
+    options.starttls = config.getboolean("destination", "starttls", fallback=False)
+    options.renameattr = config.get("destination", "detectRename", fallback=None)
+    options.renamecommand = config.get("destination", "detectRename", fallback=None)
+    options.pwd_max_days = int(config.get("source", "pwd_max_days", fallback=0))
     options.filter = filterstr
 
@@ -628,9 +613,9 @@
         options.attrlist = None
 
-    if config.get0('source', 'mode') == 'syncrepl':
+    if config.get('source', 'mode', fallback=None) == 'syncrepl':
         ldapsync = LdapSyncRepl(
             destsrv, destadmindn, destadminpw, basedn, destbasedn,
             options,
-            source_ldap_url_obj=config.get_ldap_url_obj('source'))
+            source_ldap_url_obj=get_ldap_url_obj(config['source']))
         ldapsync.sync()
     else:
