ctdb-scripts: Improve update and listing code
[samba4-gss.git] / python / samba / gp_parse / __init__.py
blobd45b9c572a8888882ae03b51c628296a0fdbf8f8
1 # GPO Parser for generic extensions
3 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
4 # Written by Garming Sam <garming@catalyst.net.nz>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 from xml.dom import minidom
21 from io import BytesIO
22 from xml.etree.ElementTree import ElementTree, fromstring, tostring
23 from hashlib import md5
24 from samba.common import get_bytes
27 ENTITY_USER_ID = 0
28 ENTITY_SDDL_ACL = 1
29 ENTITY_NETWORK_PATH = 2
32 class GPNoParserException(Exception):
33 pass
35 class GPGeneralizeException(Exception):
36 pass
39 def entity_type_to_string(ent_type):
40 type_str = None
42 if ent_type == ENTITY_USER_ID:
43 type_str = "USER_ID"
44 elif ent_type == ENTITY_SDDL_ACL:
45 type_str = "SDDL_ACL"
46 elif ent_type == ENTITY_NETWORK_PATH:
47 type_str = "NETWORK_PATH"
49 return type_str
52 # [MS-GPIPSEC] (LDAP)
53 # [MS-GPDPC] Deployed Printer Connections (LDAP)
54 # [MS-GPPREF] Preferences Extension (XML)
55 # [MS-GPWL] Wireless/Wired Protocol Extension (LDAP)
56 class GPParser(object):
57 encoding = 'utf-16'
58 output_encoding = 'utf-8'
60 def parse(self, contents):
61 pass
63 def write_xml(self, filename):
64 with open(filename, 'w') as f:
65 f.write('<?xml version="1.0" encoding="utf-8"?><UnknownFile/>')
67 def load_xml(self, filename):
68 pass
70 def write_binary(self, filename):
71 raise GPNoParserException("This file has no parser available.")
73 def write_pretty_xml(self, xml_element, handle):
74 # Add the xml header as well as format it nicely.
75 # ElementTree doesn't have a pretty-print, so use minidom.
77 et = ElementTree(xml_element)
78 temporary_bytes = BytesIO()
79 et.write(temporary_bytes, encoding=self.output_encoding,
80 xml_declaration=True)
81 minidom_parsed = minidom.parseString(temporary_bytes.getvalue())
82 handle.write(minidom_parsed.toprettyxml(encoding=self.output_encoding))
84 def new_xml_entity(self, name, ent_type):
85 identifier = md5(get_bytes(name)).hexdigest()
87 type_str = entity_type_to_string(ent_type)
89 if type_str is None:
90 raise GPGeneralizeException("No such entity type")
92 # For formatting reasons, align the length of the entities
93 longest = entity_type_to_string(ENTITY_NETWORK_PATH)
94 type_str = type_str.center(len(longest), '_')
96 return "&SAMBA__{}__{}__;".format(type_str, identifier)
98 def generalize_xml(self, root, out_file, global_entities):
99 entities = []
101 # Locate all user_id and all ACLs
102 user_ids = root.findall('.//*[@user_id="TRUE"]')
103 user_ids.sort(key = lambda x: x.tag)
105 for elem in user_ids:
106 old_text = elem.text
107 if old_text is None or old_text == '':
108 continue
110 if old_text in global_entities:
111 elem.text = global_entities[old_text]
112 entities.append((elem.text, old_text))
113 else:
114 elem.text = self.new_xml_entity(old_text,
115 ENTITY_USER_ID)
117 entities.append((elem.text, old_text))
118 global_entities.update([(old_text, elem.text)])
120 acls = root.findall('.//*[@acl="TRUE"]')
121 acls.sort(key = lambda x: x.tag)
123 for elem in acls:
124 old_text = elem.text
126 if old_text is None or old_text == '':
127 continue
129 if old_text in global_entities:
130 elem.text = global_entities[old_text]
131 entities.append((elem.text, old_text))
132 else:
133 elem.text = self.new_xml_entity(old_text,
134 ENTITY_SDDL_ACL)
136 entities.append((elem.text, old_text))
137 global_entities.update([(old_text, elem.text)])
139 share_paths = root.findall('.//*[@network_path="TRUE"]')
140 share_paths.sort(key = lambda x: x.tag)
142 for elem in share_paths:
143 old_text = elem.text
145 if old_text is None or old_text == '':
146 continue
148 stripped = old_text.lstrip('\\')
149 file_server = stripped.split('\\')[0]
151 server_index = old_text.find(file_server)
153 remaining = old_text[server_index + len(file_server):]
154 old_text = old_text[:server_index] + file_server
156 if old_text in global_entities:
157 elem.text = global_entities[old_text] + remaining
158 to_put = global_entities[old_text]
159 entities.append((to_put, old_text))
160 else:
161 to_put = self.new_xml_entity(old_text,
162 ENTITY_NETWORK_PATH)
163 elem.text = to_put + remaining
165 entities.append((to_put, old_text))
166 global_entities.update([(old_text, to_put)])
168 # Call any file specific customization of entities
169 # (which appear in any subclasses).
170 entities.extend(self.custom_entities(root, global_entities))
172 output_xml = tostring(root)
174 for ent in entities:
175 entb = get_bytes(ent[0])
176 output_xml = output_xml.replace(entb.replace(b'&', b'&amp;'), entb)
178 with open(out_file, 'wb') as f:
179 f.write(output_xml)
181 return entities
183 def custom_entities(self, root, global_entities):
184 # Override this method to do special entity handling
185 return []