summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/go-ldap
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/go-ldap')
-rw-r--r--vendor/github.com/go-ldap/ldap/.travis.yml9
-rw-r--r--vendor/github.com/go-ldap/ldap/LICENSE43
-rw-r--r--vendor/github.com/go-ldap/ldap/control.go12
-rw-r--r--vendor/github.com/go-ldap/ldap/control_test.go39
-rw-r--r--vendor/github.com/go-ldap/ldap/dn.go96
-rw-r--r--vendor/github.com/go-ldap/ldap/dn_test.go139
-rw-r--r--vendor/github.com/go-ldap/ldap/ldap.go55
7 files changed, 344 insertions, 49 deletions
diff --git a/vendor/github.com/go-ldap/ldap/.travis.yml b/vendor/github.com/go-ldap/ldap/.travis.yml
index 7e2f641e7..e32a2aa75 100644
--- a/vendor/github.com/go-ldap/ldap/.travis.yml
+++ b/vendor/github.com/go-ldap/ldap/.travis.yml
@@ -1,15 +1,20 @@
language: go
env:
global:
- - VET_VERSIONS="1.5 1.6 tip"
- - LINT_VERSIONS="1.5 1.6 tip"
+ - VET_VERSIONS="1.6 1.7 tip"
+ - LINT_VERSIONS="1.6 1.7 tip"
go:
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
+ - 1.7
- tip
+matrix:
+ fast_finish: true
+ allow_failures:
+ - go: tip
go_import_path: gopkg.in/ldap.v2
install:
- go get gopkg.in/asn1-ber.v1
diff --git a/vendor/github.com/go-ldap/ldap/LICENSE b/vendor/github.com/go-ldap/ldap/LICENSE
index 744875676..6c0ed4b38 100644
--- a/vendor/github.com/go-ldap/ldap/LICENSE
+++ b/vendor/github.com/go-ldap/ldap/LICENSE
@@ -1,27 +1,22 @@
-Copyright (c) 2012 The Go Authors. All rights reserved.
+The MIT License (MIT)
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
+Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)
+Portions copyright (c) 2015-2016 go-ldap Authors
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/go-ldap/ldap/control.go b/vendor/github.com/go-ldap/ldap/control.go
index 5c62118d4..342f325ca 100644
--- a/vendor/github.com/go-ldap/ldap/control.go
+++ b/vendor/github.com/go-ldap/ldap/control.go
@@ -334,18 +334,18 @@ func DecodeControl(packet *ber.Packet) Control {
for _, child := range sequence.Children {
if child.Tag == 0 {
//Warning
- child := child.Children[0]
- packet := ber.DecodePacket(child.Data.Bytes())
+ warningPacket := child.Children[0]
+ packet := ber.DecodePacket(warningPacket.Data.Bytes())
val, ok := packet.Value.(int64)
if ok {
- if child.Tag == 0 {
+ if warningPacket.Tag == 0 {
//timeBeforeExpiration
c.Expire = val
- child.Value = c.Expire
- } else if child.Tag == 1 {
+ warningPacket.Value = c.Expire
+ } else if warningPacket.Tag == 1 {
//graceAuthNsRemaining
c.Grace = val
- child.Value = c.Grace
+ warningPacket.Value = c.Grace
}
}
} else if child.Tag == 1 {
diff --git a/vendor/github.com/go-ldap/ldap/control_test.go b/vendor/github.com/go-ldap/ldap/control_test.go
index 3fcdab0d7..11527463e 100644
--- a/vendor/github.com/go-ldap/ldap/control_test.go
+++ b/vendor/github.com/go-ldap/ldap/control_test.go
@@ -56,3 +56,42 @@ func runControlTest(t *testing.T, originalControl Control) {
t.Errorf("%sgot different type decoding from encoded bytes: %T vs %T", header, fromBytes, originalControl)
}
}
+
+func TestDescribeControlManageDsaIT(t *testing.T) {
+ runAddControlDescriptions(t, NewControlManageDsaIT(false), "Control Type (Manage DSA IT)")
+ runAddControlDescriptions(t, NewControlManageDsaIT(true), "Control Type (Manage DSA IT)", "Criticality")
+}
+
+func TestDescribeControlPaging(t *testing.T) {
+ runAddControlDescriptions(t, NewControlPaging(100), "Control Type (Paging)", "Control Value (Paging)")
+ runAddControlDescriptions(t, NewControlPaging(0), "Control Type (Paging)", "Control Value (Paging)")
+}
+
+func TestDescribeControlString(t *testing.T) {
+ runAddControlDescriptions(t, NewControlString("x", true, "y"), "Control Type ()", "Criticality", "Control Value")
+ runAddControlDescriptions(t, NewControlString("x", true, ""), "Control Type ()", "Criticality", "Control Value")
+ runAddControlDescriptions(t, NewControlString("x", false, "y"), "Control Type ()", "Control Value")
+ runAddControlDescriptions(t, NewControlString("x", false, ""), "Control Type ()", "Control Value")
+}
+
+func runAddControlDescriptions(t *testing.T, originalControl Control, childDescriptions ...string) {
+ header := ""
+ if callerpc, _, line, ok := runtime.Caller(1); ok {
+ if caller := runtime.FuncForPC(callerpc); caller != nil {
+ header = fmt.Sprintf("%s:%d: ", caller.Name(), line)
+ }
+ }
+
+ encodedControls := encodeControls([]Control{originalControl})
+ addControlDescriptions(encodedControls)
+ encodedPacket := encodedControls.Children[0]
+ if len(encodedPacket.Children) != len(childDescriptions) {
+ t.Errorf("%sinvalid number of children: %d != %d", header, len(encodedPacket.Children), len(childDescriptions))
+ }
+ for i, desc := range childDescriptions {
+ if encodedPacket.Children[i].Description != desc {
+ t.Errorf("%sdescription not as expected: %s != %s", header, encodedPacket.Children[i].Description, desc)
+ }
+ }
+
+}
diff --git a/vendor/github.com/go-ldap/ldap/dn.go b/vendor/github.com/go-ldap/ldap/dn.go
index cc70c894c..a8ece3142 100644
--- a/vendor/github.com/go-ldap/ldap/dn.go
+++ b/vendor/github.com/go-ldap/ldap/dn.go
@@ -83,9 +83,19 @@ func ParseDN(str string) (*DN, error) {
attribute := new(AttributeTypeAndValue)
escaping := false
+ unescapedTrailingSpaces := 0
+ stringFromBuffer := func() string {
+ s := buffer.String()
+ s = s[0 : len(s)-unescapedTrailingSpaces]
+ buffer.Reset()
+ unescapedTrailingSpaces = 0
+ return s
+ }
+
for i := 0; i < len(str); i++ {
char := str[i]
if escaping {
+ unescapedTrailingSpaces = 0
escaping = false
switch char {
case ' ', '"', '#', '+', ',', ';', '<', '=', '>', '\\':
@@ -107,10 +117,10 @@ func ParseDN(str string) (*DN, error) {
buffer.WriteByte(dst[0])
i++
} else if char == '\\' {
+ unescapedTrailingSpaces = 0
escaping = true
} else if char == '=' {
- attribute.Type = buffer.String()
- buffer.Reset()
+ attribute.Type = stringFromBuffer()
// Special case: If the first character in the value is # the
// following data is BER encoded so we can just fast forward
// and decode.
@@ -133,7 +143,7 @@ func ParseDN(str string) (*DN, error) {
}
} else if char == ',' || char == '+' {
// We're done with this RDN or value, push it
- attribute.Value = buffer.String()
+ attribute.Value = stringFromBuffer()
rdn.Attributes = append(rdn.Attributes, attribute)
attribute = new(AttributeTypeAndValue)
if char == ',' {
@@ -141,8 +151,17 @@ func ParseDN(str string) (*DN, error) {
rdn = new(RelativeDN)
rdn.Attributes = make([]*AttributeTypeAndValue, 0)
}
- buffer.Reset()
+ } else if char == ' ' && buffer.Len() == 0 {
+ // ignore unescaped leading spaces
+ continue
} else {
+ if char == ' ' {
+ // Track unescaped spaces in case they are trailing and we need to remove them
+ unescapedTrailingSpaces++
+ } else {
+ // Reset if we see a non-space char
+ unescapedTrailingSpaces = 0
+ }
buffer.WriteByte(char)
}
}
@@ -150,9 +169,76 @@ func ParseDN(str string) (*DN, error) {
if len(attribute.Type) == 0 {
return nil, errors.New("DN ended with incomplete type, value pair")
}
- attribute.Value = buffer.String()
+ attribute.Value = stringFromBuffer()
rdn.Attributes = append(rdn.Attributes, attribute)
dn.RDNs = append(dn.RDNs, rdn)
}
return dn, nil
}
+
+// Equal returns true if the DNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
+// Returns true if they have the same number of relative distinguished names
+// and corresponding relative distinguished names (by position) are the same.
+func (d *DN) Equal(other *DN) bool {
+ if len(d.RDNs) != len(other.RDNs) {
+ return false
+ }
+ for i := range d.RDNs {
+ if !d.RDNs[i].Equal(other.RDNs[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// AncestorOf returns true if the other DN consists of at least one RDN followed by all the RDNs of the current DN.
+// "ou=widgets,o=acme.com" is an ancestor of "ou=sprockets,ou=widgets,o=acme.com"
+// "ou=widgets,o=acme.com" is not an ancestor of "ou=sprockets,ou=widgets,o=foo.com"
+// "ou=widgets,o=acme.com" is not an ancestor of "ou=widgets,o=acme.com"
+func (d *DN) AncestorOf(other *DN) bool {
+ if len(d.RDNs) >= len(other.RDNs) {
+ return false
+ }
+ // Take the last `len(d.RDNs)` RDNs from the other DN to compare against
+ otherRDNs := other.RDNs[len(other.RDNs)-len(d.RDNs):]
+ for i := range d.RDNs {
+ if !d.RDNs[i].Equal(otherRDNs[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// Equal returns true if the RelativeDNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
+// Relative distinguished names are the same if and only if they have the same number of AttributeTypeAndValues
+// and each attribute of the first RDN is the same as the attribute of the second RDN with the same attribute type.
+// The order of attributes is not significant.
+// Case of attribute types is not significant.
+func (r *RelativeDN) Equal(other *RelativeDN) bool {
+ if len(r.Attributes) != len(other.Attributes) {
+ return false
+ }
+ return r.hasAllAttributes(other.Attributes) && other.hasAllAttributes(r.Attributes)
+}
+
+func (r *RelativeDN) hasAllAttributes(attrs []*AttributeTypeAndValue) bool {
+ for _, attr := range attrs {
+ found := false
+ for _, myattr := range r.Attributes {
+ if myattr.Equal(attr) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return false
+ }
+ }
+ return true
+}
+
+// Equal returns true if the AttributeTypeAndValue is equivalent to the specified AttributeTypeAndValue
+// Case of the attribute type is not significant
+func (a *AttributeTypeAndValue) Equal(other *AttributeTypeAndValue) bool {
+ return strings.EqualFold(a.Type, other.Type) && a.Value == other.Value
+}
diff --git a/vendor/github.com/go-ldap/ldap/dn_test.go b/vendor/github.com/go-ldap/ldap/dn_test.go
index 39817c427..5055cc15b 100644
--- a/vendor/github.com/go-ldap/ldap/dn_test.go
+++ b/vendor/github.com/go-ldap/ldap/dn_test.go
@@ -31,6 +31,22 @@ func TestSuccessfulDNParsing(t *testing.T) {
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}},
"CN=Lu\\C4\\8Di\\C4\\87": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"CN", "Lučić"}}}}},
+ " CN = Lu\\C4\\8Di\\C4\\87 ": ldap.DN{[]*ldap.RelativeDN{
+ &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"CN", "Lučić"}}}}},
+ ` A = 1 , B = 2 `: ldap.DN{[]*ldap.RelativeDN{
+ &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"A", "1"}}},
+ &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"B", "2"}}}}},
+ ` A = 1 + B = 2 `: ldap.DN{[]*ldap.RelativeDN{
+ &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{
+ &ldap.AttributeTypeAndValue{"A", "1"},
+ &ldap.AttributeTypeAndValue{"B", "2"}}}}},
+ ` \ \ A\ \ = \ \ 1\ \ , \ \ B\ \ = \ \ 2\ \ `: ldap.DN{[]*ldap.RelativeDN{
+ &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{" A ", " 1 "}}},
+ &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{" B ", " 2 "}}}}},
+ ` \ \ A\ \ = \ \ 1\ \ + \ \ B\ \ = \ \ 2\ \ `: ldap.DN{[]*ldap.RelativeDN{
+ &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{
+ &ldap.AttributeTypeAndValue{" A ", " 1 "},
+ &ldap.AttributeTypeAndValue{" B ", " 2 "}}}}},
}
for test, answer := range testcases {
@@ -41,6 +57,13 @@ func TestSuccessfulDNParsing(t *testing.T) {
}
if !reflect.DeepEqual(dn, &answer) {
t.Errorf("Parsed DN %s is not equal to the expected structure", test)
+ t.Logf("Expected:")
+ for _, rdn := range answer.RDNs {
+ for _, attribs := range rdn.Attributes {
+ t.Logf("#%v\n", attribs)
+ }
+ }
+ t.Logf("Actual:")
for _, rdn := range dn.RDNs {
for _, attribs := range rdn.Attributes {
t.Logf("#%v\n", attribs)
@@ -68,3 +91,119 @@ func TestErrorDNParsing(t *testing.T) {
}
}
}
+
+func TestDNEqual(t *testing.T) {
+ testcases := []struct {
+ A string
+ B string
+ Equal bool
+ }{
+ // Exact match
+ {"", "", true},
+ {"o=A", "o=A", true},
+ {"o=A", "o=B", false},
+
+ {"o=A,o=B", "o=A,o=B", true},
+ {"o=A,o=B", "o=A,o=C", false},
+
+ {"o=A+o=B", "o=A+o=B", true},
+ {"o=A+o=B", "o=A+o=C", false},
+
+ // Case mismatch in type is ignored
+ {"o=A", "O=A", true},
+ {"o=A,o=B", "o=A,O=B", true},
+ {"o=A+o=B", "o=A+O=B", true},
+
+ // Case mismatch in value is significant
+ {"o=a", "O=A", false},
+ {"o=a,o=B", "o=A,O=B", false},
+ {"o=a+o=B", "o=A+O=B", false},
+
+ // Multi-valued RDN order mismatch is ignored
+ {"o=A+o=B", "O=B+o=A", true},
+ // Number of RDN attributes is significant
+ {"o=A+o=B", "O=B+o=A+O=B", false},
+
+ // Missing values are significant
+ {"o=A+o=B", "O=B+o=A+O=C", false}, // missing values matter
+ {"o=A+o=B+o=C", "O=B+o=A", false}, // missing values matter
+
+ // Whitespace tests
+ // Matching
+ {
+ "cn=John Doe, ou=People, dc=sun.com",
+ "cn=John Doe, ou=People, dc=sun.com",
+ true,
+ },
+ // Difference in leading/trailing chars is ignored
+ {
+ "cn=John Doe, ou=People, dc=sun.com",
+ "cn=John Doe,ou=People,dc=sun.com",
+ true,
+ },
+ // Difference in values is significant
+ {
+ "cn=John Doe, ou=People, dc=sun.com",
+ "cn=John Doe, ou=People, dc=sun.com",
+ false,
+ },
+ }
+
+ for i, tc := range testcases {
+ a, err := ldap.ParseDN(tc.A)
+ if err != nil {
+ t.Errorf("%d: %v", i, err)
+ continue
+ }
+ b, err := ldap.ParseDN(tc.B)
+ if err != nil {
+ t.Errorf("%d: %v", i, err)
+ continue
+ }
+ if expected, actual := tc.Equal, a.Equal(b); expected != actual {
+ t.Errorf("%d: when comparing '%s' and '%s' expected %v, got %v", i, tc.A, tc.B, expected, actual)
+ continue
+ }
+ if expected, actual := tc.Equal, b.Equal(a); expected != actual {
+ t.Errorf("%d: when comparing '%s' and '%s' expected %v, got %v", i, tc.A, tc.B, expected, actual)
+ continue
+ }
+ }
+}
+
+func TestDNAncestor(t *testing.T) {
+ testcases := []struct {
+ A string
+ B string
+ Ancestor bool
+ }{
+ // Exact match returns false
+ {"", "", false},
+ {"o=A", "o=A", false},
+ {"o=A,o=B", "o=A,o=B", false},
+ {"o=A+o=B", "o=A+o=B", false},
+
+ // Mismatch
+ {"ou=C,ou=B,o=A", "ou=E,ou=D,ou=B,o=A", false},
+
+ // Descendant
+ {"ou=C,ou=B,o=A", "ou=E,ou=C,ou=B,o=A", true},
+ }
+
+ for i, tc := range testcases {
+ a, err := ldap.ParseDN(tc.A)
+ if err != nil {
+ t.Errorf("%d: %v", i, err)
+ continue
+ }
+ b, err := ldap.ParseDN(tc.B)
+ if err != nil {
+ t.Errorf("%d: %v", i, err)
+ continue
+ }
+ if expected, actual := tc.Ancestor, a.AncestorOf(b); expected != actual {
+ t.Errorf("%d: when comparing '%s' and '%s' expected %v, got %v", i, tc.A, tc.B, expected, actual)
+ continue
+ }
+ }
+}
diff --git a/vendor/github.com/go-ldap/ldap/ldap.go b/vendor/github.com/go-ldap/ldap/ldap.go
index 90018be83..d27e639d0 100644
--- a/vendor/github.com/go-ldap/ldap/ldap.go
+++ b/vendor/github.com/go-ldap/ldap/ldap.go
@@ -153,16 +153,47 @@ func addLDAPDescriptions(packet *ber.Packet) (err error) {
func addControlDescriptions(packet *ber.Packet) {
packet.Description = "Controls"
for _, child := range packet.Children {
+ var value *ber.Packet
+ controlType := ""
child.Description = "Control"
- child.Children[0].Description = "Control Type (" + ControlTypeMap[child.Children[0].Value.(string)] + ")"
- value := child.Children[1]
- if len(child.Children) == 3 {
+ switch len(child.Children) {
+ case 0:
+ // at least one child is required for control type
+ continue
+
+ case 1:
+ // just type, no criticality or value
+ controlType = child.Children[0].Value.(string)
+ child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
+
+ case 2:
+ controlType = child.Children[0].Value.(string)
+ child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
+ // Children[1] could be criticality or value (both are optional)
+ // duck-type on whether this is a boolean
+ if _, ok := child.Children[1].Value.(bool); ok {
+ child.Children[1].Description = "Criticality"
+ } else {
+ child.Children[1].Description = "Control Value"
+ value = child.Children[1]
+ }
+
+ case 3:
+ // criticality and value present
+ controlType = child.Children[0].Value.(string)
+ child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
child.Children[1].Description = "Criticality"
+ child.Children[2].Description = "Control Value"
value = child.Children[2]
- }
- value.Description = "Control Value"
- switch child.Children[0].Value.(string) {
+ default:
+ // more than 3 children is invalid
+ continue
+ }
+ if value == nil {
+ continue
+ }
+ switch controlType {
case ControlTypePaging:
value.Description += " (Paging)"
if value.Value != nil {
@@ -188,18 +219,18 @@ func addControlDescriptions(packet *ber.Packet) {
for _, child := range sequence.Children {
if child.Tag == 0 {
//Warning
- child := child.Children[0]
- packet := ber.DecodePacket(child.Data.Bytes())
+ warningPacket := child.Children[0]
+ packet := ber.DecodePacket(warningPacket.Data.Bytes())
val, ok := packet.Value.(int64)
if ok {
- if child.Tag == 0 {
+ if warningPacket.Tag == 0 {
//timeBeforeExpiration
value.Description += " (TimeBeforeExpiration)"
- child.Value = val
- } else if child.Tag == 1 {
+ warningPacket.Value = val
+ } else if warningPacket.Tag == 1 {
//graceAuthNsRemaining
value.Description += " (GraceAuthNsRemaining)"
- child.Value = val
+ warningPacket.Value = val
}
}
} else if child.Tag == 1 {