package main import ( "io/ioutil" "net/http" "strings" lxml "github.com/lestrrat-go/libxml2" lxmlp "github.com/lestrrat-go/libxml2/parser" lxmlt "github.com/lestrrat-go/libxml2/types" "github.com/lestrrat-go/libxml2/xsd" rpaths "r00t2.io/sysutils/paths" ) func NewNsXml(raw *[]byte) (x *NsXml, err error) { var xml NsXml xml.Raw = raw x = &xml return } func (x *NsXml) Validate(defaults bool) (bool, error) { // We need the XSD before we can validate. if err := x.getXSD(); err != nil { return false, err } return false, nil } func (x *NsXml) getXSD() error { if err := x.mapNS(); err != nil { return err } r := uriRe.FindStringSubmatch(strings.ToLower(x.XSD.XSDPath)) reResults := make(map[string]string) for i, name := range uriRe.SubexpNames() { if i != 0 && name != "" { reResults[name] = r[i] } } var xsdRaw []byte switch reResults["type"] { case "file": p := reResults["path"] exists, err := rpaths.RealPathExists(&p) if (err != nil) || (!exists) { return err } xsdRaw, err = ioutil.ReadFile(p) if err != nil { return err } case "http", "https": resp, err := http.Get(x.XSD.XSDPath) if err != nil { return err } defer resp.Body.Close() xsdRaw, err = ioutil.ReadAll(resp.Body) if err != nil { return err } default: return errors.New("invalid URI type for schemaLocation") } x.XSD, err = xsd.Parse(xsdRaw) if err != nil { return err } } // mapNS (tries to) extract the default namespace from the document via the SchemaLocation property of XML, sets DefaultNS, // and then gets the XSDPath specified therein. func (x *NsXml) mapNS() error { if x.XSD.Pointer() != 0 { // Already set. return nil } if err := x.parse(); err != nil { return err } x.DefaultNS = x.Root.NamespaceURI() /* x.DefaultNS = *x.XML.XMLName.Space sl := *x.XML.SchemaLocation */ ns := strings.Fields(sl) if ns != nil { if len(ns) > 2 { return errors.New("too many values for a valid schemaLocation") } else if len(ns) == 0 { return errors.New("no specified value for schemaLocation") } else if len(ns) == 1 { // LAZY. This is improper XML, but is commonly used regardless. x.XSDPath = ns[0] } else { if ns[0] == x.DefaultNS { x.XSDPath = ns[1] } } } return nil } // parse parses the x.XML into its x.LXML func (x *NsXml) parse() error { if x.LXML != nil { return nil // Already parsed } if x.XML == nil { return errors.New("XML property is empty") } x.LXML, err = lxml.Parse(*x.XML, lxmlp.XMLParseBigLines, lxmlp.XMLParseXInclude) if err != nil { return err } de, err := x.LXML.DocumentElement() if err != nil { return err } x.Root = de.(lxmlt.Element) return nil }