PEP8: fix E123: closing bracket does not match indentation of opening bracket's line
[samba.git] / python / samba / netcmd / gpo.py
index 9706ca8a83331290b415e03da9a47bf4337f052b..a63fc8f97223ee0186daecbc67faa76062d7e36e 100644 (file)
@@ -32,7 +32,7 @@ from samba.netcmd import (
     CommandError,
     Option,
     SuperCommand,
-    )
+)
 from samba.samdb import SamDB
 from samba import dsdb
 from samba.dcerpc import security
@@ -48,7 +48,7 @@ import uuid
 from samba.ntacls import dsacl2fsacl
 from samba.dcerpc import nbt
 from samba.net import Net
-from samba.gp_parse import GPParser, GPNoParserException
+from samba.gp_parse import GPParser, GPNoParserException, GPGeneralizeException
 from samba.gp_parse.gp_pol import GPPolParser
 from samba.gp_parse.gp_ini import (
     GPIniParser,
@@ -381,7 +381,7 @@ class cmd_listall(Command):
     takes_options = [
         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
                metavar="URL", dest="H")
-        ]
+    ]
 
     def run(self, H=None, sambaopts=None, credopts=None, versionopts=None):
 
@@ -419,7 +419,7 @@ class cmd_list(Command):
     takes_options = [
         Option("-H", "--URL", help="LDB URL for database or target server",
             type=str, metavar="URL", dest="H")
-        ]
+    ]
 
     def run(self, username, H=None, sambaopts=None, credopts=None, versionopts=None):
 
@@ -534,7 +534,7 @@ class cmd_show(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str)
-        ]
+    ]
 
     def run(self, gpo, H=None, sambaopts=None, credopts=None, versionopts=None):
 
@@ -582,7 +582,7 @@ class cmd_getlink(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str)
-        ]
+    ]
 
     def run(self, container_dn, H=None, sambaopts=None, credopts=None,
                 versionopts=None):
@@ -633,7 +633,7 @@ class cmd_setlink(Command):
             help="Disable policy"),
         Option("--enforce", dest="enforced", default=False, action='store_true',
             help="Enforce policy")
-        ]
+    ]
 
     def run(self, container_dn, gpo, H=None, disabled=False, enforced=False,
                 sambaopts=None, credopts=None, versionopts=None):
@@ -719,7 +719,7 @@ class cmd_dellink(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str),
-        ]
+    ]
 
     def run(self, container, gpo, H=None, sambaopts=None, credopts=None,
                 versionopts=None):
@@ -758,7 +758,7 @@ class cmd_listcontainers(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str)
-        ]
+    ]
 
     def run(self, gpo, H=None, sambaopts=None, credopts=None,
                 versionopts=None):
@@ -794,7 +794,7 @@ class cmd_getinheritance(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str)
-        ]
+    ]
 
     def run(self, container_dn, H=None, sambaopts=None, credopts=None,
                 versionopts=None):
@@ -838,7 +838,7 @@ class cmd_setinheritance(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str)
-        ]
+    ]
 
     def run(self, container_dn, inherit_state, H=None, sambaopts=None, credopts=None,
                 versionopts=None):
@@ -893,7 +893,7 @@ class cmd_fetch(Command):
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str),
         Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
-        ]
+    ]
 
     def run(self, gpo, H=None, tmpdir=None, sambaopts=None, credopts=None, versionopts=None):
 
@@ -923,7 +923,11 @@ class cmd_fetch(Command):
 
         # SMB connect to DC
         try:
-            conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
+            conn = smb.SMB(dc_hostname,
+                           service,
+                           lp=self.lp,
+                           creds=self.creds,
+                           sign=True)
         except Exception:
             raise CommandError("Error connecting to '%s' using SMB" % dc_hostname)
 
@@ -965,10 +969,15 @@ class cmd_backup(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str),
-        Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
+        Option("--tmpdir", help="Temporary directory for copying policy files", type=str),
+        Option("--generalize", help="Generalize XML entities to restore",
+               default=False, action='store_true'),
+        Option("--entities", help="File to export defining XML entities for the restore",
+               dest='ent_file', type=str)
         ]
 
-    def run(self, gpo, H=None, tmpdir=None, sambaopts=None, credopts=None, versionopts=None):
+    def run(self, gpo, H=None, tmpdir=None, generalize=False, sambaopts=None,
+            credopts=None, versionopts=None, ent_file=None):
 
         self.lp = sambaopts.get_loadparm()
         self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
@@ -1020,8 +1029,79 @@ class cmd_backup(Command):
         except Exception as e:
             # FIXME: Catch more specific exception
             raise CommandError("Error copying GPO from DC", e)
