diff --git a/hshetl/loaders.py b/hshetl/loaders.py
index bbf7ad7fa81c9ab92bbd683fdddd36c70bf02476..0668aeb8720da0c962ee722852f5db2a0f63c1d4 100644
--- a/hshetl/loaders.py
+++ b/hshetl/loaders.py
@@ -562,3 +562,96 @@ class LdapLoader(AbstractLoader):
         for attribute in changed_attributes:
             output += attribute + ': ' + old[attribute] + ' -> ' + new[attribute] + ', '
         return output[:-2]
+
+    def modifyModlist(self, old_entry,new_entry,ignore_attr_types=None,ignore_oldexistent=0,case_ignore_attr_types=None):
+        """
+        Build differential modify list for calling LDAPObject.modify()/modify_s()
+
+        old_entry
+        Dictionary holding the old entry
+        new_entry
+        Dictionary holding what the new entry should be
+        ignore_attr_types
+        List of attribute type names to be ignored completely
+        ignore_oldexistent
+        If non-zero attribute type names which are in old_entry
+        but are not found in new_entry at all are not deleted.
+        This is handy for situations where your application
+        sets attribute value to '' for deleting an attribute.
+        In most cases leave zero.
+        case_ignore_attr_types
+        List of attribute type names for which comparison will be made
+        case-insensitive
+        """
+        import string, ldap
+        ignore_attr_types = list_dict(map(string.lower,(ignore_attr_types or [])))
+        case_ignore_attr_types = list_dict(map(string.lower,(case_ignore_attr_types or [])))
+        modlist = []
+        attrtype_lower_map = {}
+        for a in old_entry.keys():
+            attrtype_lower_map[string.lower(a)]=a
+        for attrtype in new_entry.keys():
+            attrtype_lower = string.lower(attrtype)
+            if ignore_attr_types.has_key(attrtype_lower):
+                # This attribute type is ignored
+                continue
+            # Filter away null-strings
+            new_value = filter(lambda x:x!=None,new_entry[attrtype])
+            if attrtype_lower_map.has_key(attrtype_lower):
+                old_value = old_entry.get(attrtype_lower_map[attrtype_lower],[])
+                old_value = filter(lambda x:x!=None,old_value)
+                del attrtype_lower_map[attrtype_lower]
+            else:
+                old_value = []
+            if not old_value and new_value:
+                # Add a new attribute to entry
+                modlist.append((ldap.MOD_ADD,attrtype,new_value))
+            elif old_value and new_value:
+                # Replace existing attribute
+                replace_attr_value = old_value!=new_value
+                if not replace_attr_value:
+                    case_insensitive = case_ignore_attr_types.has_key(attrtype_lower)
+                    old_value_dict=list_dict(old_value,case_insensitive)
+                    new_value_dict=list_dict(new_value,case_insensitive)
+                    delete_values = []
+                    for v in old_value:
+                        if not new_value_dict.has_key(v):
+                            replace_attr_value = 1
+                            break
+                    add_values = []
+                    if not replace_attr_value:
+                        for v in new_value:
+                            if not old_value_dict.has_key(v):
+                                replace_attr_value = 1
+                                break
+                if replace_attr_value:
+                    modlist.append((ldap.MOD_DELETE,attrtype,None))
+                    modlist.append((ldap.MOD_ADD,attrtype,new_value))
+            elif old_value and not new_value:
+                # Completely delete an existing attribute
+                modlist.append((ldap.MOD_DELETE,attrtype,None))
+        if not ignore_oldexistent:
+            # Remove all attributes of old_entry which are not present
+            # in new_entry at all
+            for a in attrtype_lower_map.keys():
+                if ignore_attr_types.has_key(a):
+                    # This attribute type is ignored
+                    continue
+                attrtype = attrtype_lower_map[a]
+                modlist.append((ldap.MOD_DELETE,attrtype,None))
+        return modlist # modifyModlist()
+
+def list_dict(l,case_insensitive=0):
+  """
+  return a dictionary with all items of l being the keys of the dictionary
+
+  If argument case_insensitive is non-zero ldap.cidict.cidict will be
+  used for case-insensitive string keys
+  """
+  if case_insensitive:
+    d = ldap.cidict.cidict()
+  else:
+    d = {}
+  for i in l:
+    d[i]=None
+  return d