This repository has been archived by the owner on Dec 1, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
misc.go
379 lines (316 loc) · 7.93 KB
/
misc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
package schemax
/*
IsNumericOID returns a Boolean value indicative of the outcome of an
attempt to parse input value id in the context of an unencoded ASN.1
OBJECT IDENTIFIER in dot form, e.g.:
1.3.6.1.4.1.56521.999
The qualifications are as follows:
- Must only consist of digits (arcs) and dot (ASCII FULL STOP) delimiters
- Must begin with a root arc: 0, 1 or 2
- Second-level arcs below root arcs 0 and 1 cannot be greater than 39
- Cannot end with a dot
- Dots cannot be contiguous
- Though arcs are unbounded, no arc may ever be negative
- OID must consist of at least two (2) arcs
Note: poached from JesseCoretta/go-objectid.
*/
func IsNumericOID(id string) bool {
return isNumericOID(id)
}
func isNumericOID(id string) bool {
if !isValidOIDPrefix(id) {
return false
}
var last rune
for i, c := range id {
switch {
case c == '.':
if last == c {
return false
} else if i == len(id)-1 {
return false
}
last = '.'
case ('0' <= c && c <= '9'):
last = c
continue
}
}
return true
}
func isValidOIDPrefix(id string) bool {
slices := split(id, `.`)
if len(slices) < 2 {
return false
}
root, err := atoi(slices[0])
if err != nil {
return false
}
if !(0 <= root && root <= 2) {
return false
}
var sub int
if sub, err = atoi(slices[1]); err != nil {
return false
} else if !(0 <= sub && sub <= 39) && root != 2 {
return false
}
return true
}
func isAlnum(x rune) bool {
return isDigit(x) || isAlpha(x)
}
func bool2str(x bool) string {
if x {
return `true`
}
return `false`
}
/*
IsDescriptor scans the input string val and judges whether it
qualifies as an RFC 4512 "descr", in that all of the following
evaluate as true:
- Non-zero in length
- Begins with an alphabetical character
- Ends in an alphanumeric character
- Contains only alphanumeric characters or hyphens
- No contiguous hyphens
This function is an alternative to engaging the [antlr4512]
parsing subsystem.
*/
func IsDescriptor(val string) bool {
return isDescriptor(val)
}
func isDescriptor(val string) bool {
if len(val) == 0 {
return false
}
// must begin with an alpha.
if !isAlpha(rune(val[0])) {
return false
}
// can only end in alnum.
if !isAlnum(rune(val[len(val)-1])) {
return false
}
// watch hyphens to avoid contiguous use
var lastHyphen bool
// iterate all characters in val, checking
// each one for "descr" validity.
for i := 0; i < len(val); i++ {
ch := rune(val[i])
switch {
case isAlnum(ch):
lastHyphen = false
case ch == '-':
if lastHyphen {
// cannot use consecutive hyphens
return false
}
lastHyphen = true
default:
// invalid character (none of [a-zA-Z0-9\-])
return false
}
}
return true
}
/*
mapTransferExtensions returns the provided dest instance of DefinitionMap,
following an attempt to copy all extensions found within src into dest.
This is mainly used to keep cyclomatics low during presentation and marshaling
procedures and may be used for any Definition qualifier.
The dest input value must be initialized else go will panic.
*/
func mapTransferExtensions(src Definition, dest DefinitionMap) DefinitionMap {
exts := src.Extensions()
for _, k := range exts.Keys() {
if ext, found := exts.get(k); found {
dest[k] = ext.List()
}
}
return dest
}
/*
condenseWHSP returns input string b with all contiguous
WHSP characters condensed into single space characters.
WHSP is qualified through space or TAB chars (ASCII #32
and #9 respectively).
*/
func condenseWHSP(b string) (a string) {
// remove leading and trailing
// WHSP characters ...
b = trimS(b)
b = repAll(b, string(rune(10)), string(rune(32)))
var last bool
for i := 0; i < len(b); i++ {
c := rune(b[i])
switch c {
// match space (32) or tab (9)
case rune(9), rune(10), rune(32):
if !last {
last = true
a += string(rune(32))
}
default:
if last {
last = false
}
a += string(c)
}
}
a = trimS(a) //once more
return
}
/*
governedDistinguishedName contains the components of a distinguished
name and the integer length of those components that are not distinct.
For example, a root suffix of "dc=example,dc=com" has two (2) components,
meaning its flattened integer length is one (1), as "dc=example" and
"dc=com" are not separate and distinct.
An easier, though less descriptive, explanation of the integer length
is simply "the number of comma characters at the far right (root) of
the DN which do NOT describe separate entries. Again, the comma in the
"dc=example,dc=com" suffix equals a length of one (1).
*/
type governedDistinguishedName struct {
components [][][]string
flat int
length int
}
func (r *governedDistinguishedName) isZero() bool {
if r != nil {
return len(r.components) == 0
}
return true
}
/*
tokenizeDN will attempt to tokenize the input dn value.
Through the act of tokenization, the following occurs:
An LDAP distinguished name, such as "uid=jesse+gidNumber=5042,ou=People,dc=example,dc=com,
... is returned as:
[][][]string{
[][]string{
[]string{`uid`,`jesse`},
[]string{`gidNumber`,`5042`},
},
[][]string{
[]string{`ou`,`People`},
},
[][]string{
[]string{`dc`,`example`},
},
[][]string{
[]string{`dc`,`com`},
},
}
Please note that this function is NOT considered a true parser. If actual
parsing of component attribute values within a given DN is either desired
or required, consider use of a proper parser such as [go-ldap/v3's ParseDN]
function.
flat is an integer value that describes the flattened root suffix "length".
For instance, given the root suffix of "dc=example,dc=com" -- which is a
single entry and not two separate entries -- the input value should be the
integer 1.
[go-ldap/v3's ParseDN]: https://pkg.go.dev/github.com/go-ldap/ldap/v3#ParseDN
*/
func tokenizeDN(d string, flat ...int) (x *governedDistinguishedName) {
if len(d) == 0 {
return
}
x = &governedDistinguishedName{
components: make([][][]string, 0),
}
if len(flat) > 0 {
x.flat = flat[0]
}
rdns := splitUnescaped(d, `,`, `\`)
lr := len(rdns)
if lr == x.flat || x.flat < 0 {
// bogus depth
return
}
for i := 0; i < lr; i++ {
var atvs [][]string = make([][]string, 0)
srdns := splitUnescaped(rdns[i], `+`, `\`)
for j := 0; j < len(srdns); j++ {
if atv := split(srdns[j], `=`); len(atv) == 2 {
atvs = append(atvs, atv)
} else {
atvs = append(atvs, []string{})
}
}
x.components = append(x.components, atvs)
}
if x.flat > 0 {
e := lr - 1
f := e - x.flat
x.components[f] = append(x.components[f], x.components[e]...)
x.components = x.components[:e]
}
x.length = len(x.components)
return
}
func detokenizeDN(x *governedDistinguishedName) (dtkz string) {
if x.isZero() {
return
}
var rdns []string
for i := 0; i < x.length; i++ {
rdn := x.components[i]
char := `,`
if i < x.length-x.flat && len(rdn) > 1 {
char = `+`
}
//var r []string
var atv []string
for j := 0; j < len(rdn); j++ {
atv = append(atv, rdn[j][0]+`=`+rdn[j][1])
}
rdns = append(rdns, join(atv, char))
}
dtkz = join(rdns, `,`)
return
}
func splitUnescaped(str, sep, esc string) (slice []string) {
slice = split(str, sep)
for i := len(slice) - 2; i >= 0; i-- {
if hasSfx(slice[i], esc) {
slice[i] = slice[i][:len(slice[i])-len(esc)] + sep + slice[i+1]
slice = append(slice[:i+1], slice[i+2:]...)
}
}
return
}
/*
strInSlice returns a Boolean value indicative of whether the
specified string (str) is present within slice. Please note
that case is a significant element in the matching process.
*/
func strInSlice(str string, slice []string) bool {
for i := 0; i < len(slice); i++ {
if str == slice[i] {
return true
}
}
return false
}
func getMarshalMap(r Definition, z any) (m map[string]any, err error) {
if r.IsZero() {
err = ErrNilReceiver
return
}
switch tv := z.(type) {
case map[string]any:
m = tv
case DefinitionMap:
m = make(map[string]any, 0)
for k, v := range tv {
m[k] = v
}
default:
err = ErrInvalidType
}
return
}