package conf // The usage of this depends on the successful resolution of https://github.com/lestrrat-go/libxml2/issues/67. import ( `errors` `io/ioutil` `net/http` `regexp` `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` ) var uriRe = regexp.MustCompile(`^(?Pfile|https?)://(?P.+)$`) type NSXML struct { XML *[]byte // The raw XML bytes. LXML lxmlt.Document // The lxml.Parse()'d XML. Root lxmlt.Element // The Document's root element XSD xsd.Schema // Its schema. } 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.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.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 }