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 -}