diff --git a/common.py b/common.py index 1ee5b7f..11bd9e2 100644 --- a/common.py +++ b/common.py @@ -4,19 +4,24 @@ import urllib3 import os import sys from io import StringIO -from xml.etree import ElementTree as ET +from lxml import etree +from lxml.builder import ElementMaker import requests from passwords import * +E_plain = ElementMaker(nsmap={"hid": "http://www.hidglobal.com/VertX"}) +E = ElementMaker(namespace="http://www.hidglobal.com/VertX", + nsmap={"hid": "http://www.hidglobal.com/VertX"}) + # it's fine, ssl certs are for losers anyway urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) -config = json.load( - open(os.path.dirname(os.path.abspath(__file__)) + "/config.json")) - -ET.register_namespace("hid", "http://www.hidglobal.com/VertX") -ET.register_namespace("hid", "http://www.hidcorp.com/VertX") +try: + config = json.load( + open(os.path.dirname(os.path.abspath(__file__)) + "/config.json")) +except NameError: + config = json.load(open("config.json")) fieldnames = "CardNumber,CardFormat,PinRequired,PinCode,ExtendedAccess,ExpiryDate,Forename,Initial,Surname,Email,Phone,Custom1,Custom2,Schedule1,Schedule2,Schedule3,Schedule4,Schedule5,Schedule6,Schedule7,Schedule8".split(",") @@ -78,7 +83,7 @@ def doImportRequest(ip, params=None, files=None): auth=requests.auth.HTTPDigestAuth(DOOR_USERNAME, DOOR_PASSWORD), timeout=60, verify=False) # ignore insecure SSL - xml = ET.XML(r.text) + xml = etree.XML(r.content) if r.status_code != 200 \ or len(xml.findall("{http://www.hidglobal.com/VertX}Error")) > 0: print("Door Updating Error: ", r.status_code, r.reason) @@ -94,16 +99,17 @@ def doCSVImport(doorIP, csv): doImportRequest(doorIP, {"task": "importDone"}) def doXMLRequest(doorIP, xml, prefix=b''): + if not isinstance(xml, str): xml = etree.tostring(xml) r = requests.get( 'https://' + doorIP + '/cgi-bin/vertx_xml.cgi', params={'XML': prefix + xml}, auth=requests.auth.HTTPDigestAuth(DOOR_USERNAME, DOOR_PASSWORD), verify=False) - xml = ET.XML(r.text) + resp_xml = etree.XML(r.content) # probably meed to be more sane about this if r.status_code != 200 \ - or len(xml.findall("{http://www.hidglobal.com/VertX}Error")) > 0: + or len(resp_xml.findall("{*}Error")) > 0: print("Door Updating Error: ", r.status_code, r.reason) print(r.text) sys.exit(1) - return xml + return resp_xml diff --git a/doorUtil.py b/doorUtil.py index 18b4620..22e6b0c 100644 --- a/doorUtil.py +++ b/doorUtil.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 import requests -from xml.etree import ElementTree as ET import csv from io import StringIO @@ -27,11 +26,10 @@ def sendSchedule(target_ip): doCSVImport(target_ip, outString) # clear all schedules - delXML = ET.Element("VertXMessage") - for ii in range(1, 8): - ET.SubElement(delXML, "hid:Schedules", - attrib={"action": "DD", "scheduleID": str(ii)}) - doXMLRequest(target_ip, ET.tostring(delXML)) + delXML = E_plain.VertXMessage( + *[E.Schedules({"action": "DD", "scheduleID": str(ii)}) + for ii in range(1, 8)]) + doXMLRequest(target_ip, delXML) # load new schedules with open("schedules.xml", "rb") as f: @@ -41,21 +39,18 @@ def sendCardFormat(targetIP, formatName, templateID, facilityCode): # TODO: add delete formats # delete example: - el = ET.Element("VertXMessage") - formats = ET.SubElement(el, "hid:CardFormats", attrib={"action": "AD"}) - fmt = ET.SubElement(formats, "hid:CardFormat", - attrib={"formatName": formatName, - "templateID": str(templateID)}) - ET.SubElement(fmt, "hid:FixedField", - attrib={"value": str(facilityCode)}) - return doXMLRequest(targetIP, ET.tostring(el)) + el = E_plain.VertXMessage( + E.CardFormats({"action": "AD"}, + E.CardFormat({"formatName": formatName, + "templateID": str(templateID)} + E.FixedField({"value": str(facilityCode)})))) + return doXMLRequest(targetIP, el) def lockOrUnlockDoor(targetIP, lock=True): - el = ET.Element("VertXMessage") - ET.SubElement(el, "hid:Doors", - attrib={"action": "CM", - "command": "lockDoor" if lock else "unlockDoor"}) - return doXMLRequest(targetIP, ET.tostring(el)) + el = E_plain.VertXMessage( + E.Doors({"action": "CM", + "command": "lockDoor" if lock else "unlockDoor"})) + return doXMLRequest(targetIP, el) def forEachDoor(fxn): for doorName, doorData in config["doors"].items(): diff --git a/events.py b/events.py index 3a95d22..e90aa2f 100755 --- a/events.py +++ b/events.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 from collections import defaultdict -from xml.etree import ElementTree as ET +from lxml import etree import os import re import requests @@ -20,14 +20,14 @@ def getStrings(targetIP): def getMessages(doorName, doorIP): # get parameters for messages to get? # honestly not really sure why this is required, their API is confusing - parXMLIn = ET.Element("VertXMessage") - ET.SubElement(parXMLIn, "hid:EventMessages", attrib={"action": "LR"}) - parXMLOut = doXMLRequest(doorIP, ET.tostring(parXMLIn)) - ET.dump(parXMLOut) + parXMLIn = E_plain.VertXMessage( + E.EventMessages({"action": "LR"})) + parXMLOut = doXMLRequest(doorIP, parXMLIn) + etree.dump(parXMLOut) if os.path.exists("logs/" + doorName + ".xml"): # read last log - tree = ET.ElementTree(None, "logs/" + doorName + ".xml") + tree = etree.ElementTree(file="logs/" + doorName + ".xml") root = tree.getroot() recordCount = int(parXMLOut[0].attrib["historyRecordMarker"]) - \ int(root[0][0].attrib["recordMarker"]) @@ -41,20 +41,19 @@ def getMessages(doorName, doorIP): return print("Getting", recordCount, "records") # get the actual messages - eventsXMLIn = ET.Element("VertXMessage") - ET.SubElement(eventsXMLIn, "hid:EventMessages", - attrib={"action": "LR", - "recordCount": str(recordCount), - "historyRecordMarker": parXMLOut[0].attrib["historyRecordMarker"], - "historyTimestamp": parXMLOut[0].attrib["historyTimestamp"]}) - eventsXMLOut = doXMLRequest(doorIP, ET.tostring(eventsXMLIn)) + eventsXMLIn = E_plain.VertXMessage( + E.EventMessages({"action": "LR", + "recordCount": str(recordCount), + "historyRecordMarker": parXMLOut[0].attrib["historyRecordMarker"], + "historyTimestamp": parXMLOut[0].attrib["historyTimestamp"]})) + eventsXMLOut = doXMLRequest(doorIP, eventsXMLIn) #TODO: handle modeRecords=true for index, event in enumerate(eventsXMLOut[0]): event.attrib["recordMarker"] = str(int(parXMLOut[0].attrib["historyRecordMarker"]) - index) if root is None: - tree = ET.ElementTree(eventsXMLOut) + tree = etree.ElementTree(eventsXMLOut) else: for event in reversed(eventsXMLOut[0]): root[0].insert(0, event)