+
         self.outf.write('GPO copied to %s\n' % gpodir)
 
+        if generalize:
+            self.outf.write('\nAttempting to generalize XML entities:\n')
+            entities = cmd_backup.generalize_xml_entities(self.outf, gpodir,
+                                                          gpodir)
+            import operator
+            ents = ''
+            for ent in sorted(entities.items(), key=operator.itemgetter(1)):
+                ents += '<!ENTITY {} "{}">\n'.format(ent[1].strip('&;'), ent[0])
+
+            if ent_file:
+                with open(ent_file, 'w') as f:
+                    f.write(ents)
+                self.outf.write('Entities successfully written to %s\n' %
+                                ent_file)
+            else:
+                self.outf.write('\nEntities:\n')
+                self.outf.write(ents)
+
+    @staticmethod
+    def generalize_xml_entities(outf, sourcedir, targetdir):
+        entities = {}
+
+        if not os.path.exists(targetdir):
+            os.mkdir(targetdir)
+
+        l_dirs = [ sourcedir ]
+        r_dirs = [ targetdir ]
+        while l_dirs:
+            l_dir = l_dirs.pop()
+            r_dir = r_dirs.pop()
+
+            dirlist = os.listdir(l_dir)
+            dirlist.sort()
+            for e in dirlist:
+                l_name = os.path.join(l_dir, e)
+                r_name = os.path.join(r_dir, e)
+
+                if os.path.isdir(l_name):
+                    l_dirs.append(l_name)
+                    r_dirs.append(r_name)
+                    if not os.path.exists(r_name):
+                        os.mkdir(r_name)
+                else:
+                    if l_name.endswith('.xml'):
+                        # Restore the xml file if possible
+
+                        # Get the filename to find the parser
+                        to_parse = os.path.basename(l_name)[:-4]
+
+                        parser = find_parser(to_parse)
+                        try:
+                            with open(l_name, 'r') as ltemp:
+                                data = ltemp.read()
+
+                            concrete_xml = ET.fromstring(data)
+                            found_entities = parser.generalize_xml(concrete_xml, r_name, entities)
+                        except GPGeneralizeException:
+                            outf.write('SKIPPING: Generalizing failed for %s\n' % to_parse)
+
+                    else:
+                        # No need to generalize non-xml files.
+                        #
+                        # TODO This could be improved with xml files stored in
+                        # the renamed backup file (with custom extension) by
+                        # inlining them into the exported backups.
+                        if not os.path.samefile(l_name, r_name):
+                            shutil.copy2(l_name, r_name)
+
+        return entities
+
 
 class cmd_create(Command):
     """Create an empty GPO."""
@@ -1039,7 +1119,7 @@ class cmd_create(Command):
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str),
         Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
-        ]
+    ]
 
     def run(self, displayname, H=None, tmpdir=None, sambaopts=None, credopts=None,
             versionopts=None):
@@ -1213,6 +1293,7 @@ class cmd_restore(cmd_create):
             r_dir = r_dirs.pop()
 
             dirlist = os.listdir(l_dir)
+            dirlist.sort()
             for e in dirlist:
                 l_name = os.path.join(l_dir, e)
                 r_name = os.path.join(r_dir, e)
@@ -1233,8 +1314,17 @@ class cmd_restore(cmd_create):
                         try:
                             with open(l_name, 'r') as ltemp:
                                 data = ltemp.read()
-                                # Load the XML file with the DTD (entity) header
-                                parser.load_xml(ET.fromstring(dtd_header + data))
+                                xml_head = '<?xml version="1.0" encoding="utf-8"?>'
+
+                                if data.startswith(xml_head):
+                                    # It appears that sometimes the DTD rejects
+                                    # the xml header being after it.
+                                    data = data[len(xml_head):]
+
+                                    # Load the XML file with the DTD (entity) header
+                                    parser.load_xml(ET.fromstring(xml_head + dtd_header + data))
+                                else:
+                                    parser.load_xml(ET.fromstring(dtd_header + data))
 
                                 # Write out the substituted files in the output
                                 # location, ready to copy over.
@@ -1328,7 +1418,7 @@ class cmd_del(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str),
-        ]
+    ]
 
     def run(self, gpo, H=None, sambaopts=None, credopts=None,
                 versionopts=None):
@@ -1403,7 +1493,7 @@ class cmd_aclcheck(Command):
     takes_options = [
         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
                metavar="URL", dest="H")
-        ]
+    ]
 
     def run(self, H=None, sambaopts=None, credopts=None, versionopts=None):