...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package xmldsig
18
19 import (
20 "sort"
21
22 "github.com/beevik/etree"
23 )
24
25
26
27
28
29
30
31
32 func SerializeCanonical(oldroot *etree.Element) ([]byte, error) {
33
34 root := oldroot.Copy()
35
36 doc := etree.NewDocument()
37 doc.SetRoot(root)
38 doc.WriteSettings.CanonicalEndTags = true
39 doc.WriteSettings.CanonicalText = true
40 doc.WriteSettings.CanonicalAttrVal = true
41 pullDown(oldroot, root)
42 walkAttributes(root)
43 return doc.WriteToBytes()
44 }
45
46
47 func getDecl(attr etree.Attr) (string, bool) {
48 if attr.Space == "" && attr.Key == "xmlns" {
49 return "", true
50 } else if attr.Space == "xmlns" {
51 return attr.Key, true
52 } else {
53 return "", false
54 }
55 }
56
57
58 func putDecl(space string) string {
59 if space == "" {
60 return "xmlns"
61 }
62 return "xmlns:" + space
63 }
64
65 func walkAttributes(elem *etree.Element) {
66
67 for i := 0; i < len(elem.Attr); {
68 attr := elem.Attr[i]
69 if space, isDecl := getDecl(attr); isDecl && !usesSpace(elem, space) {
70 pushDown(elem, elem, space, putDecl(space), attr.Value)
71 elem.Attr = append(elem.Attr[:i], elem.Attr[i+1:]...)
72 continue
73 }
74 i++
75 }
76 sort.Slice(elem.Attr, func(i, j int) bool {
77 x := elem.Attr[i]
78 y := elem.Attr[j]
79
80 if x.Space == "" && x.Key == "xmlns" {
81 return true
82 } else if y.Space == "" && y.Key == "xmlns" {
83 return false
84 }
85
86 if x.Space == "xmlns" && y.Space != "xmlns" {
87 return true
88 } else if y.Space == "xmlns" && x.Space != "xmlns" {
89 return false
90 }
91
92 if x.Space != y.Space {
93 return x.Space < y.Space
94 }
95 return x.Key < y.Key
96 })
97 for i := 0; i < len(elem.Child); {
98 token := elem.Child[i]
99 switch t := token.(type) {
100 case *etree.Element:
101 walkAttributes(t)
102 case *etree.CharData:
103
104 default:
105
106 elem.Child = append(elem.Child[:i], elem.Child[i+1:]...)
107 continue
108 }
109 i++
110 }
111 }
112
113
114 func usesSpace(elem *etree.Element, space string) bool {
115 if elem.Space == space {
116 return true
117 } else if space == "" {
118
119 return false
120 }
121 for _, attr := range elem.Attr {
122 if attr.Space == space {
123 return true
124 }
125 }
126 return false
127 }
128
129
130
131 func pullDown(oldroot, newroot *etree.Element) {
132 spaces := make(map[string]string)
133 for p := oldroot.Parent(); p != nil; p = p.Parent() {
134 for _, attr := range p.Attr {
135 space, isDecl := getDecl(attr)
136 if !isDecl {
137 continue
138 }
139 if spaces[space] != "" {
140 continue
141 }
142 spaces[space] = attr.Value
143 }
144 }
145 for space, value := range spaces {
146 pushDown(nil, newroot, space, putDecl(space), value)
147 }
148 }
149
150
151 func pushDown(top, elem *etree.Element, space, key, value string) {
152 if elem != top && elem.SelectAttr(key) != nil {
153
154 return
155 } else if usesSpace(elem, space) {
156
157 elem.CreateAttr(key, value)
158 } else {
159
160 for _, elem := range elem.ChildElements() {
161 pushDown(top, elem, space, key, value)
162 }
163 }
164 }
165
View as plain text