diff --git a/cyclonedx.go b/cyclonedx.go
index 6c2b0b1..0bb3ee3 100644
--- a/cyclonedx.go
+++ b/cyclonedx.go
@@ -20,7 +20,9 @@ package cyclonedx
import (
"encoding/json"
"encoding/xml"
+ "errors"
"fmt"
+ "io"
)
const (
@@ -109,7 +111,7 @@ type Component struct {
Description string `json:"description,omitempty" xml:"description,omitempty"`
Scope Scope `json:"scope,omitempty" xml:"scope,omitempty"`
Hashes *[]Hash `json:"hashes,omitempty" xml:"hashes>hash,omitempty"`
- Licenses *[]LicenseChoice `json:"licenses,omitempty" xml:"licenses>license,omitempty"`
+ Licenses *Licenses `json:"licenses,omitempty" xml:"licenses,omitempty"`
Copyright string `json:"copyright,omitempty" xml:"copyright,omitempty"`
CPE string `json:"cpe,omitempty" xml:"cpe,omitempty"`
PackageURL string `json:"purl,omitempty" xml:"purl,omitempty"`
@@ -267,53 +269,75 @@ type License struct {
URL string `json:"url,omitempty" xml:"url,omitempty"`
}
-type LicenseChoice struct {
- License *License `json:"license,omitempty" xml:"-"`
- Expression string `json:"expression,omitempty" xml:"-"`
-}
+type Licenses []LicenseChoice
-func (l LicenseChoice) MarshalXML(e *xml.Encoder, _ xml.StartElement) error {
- if l.License != nil && l.Expression != "" {
- return fmt.Errorf("either license or expression must be set, but not both")
+func (l Licenses) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+ if len(l) == 0 {
+ return nil
}
- if l.License != nil {
- return e.EncodeElement(l.License, xml.StartElement{Name: xml.Name{Local: "license"}})
- } else if l.Expression != "" {
- expressionElement := xml.StartElement{Name: xml.Name{Local: "expression"}}
- if err := e.EncodeToken(expressionElement); err != nil {
- return err
+ if err := e.EncodeToken(start); err != nil {
+ return err
+ }
+
+ for _, choice := range l {
+ if choice.License != nil && choice.Expression != "" {
+ return fmt.Errorf("either license or expression must be set, but not both")
}
- if err := e.EncodeToken(xml.CharData(l.Expression)); err != nil {
- return err
+
+ if choice.License != nil {
+ if err := e.EncodeElement(choice.License, xml.StartElement{Name: xml.Name{Local: "license"}}); err != nil {
+ return err
+ }
+ } else if choice.Expression != "" {
+ if err := e.EncodeElement(choice.Expression, xml.StartElement{Name: xml.Name{Local: "expression"}}); err != nil {
+ return err
+ }
}
- return e.EncodeToken(xml.EndElement{Name: expressionElement.Name})
}
- // Neither license nor expression set - don't write anything
- return nil
+ return e.EncodeToken(start.End())
}
-func (l *LicenseChoice) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
- if start.Name.Local == "license" {
- license := new(License)
- if err := d.DecodeElement(license, &start); err != nil {
+func (l *Licenses) UnmarshalXML(d *xml.Decoder, _ xml.StartElement) error {
+ licenses := make([]LicenseChoice, 0)
+
+ for {
+ token, err := d.Token()
+ if err != nil {
+ if errors.Is(err, io.EOF) {
+ break
+ }
return err
}
- l.License = license
- l.Expression = ""
- return nil
- } else if start.Name.Local == "expression" {
- expression := new(string)
- if err := d.DecodeElement(expression, &start); err != nil {
- return err
+
+ switch tokenType := token.(type) {
+ case xml.StartElement:
+ if tokenType.Name.Local == "expression" {
+ var expression string
+ if err = d.DecodeElement(&expression, &tokenType); err != nil {
+ return err
+ }
+ licenses = append(licenses, LicenseChoice{Expression: expression})
+ } else if tokenType.Name.Local == "license" {
+ var license License
+ if err = d.DecodeElement(&license, &tokenType); err != nil {
+ return err
+ }
+ licenses = append(licenses, LicenseChoice{License: &license})
+ } else {
+ return fmt.Errorf("unknown element: %s", tokenType.Name.Local)
+ }
}
- l.License = nil
- l.Expression = *expression
- return nil
}
- return xml.UnmarshalError(fmt.Sprintf("cannot unmarshal element %#v", start))
+ *l = licenses
+ return nil
+}
+
+type LicenseChoice struct {
+ License *License `json:"license,omitempty" xml:"-"`
+ Expression string `json:"expression,omitempty" xml:"-"`
}
type Metadata struct {
@@ -380,7 +404,7 @@ type Service struct {
Authenticated *bool `json:"authenticated,omitempty" xml:"authenticated,omitempty"`
CrossesTrustBoundary *bool `json:"x-trust-boundary,omitempty" xml:"x-trust-boundary,omitempty"`
Data *[]DataClassification `json:"data,omitempty" xml:"data>classification,omitempty"`
- Licenses *[]LicenseChoice `json:"licenses,omitempty" xml:"licenses>license,omitempty"`
+ Licenses *Licenses `json:"licenses,omitempty" xml:"licenses,omitempty"`
ExternalReferences *[]ExternalReference `json:"externalReferences,omitempty" xml:"externalReferences>reference,omitempty"`
Services *[]Service `json:"services,omitempty" xml:"services>service,omitempty"`
}
diff --git a/cyclonedx_test.go b/cyclonedx_test.go
index 1d80004..dfb4010 100644
--- a/cyclonedx_test.go
+++ b/cyclonedx_test.go
@@ -82,99 +82,83 @@ func TestDependency_UnmarshalJSON(t *testing.T) {
assert.Equal(t, "transitiveDependencyRef", (*dependency.Dependencies)[0].Ref)
}
-func TestLicenseChoice_MarshalJSON(t *testing.T) {
- // Marshal license
- choice := LicenseChoice{
- License: &License{
- ID: "licenseID",
- Name: "licenseName",
- URL: "licenseURL",
+func TestLicenses_MarshalXML(t *testing.T) {
+ // Marshal license and expressions
+ licenses := Licenses{
+ LicenseChoice{
+ Expression: "expressionValue1",
},
- }
- jsonBytes, err := json.Marshal(choice)
- assert.NoError(t, err)
- assert.Equal(t, "{\"license\":{\"id\":\"licenseID\",\"name\":\"licenseName\",\"url\":\"licenseURL\"}}", string(jsonBytes))
-
- // Marshal expression
- choice = LicenseChoice{
- Expression: "expressionValue",
- }
- jsonBytes, err = json.Marshal(choice)
- assert.NoError(t, err)
- assert.Equal(t, "{\"expression\":\"expressionValue\"}", string(jsonBytes))
-}
-
-func TestLicenseChoice_MarshalXML(t *testing.T) {
- // Marshal license
- choice := LicenseChoice{
- License: &License{
- ID: "licenseID",
- Name: "licenseName",
- URL: "licenseURL",
+ LicenseChoice{
+ License: &License{
+ ID: "licenseID",
+ URL: "licenseURL",
+ },
+ },
+ LicenseChoice{
+ Expression: "expressionValue2",
},
}
- xmlBytes, err := xml.Marshal(choice)
- assert.NoError(t, err)
- assert.Equal(t, "licenseIDlicenseNamelicenseURL", string(xmlBytes))
-
- // Marshal expression
- choice = LicenseChoice{
- Expression: "expressionValue",
- }
- xmlBytes, err = xml.Marshal(choice)
+ xmlBytes, err := xml.MarshalIndent(licenses, "", " ")
assert.NoError(t, err)
- assert.Equal(t, "expressionValue", string(xmlBytes))
-
- // Should return error when both license and expression are set
- choice = LicenseChoice{
- License: &License{
- ID: "licenseID",
+ assert.Equal(t, `
+ expressionValue1
+
+ licenseID
+ licenseURL
+
+ expressionValue2
+`, string(xmlBytes))
+
+ // Should return error when both license and expression are set on an element
+ licenses = Licenses{
+ LicenseChoice{
+ License: &License{
+ ID: "licenseID",
+ },
+ Expression: "expressionValue",
},
- Expression: "expressionValue",
}
- _, err = xml.Marshal(choice)
+ _, err = xml.Marshal(licenses)
assert.Error(t, err)
- // Should encode nothing when neither license nor expression are set
- choice = LicenseChoice{}
- xmlBytes, err = xml.Marshal(choice)
+ // Should encode nothing when empty
+ licenses = Licenses{}
+ xmlBytes, err = xml.Marshal(licenses)
assert.NoError(t, err)
assert.Nil(t, xmlBytes)
}
-func TestLicenseChoice_UnmarshalJSON(t *testing.T) {
- // Unmarshal license
- choice := new(LicenseChoice)
- err := json.Unmarshal([]byte("{\"license\":{\"id\":\"licenseID\",\"name\":\"licenseName\",\"url\":\"licenseURL\"}}"), choice)
- assert.NoError(t, err)
- assert.NotNil(t, choice.License)
- assert.Equal(t, "", choice.Expression)
-
- // Unmarshal expression
- choice = new(LicenseChoice)
- err = json.Unmarshal([]byte("{\"expression\":\"expressionValue\"}"), choice)
+func TestLicenses_UnmarshalXML(t *testing.T) {
+ // Unmarshal license and expressions
+ licenses := new(Licenses)
+ err := xml.Unmarshal([]byte(`
+
+ expressionValue1
+
+ licenseID
+ licenseURL
+
+ expressionValue2
+`), licenses)
assert.NoError(t, err)
- assert.Nil(t, choice.License)
- assert.Equal(t, "expressionValue", choice.Expression)
-}
-
-func TestLicenseChoice_UnmarshalXML(t *testing.T) {
- // Unmarshal license
- choice := new(LicenseChoice)
- err := xml.Unmarshal([]byte("licenseIDlicenseNamelicenseURL"), choice)
- assert.NoError(t, err)
- assert.NotNil(t, choice.License)
- assert.Equal(t, "", choice.Expression)
-
- // Unmarshal expression
- choice = new(LicenseChoice)
- err = xml.Unmarshal([]byte("expressionValue"), choice)
+ assert.Len(t, *licenses, 3)
+ assert.Nil(t, (*licenses)[0].License)
+ assert.Equal(t, "expressionValue1", (*licenses)[0].Expression)
+ assert.NotNil(t, (*licenses)[1].License)
+ assert.Equal(t, "licenseID", (*licenses)[1].License.ID)
+ assert.Equal(t, "licenseURL", (*licenses)[1].License.URL)
+ assert.Empty(t, (*licenses)[1].Expression)
+ assert.Nil(t, (*licenses)[2].License)
+ assert.Equal(t, "expressionValue2", (*licenses)[2].Expression)
+
+ // Unmarshal empty licenses
+ licenses = new(Licenses)
+ err = xml.Unmarshal([]byte(""), licenses)
assert.NoError(t, err)
- assert.Nil(t, choice.License)
- assert.Equal(t, "expressionValue", choice.Expression)
+ assert.Empty(t, *licenses)
- // Should return error when input is neither license nor expression
- choice = new(LicenseChoice)
- err = xml.Unmarshal([]byte("expressionValue"), choice)
+ // Should return error when an element is neither license nor expression
+ licenses = new(Licenses)
+ err = xml.Unmarshal([]byte("expressionValue"), licenses)
assert.Error(t, err)
}
diff --git a/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-bom-1.2.xml b/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-bom-1.2.xml
index 8d974e5..a2ea590 100644
--- a/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-bom-1.2.xml
+++ b/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-bom-1.2.xml
@@ -131,6 +131,9 @@
708f1f53b41f11f02d12a11b1a38d2905d47b099afc71a0f1124ef8582ec7313
387b7ae16b9cae45f830671541539bf544202faae5aac544a93b7b0a04f5f846fa2f4e81ef3f1677e13aed7496408a441f5657ab6d54423e56bf6f38da124aef
+
+ EPL-2.0 OR GPL-2.0-with-classpath-exception
+
Copyright Example Inc. All rights reserved.
cpe:/a:example:myapplication:1.0.0
pkg:maven/com.example/myapplication@1.0.0?packaging=war
diff --git a/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-license-expression-1.2.xml b/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-license-expression-1.2.xml
index 590d0ff..aee8304 100644
--- a/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-license-expression-1.2.xml
+++ b/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-license-expression-1.2.xml
@@ -14,6 +14,9 @@
f498a8ff2dd007e29c2074f5e4b01a9a01775c3ff3aeaf6906ea503bc5791b7b
e8f33e424f3f4ed6db76a482fde1a5298970e442c531729119e37991884bdffab4f9426b7ee11fccd074eeda0634d71697d6f88a460dce0ac8d627a29f7d1282
+
+ EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
pkg:maven/com.acme/tomcat-catalina@9.0.14?packaging=jar
diff --git a/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-random-attributes-1.2.xml b/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-random-attributes-1.2.xml
index 79e4338..ff737b9 100644
--- a/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-random-attributes-1.2.xml
+++ b/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-random-attributes-1.2.xml
@@ -69,6 +69,9 @@
708f1f53b41f11f02d12a11b1a38d2905d47b099afc71a0f1124ef8582ec7313
387b7ae16b9cae45f830671541539bf544202faae5aac544a93b7b0a04f5f846fa2f4e81ef3f1677e13aed7496408a441f5657ab6d54423e56bf6f38da124aef
+
+ EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
Copyright Example Inc. All rights reserved.
cpe:/a:example:myapplication:1.0.0
pkg:maven/com.example/myapplication@1.0.0?packaging=war
diff --git a/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-xml-signature-1.2.xml b/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-xml-signature-1.2.xml
deleted file mode 100644
index 2b9b2a2..0000000
--- a/testdata/snapshots/cyclonedx-go-TestRoundTripXML-valid-xml-signature-1.2.xml
+++ /dev/null
@@ -1,107 +0,0 @@
-
-
-
-
- Acme Inc
- com.acme
- tomcat-catalina
- 9.0.14
-
- 3942447fac867ae5cdb3229b658f4d48
- e6b1000b94e835ffd37f4c6dcbdad43f4b48a02a
- f498a8ff2dd007e29c2074f5e4b01a9a01775c3ff3aeaf6906ea503bc5791b7b
- e8f33e424f3f4ed6db76a482fde1a5298970e442c531729119e37991884bdffab4f9426b7ee11fccd074eeda0634d71697d6f88a460dce0ac8d627a29f7d1282
-
-
-
- Apache-2.0
-
-
- pkg:maven/com.acme/tomcat-catalina@9.0.14?packaging=jar
-
-
-
- Apache
- org.apache.tomcat
- tomcat-catalina
- 9.0.14
-
-
- Apache-2.0
-
-
- pkg:maven/org.apache.tomcat/tomcat-catalina@9.0.14?packaging=jar
-
-
-
-
- 7638417db6d59f3c431d3e1f261cc637155684cd
- https://location/to/7638417db6d59f3c431d3e1f261cc637155684cd
-
- 2018-11-07T22:01:45Z
- John Doe
- jdoe@example.com
-
-
- 2018-11-07T22:01:45Z
- John Doe
- jdoe@example.com
-
- Initial commit
-
-
-
-
-
- org.example
- mylibrary
- 1.0.0
- required
-
- 2342c2eaf1feb9a80195dbaddf2ebaa3
- 68b78babe00a053f9e35ec6a2d9080f5b90122b0
- 708f1f53b41f11f02d12a11b1a38d2905d47b099afc71a0f1124ef8582ec7313
- 387b7ae16b9cae45f830671541539bf544202faae5aac544a93b7b0a04f5f846fa2f4e81ef3f1677e13aed7496408a441f5657ab6d54423e56bf6f38da124aef
-
-
-
- Apache-2.0
- blah
- fdaf
-
-
- Copyright Example Inc. All rights reserved.
- cpe:/a:example:myapplication:1.0.0
- pkg:maven/com.example/myapplication@1.0.0?packaging=war
- false
-
-
- com.example
- myframework
- 1.0.0
- Example Inc, enterprise framework
- required
-
- cfcb0b64aacd2f81c1cd546543de965a
- 7fbeef2346c45d565c3341f037bce4e088af8a52
- 0384db3cec55d86a6898c489fdb75a8e75fe66b26639634983d2f3c3558493d1
- 854909cdb9e3ca183056837144aab6d8069b377bd66445087cc7157bf0c3f620418705dd0b83bdc2f73a508c2bdb316ca1809d75ee6972d02023a3e7dd655c79
-
-
-
- Apache-2.0
-
-
- pkg:maven/com.example/myframework@1.0.0?packaging=war
- false
-
-
- http://example.com/myframework
-
-
- http://example.com/security
-
-
-
-
-
diff --git a/testdata/valid-xml-signature-1.2.xml b/testdata/valid-xml-signature-1.2.xml
deleted file mode 100644
index 9605144..0000000
--- a/testdata/valid-xml-signature-1.2.xml
+++ /dev/null
@@ -1,177 +0,0 @@
-
-
-
-
- Acme Inc
- com.acme
- tomcat-catalina
- 9.0.14
-
- 3942447fac867ae5cdb3229b658f4d48
- e6b1000b94e835ffd37f4c6dcbdad43f4b48a02a
- f498a8ff2dd007e29c2074f5e4b01a9a01775c3ff3aeaf6906ea503bc5791b7b
- e8f33e424f3f4ed6db76a482fde1a5298970e442c531729119e37991884bdffab4f9426b7ee11fccd074eeda0634d71697d6f88a460dce0ac8d627a29f7d1282
-
-
-
- Apache-2.0
-
-
- pkg:maven/com.acme/tomcat-catalina@9.0.14?packaging=jar
-
-
-
- Apache
- org.apache.tomcat
- tomcat-catalina
- 9.0.14
-
-
- Apache-2.0
-
-
- pkg:maven/org.apache.tomcat/tomcat-catalina@9.0.14?packaging=jar
-
-
-
-
-
- 7638417db6d59f3c431d3e1f261cc637155684cd
- https://location/to/7638417db6d59f3c431d3e1f261cc637155684cd
-
- 2018-11-07T22:01:45Z
- John Doe
- jdoe@example.com
-
-
- 2018-11-07T22:01:45Z
- John Doe
- jdoe@example.com
-
- Initial commit
-
-
-
-
-
- org.example
- mylibrary
- 1.0.0
- required
-
- 2342c2eaf1feb9a80195dbaddf2ebaa3
- 68b78babe00a053f9e35ec6a2d9080f5b90122b0
- 708f1f53b41f11f02d12a11b1a38d2905d47b099afc71a0f1124ef8582ec7313
- 387b7ae16b9cae45f830671541539bf544202faae5aac544a93b7b0a04f5f846fa2f4e81ef3f1677e13aed7496408a441f5657ab6d54423e56bf6f38da124aef
-
-
-
- Apache-2.0
- blah
- fdaf
-
-
- Copyright Example Inc. All rights reserved.
- cpe:/a:example:myapplication:1.0.0
- pkg:maven/com.example/myapplication@1.0.0?packaging=war
- false
-
-
- com.example
- myframework
- 1.0.0
- Example Inc, enterprise framework
- required
-
- cfcb0b64aacd2f81c1cd546543de965a
- 7fbeef2346c45d565c3341f037bce4e088af8a52
- 0384db3cec55d86a6898c489fdb75a8e75fe66b26639634983d2f3c3558493d1
- 854909cdb9e3ca183056837144aab6d8069b377bd66445087cc7157bf0c3f620418705dd0b83bdc2f73a508c2bdb316ca1809d75ee6972d02023a3e7dd655c79
-
-
-
- Apache-2.0
-
-
- pkg:maven/com.example/myframework@1.0.0?packaging=war
- false
-
-
- http://example.com/myframework
-
-
- http://example.com/security
-
-
-
-
-
-
-
-
-
-
-
-
-
- PrB8/rofGs34XwIX5OIdYSjV2aKSe5VaztJKBvsgjIk=
-
-
-
- ePGNg30Zl9CW7RZdcRn8gFCp1AlWncjudA9pQDXyqZOvyj9RC2YtkI688WdfDOdVRZs6mflJFXr7
- IKA9wY6jVrEqZmlef55Qp/8iGwOjOjWbwYsm2AhrdkUi9gaFSWEd8uITYHOpWbiPFSsnimiK9+ft
- 56dkg/oJMLdXzlaukzq9iGkRcafRkW433OQcZIXwD2K8lg4cdD0pNNNqBa+PgIvzbxA5H84TyQDB
- HBcQiw/j1edRBJgPOwlqzZDUawOJaFhAPUQ+GGKMetIJH2FqqrHXGuV1NIwnbWTCg40RdOcBdCrl
- PDtDVjFh34uZ4dYBpJBIlM4daD2N4B6WPB5iHRyuZTczF2q03ObabuTgkpK6EeadFVqFNsEOOPPt
- MDDyda+Lwff5KjvUHvRRtUDIOm2rNIQKzaseulwYcA9UWQHAFcupJmWcLLM4zzY7F/uOdZuSurzh
- U6h5kdb76Juepof6ee4Q5YpwNOGNL5JfB4C3sc/Dbbv8dZ8OuXFYSZN7reUGZzCNksByqERPEbAe
- n1ldJu1HnRXRQpwaon8Asy9CuNmPfFCfDwOs2B4p4tb+tLNIKFHdRlpd19Zr9vCMCbltXeqq0Cpq
- OejSyLYGqSWzzzUh449dJrg6KTevrTNEln5GAlLBFSdjM5JA7KV2u/GyDVFwSEW7UKooGN4CtgU=
-
-
-
- CN=bomsigner,OU=development,O=cyclonedx
-
- MIIE+DCCAuCgAwIBAgIEXGzayTANBgkqhkiG9w0BAQsFADA+MRIwEAYDVQQKDAljeWNsb25lZHgx
- FDASBgNVBAsMC2RldmVsb3BtZW50MRIwEAYDVQQDDAlib21zaWduZXIwHhcNMTkwMjIwMDQ0MjQ5
- WhcNNDkwMjIwMDQ0MjQ5WjA+MRIwEAYDVQQKDAljeWNsb25lZHgxFDASBgNVBAsMC2RldmVsb3Bt
- ZW50MRIwEAYDVQQDDAlib21zaWduZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCo
- 5JZsM4ZLfWW/dpRlU6CpnItWspddF+bEVDETKVwVj9tGpqR5jURgKS/BOQP2TGUsR3/ZJJBhYRll
- ONhrUQrVKV/I6wp3Z40qPEa1RJLE+QlG9iL8qBV52CnXkLmnUSax3dspSzmSct5vDiTnvpHG9jr0
- AKFeTjy7U9rv8GJybz0ijwlpBoO9JRdYPX2PrrzoSeJLoxKq+GwuyCZ5LhXRN0p1a+NAirTAmY+c
- G1ZTLkMmfeCUy1t6H/bG4RnYOSSPOvk7Rb68lQpUqb+pbbNuB2o/b9cDwtLLCtGVlu+5Wj8mrytY
- 3FGFQM20j3yVeRInmGqTTDBelQa/CO4JKqBlmaeYEIvNYbFs9+AlqadivwDO51RpdPo9fPSpsBpy
- ZMv6S2bXNuUML+Rk99WyKJTPM0PTZhRLZ64ZXEhlz3kQWVoSlrcwwim6sj6LRUb5IRqA3lxRFUI6
- NXKyiQLamQp+t3/9OGW9L1rLCcw7yFo0s8LhMTPMiv4ol9/hQViT+8ICzDsr0OM9ZiF4/UagFRlt
- IClV70cjh1DpsZjzQIRVGaj8uQ/JdtfRz4E43Ki7U0a2Vpho/t6poLVndv46tkX5nYGtMW4WfMoD
- ZflQ9pajvvKtr2jB1wob6nsU+VTmAcWZy4BCPH+XyfDw/0SFBdUceJJJtPWIeYFDUY7onptf+wID
- AQABMA0GCSqGSIb3DQEBCwUAA4ICAQCOVariNgK+9OF/5T9ZaSvZbkk45RTmzgQNXtFc5xfRvqwP
- s+pu/DFXm1R+ltjyS5j3w6NBZUFUI5MqLQr6JEEDrbu8BvfBO57wJNAEATj1JIHEfDfh7BxnBF8f
- oYFOwbrh4jOt0wz0FW2obsSVmF4GSvS7tTlWqTcsxjdZVmwP40RWu18B9jzv7M61adrWD3ksDA5O
- amSOsZi3Nt0aacDkyGRdCIEFi0fplxQInXMtD1z3RhXu2JSTAIr54Cei49Bh71kAXSWHMCog/f8a
- lSrZyqZBty/ACfU9DqlPIM+giHePKm4z2bcdpUdKZk6wcKDn4CvuBOqsMBMg7L05UEyyqTPD/4dk
- 2GwJ8Nv0E5gsYHCIXF2cZ3OUVsw0mB/ozleEJVDE02uZZN/1wW1Xq028LsMdgN0Wk1WvWyF5MEdh
- nPWuhqp6tNaDI/kK6XQF+LjYJUzua3AQFOHfYNLKhO6d+bJ4rr0833v4v3cLW34kbXkKb6U3Yv8X
- SK3jBGCACiPgnc0N6awkh1kDlrZQ7GMsl14c+2+vpl9Lf0sL0mRUIyICfSC8MjlsP/BZH3emyfsk
- iWivPALomycKqP+PSkt1WaWApGENZWk1wNN99FYSYlt6LViW2p6T97fRx4jPRlHu+wecfD2k9RP4
- bt5W2HWfOP0zNAS7SnAVLEl2QZxXKw==
-
-
-
-
-
- qOSWbDOGS31lv3aUZVOgqZyLVrKXXRfmxFQxEylcFY/bRqakeY1EYCkvwTkD9kxlLEd/2SSQYWEZ
- ZTjYa1EK1SlfyOsKd2eNKjxGtUSSxPkJRvYi/KgVedgp15C5p1Emsd3bKUs5knLebw4k576RxvY6
- 9AChXk48u1Pa7/Bicm89Io8JaQaDvSUXWD19j6686EniS6MSqvhsLsgmeS4V0TdKdWvjQIq0wJmP
- nBtWUy5DJn3glMtbeh/2xuEZ2Dkkjzr5O0W+vJUKVKm/qW2zbgdqP2/XA8LSywrRlZbvuVo/Jq8r
- WNxRhUDNtI98lXkSJ5hqk0wwXpUGvwjuCSqgZZmnmBCLzWGxbPfgJamnYr8AzudUaXT6PXz0qbAa
- cmTL+ktm1zblDC/kZPfVsiiUzzND02YUS2euGVxIZc95EFlaEpa3MMIpurI+i0VG+SEagN5cURVC
- OjVysokC2pkKfrd//ThlvS9aywnMO8haNLPC4TEzzIr+KJff4UFYk/vCAsw7K9DjPWYheP1GoBUZ
- bSApVe9HI4dQ6bGY80CEVRmo/LkPyXbX0c+BONyou1NGtlaYaP7eqaC1Z3b+OrZF+Z2BrTFuFnzK
- A2X5UPaWo77yra9owdcKG+p7FPlU5gHFmcuAQjx/l8nw8P9EhQXVHHiSSbT1iHmBQ1GO6J6bX/s=
-
- AQAB
-
-
-
-