Mercurial > gemma
comparison pkg/soap/validate.go @ 2181:bd09d6ad4c14
SOAP: Add validating parser (uses 'xmllint') for manual uploads.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Mon, 11 Feb 2019 15:56:59 +0100 |
parents | |
children | 31973f6f5cca |
comparison
equal
deleted
inserted
replaced
2179:20d9b71f4125 | 2181:bd09d6ad4c14 |
---|---|
1 // This is Free Software under GNU Affero General Public License v >= 3.0 | |
2 // without warranty, see README.md and license for details. | |
3 // | |
4 // SPDX-License-Identifier: AGPL-3.0-or-later | |
5 // License-Filename: LICENSES/AGPL-3.0.txt | |
6 // | |
7 // Copyright (C) 2018 by via donau | |
8 // – Österreichische Wasserstraßen-Gesellschaft mbH | |
9 // Software engineering by Intevation GmbH | |
10 // | |
11 // Author(s): | |
12 // * Sascha L. Teichmann <sascha.teichmann@intevation.de> | |
13 | |
14 package soap | |
15 | |
16 import ( | |
17 "bytes" | |
18 "encoding/xml" | |
19 "fmt" | |
20 "io" | |
21 "io/ioutil" | |
22 "os" | |
23 "os/exec" | |
24 "path/filepath" | |
25 "strings" | |
26 | |
27 "golang.org/x/net/html/charset" | |
28 | |
29 "gemma.intevation.de/gemma/pkg/config" | |
30 ) | |
31 | |
32 const linter = "xmllint" | |
33 | |
34 type ( | |
35 ValidationError string | |
36 foundError string | |
37 ) | |
38 | |
39 func (ef foundError) Error() string { | |
40 return string(ef) | |
41 } | |
42 | |
43 func (ve ValidationError) Error() string { | |
44 return string(ve) | |
45 } | |
46 | |
47 func FindSchema(name string) (string, error) { | |
48 name = strings.ToLower(name) | |
49 config.WaitReady() | |
50 for _, root := range filepath.SplitList(config.SchemaDirs()) { | |
51 err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { | |
52 if err != nil { | |
53 return err | |
54 } | |
55 if info == nil { | |
56 return nil | |
57 } | |
58 if info.Mode().IsRegular() && strings.ToLower(info.Name()) == name { | |
59 return foundError(path) | |
60 } | |
61 return nil | |
62 }) | |
63 if path, ok := err.(foundError); ok { | |
64 return string(path), nil | |
65 } | |
66 if err != nil { | |
67 return "", err | |
68 } | |
69 } | |
70 return "", nil | |
71 } | |
72 | |
73 func ValidateFile(fname, schema string, dst interface{}) error { | |
74 f, err := os.Open(fname) | |
75 if err != nil { | |
76 return err | |
77 } | |
78 defer f.Close() | |
79 return Validate(f, schema, dst) | |
80 } | |
81 | |
82 func Validate(r io.Reader, schema string, dst interface{}) error { | |
83 schemaPath, err := FindSchema(schema) | |
84 if err != nil { | |
85 return err | |
86 } | |
87 if schemaPath == "" { | |
88 return fmt.Errorf("no schema file '%s' found", schema) | |
89 } | |
90 linterPath, err := exec.LookPath(linter) | |
91 if err != nil { | |
92 return err | |
93 } | |
94 | |
95 type envelope struct { | |
96 _ xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` | |
97 Body *struct { | |
98 Inner []byte `xml:",innerxml"` | |
99 } `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` | |
100 } | |
101 | |
102 content, err := ioutil.ReadAll(r) | |
103 if err != nil { | |
104 return err | |
105 } | |
106 | |
107 dec := xml.NewDecoder(bytes.NewReader(content)) | |
108 dec.CharsetReader = charset.NewReaderLabel | |
109 | |
110 var env envelope | |
111 if err := dec.Decode(&env); err != nil { | |
112 return err | |
113 } | |
114 | |
115 // It has a body -> throw envelope away. | |
116 if env.Body != nil && len(env.Body.Inner) > 0 { | |
117 content = env.Body.Inner | |
118 } | |
119 | |
120 cmd := exec.Command( | |
121 linterPath, | |
122 "--schema", schemaPath, | |
123 "--noout", | |
124 "-") | |
125 | |
126 var stderr bytes.Buffer | |
127 | |
128 cmd.Stdin = bytes.NewReader(content) | |
129 cmd.Stderr = &stderr | |
130 cmd.Stdout = ioutil.Discard | |
131 | |
132 if err := cmd.Start(); err != nil { | |
133 return err | |
134 } | |
135 | |
136 if err := cmd.Wait(); err != nil { | |
137 if err2, ok := err.(*exec.ExitError); ok { | |
138 if !err2.Success() { | |
139 return ValidationError(stderr.String()) | |
140 } | |
141 } | |
142 return err | |
143 } | |
144 | |
145 // Validation successful -> Deserialize. | |
146 | |
147 dec = xml.NewDecoder(bytes.NewReader(content)) | |
148 dec.CharsetReader = charset.NewReaderLabel | |
149 | |
150 return dec.Decode(dst) | |
151 } |