diff --git a/pkg/service/marge/marge.go b/pkg/service/marge/marge.go
index 96f0746..7d9421a 100644
--- a/pkg/service/marge/marge.go
+++ b/pkg/service/marge/marge.go
@@ -616,20 +616,40 @@ func recentToXML(r *models.ServiceRecent, matchingSrc *models.ConfiguredSource)
// ProviderSettingsToXML generates provider settings XML for the specified account.
func ProviderSettingsToXML(account string) string {
- return constants.XMLHeader + fmt.Sprintf(`
-
- %s
- ELIGIBLE_FOR_TRIAL
- false
- 14
-
-
- %s
- STREAMING_QUALITY
- 2
- 15
-
- `, EscapeXML(account), EscapeXML(account))
+ type providerSetting struct {
+ BoseID string `xml:"boseId"`
+ KeyName string `xml:"keyName"`
+ Value string `xml:"value"`
+ ProviderID string `xml:"providerId"`
+ }
+ type providerSettings struct {
+ XMLName xml.Name `xml:"providerSettings"`
+ Settings []providerSetting `xml:"providerSetting"`
+ }
+
+ payload := providerSettings{
+ Settings: []providerSetting{
+ {
+ BoseID: account,
+ KeyName: "ELIGIBLE_FOR_TRIAL",
+ Value: "false",
+ ProviderID: "14",
+ },
+ {
+ BoseID: account,
+ KeyName: "STREAMING_QUALITY",
+ Value: "2",
+ ProviderID: "15",
+ },
+ },
+ }
+
+ out, err := xml.Marshal(payload)
+ if err != nil {
+ return constants.XMLHeader + ``
+ }
+
+ return constants.XMLHeader + string(out)
}
// SoftwareUpdateToXML generates software update configuration XML.
diff --git a/pkg/service/testing/fakespeaker/fakespeaker.go b/pkg/service/testing/fakespeaker/fakespeaker.go
index 9944aaf..99af659 100644
--- a/pkg/service/testing/fakespeaker/fakespeaker.go
+++ b/pkg/service/testing/fakespeaker/fakespeaker.go
@@ -12,6 +12,7 @@ package fakespeaker
import (
"context"
"embed"
+ "encoding/xml"
"errors"
"fmt"
"io"
@@ -324,41 +325,48 @@ func handleRemoveGroup(w http.ResponseWriter, r *http.Request) {
// not contain , it falls back to a minimal canned success
// response so callers still see a 200 + parseable XML.
func buildAddGroupResponse(posted []byte) []byte {
- const closeTag = ""
-
- const okFragment = " GROUP_OK\n"
+ type groupPayload struct {
+ XMLName xml.Name `xml:"group"`
+ Name string `xml:"name,omitempty"`
+ Master string `xml:"master,omitempty"`
+ Slave string `xml:"slave,omitempty"`
+ Status string `xml:"status,omitempty"`
+ }
if len(posted) == 0 {
- return []byte(`` + "\n\n" + okFragment + closeTag + "\n")
+ return []byte(`
+
+ GROUP_OK
+
+`)
}
- idx := indexOfClose(posted, closeTag)
- if idx < 0 {
- return []byte(`` + "\n\n" + okFragment + closeTag + "\n")
+ var in groupPayload
+ if err := xml.Unmarshal(posted, &in); err != nil {
+ return []byte(`
+
+ GROUP_OK
+
+`)
}
- out := make([]byte, 0, len(posted)+len(okFragment))
- out = append(out, posted[:idx]...)
- out = append(out, []byte(okFragment)...)
- out = append(out, posted[idx:]...)
+ out := groupPayload{
+ Name: in.Name,
+ Master: in.Master,
+ Slave: in.Slave,
+ Status: "GROUP_OK",
+ }
- return out
+ data, err := xml.Marshal(out)
+ if err != nil {
+ return []byte(`
+
+ GROUP_OK
+
+`)
+ }
+
+ return append([]byte(``+"\n"), data...)
}
-// indexOfClose returns the index of the last occurrence of needle in b,
-// or -1 if not present. We scan from the right because real-world
-// payloads can technically nest blocks (e.g. inside ),
-// even though the documented stereo-pair payload does not.
-func indexOfClose(b []byte, needle string) int {
- if len(needle) == 0 || len(b) < len(needle) {
- return -1
- }
- for i := len(b) - len(needle); i >= 0; i-- {
- if string(b[i:i+len(needle)]) == needle {
- return i
- }
- }
-
- return -1
-}