scpd.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. package scpd
  2. import (
  3. "encoding/xml"
  4. "strings"
  5. )
  6. const (
  7. SCPDXMLNamespace = "urn:schemas-upnp-org:service-1-0"
  8. )
  9. func cleanWhitespace(s *string) {
  10. *s = strings.TrimSpace(*s)
  11. }
  12. // SCPD is the service description as described by section 2.5 "Service
  13. // description" in
  14. // http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf
  15. type SCPD struct {
  16. XMLName xml.Name `xml:"scpd"`
  17. ConfigId string `xml:"configId,attr"`
  18. SpecVersion SpecVersion `xml:"specVersion"`
  19. Actions []Action `xml:"actionList>action"`
  20. StateVariables []StateVariable `xml:"serviceStateTable>stateVariable"`
  21. }
  22. // Clean attempts to remove stray whitespace etc. in the structure. It seems
  23. // unfortunately common for stray whitespace to be present in SCPD documents,
  24. // this method attempts to make it easy to clean them out.
  25. func (scpd *SCPD) Clean() {
  26. cleanWhitespace(&scpd.ConfigId)
  27. for i := range scpd.Actions {
  28. scpd.Actions[i].clean()
  29. }
  30. for i := range scpd.StateVariables {
  31. scpd.StateVariables[i].clean()
  32. }
  33. }
  34. func (scpd *SCPD) GetStateVariable(variable string) *StateVariable {
  35. for i := range scpd.StateVariables {
  36. v := &scpd.StateVariables[i]
  37. if v.Name == variable {
  38. return v
  39. }
  40. }
  41. return nil
  42. }
  43. func (scpd *SCPD) GetAction(action string) *Action {
  44. for i := range scpd.Actions {
  45. a := &scpd.Actions[i]
  46. if a.Name == action {
  47. return a
  48. }
  49. }
  50. return nil
  51. }
  52. // SpecVersion is part of a SCPD document, describes the version of the
  53. // specification that the data adheres to.
  54. type SpecVersion struct {
  55. Major int32 `xml:"major"`
  56. Minor int32 `xml:"minor"`
  57. }
  58. type Action struct {
  59. Name string `xml:"name"`
  60. Arguments []Argument `xml:"argumentList>argument"`
  61. }
  62. func (action *Action) clean() {
  63. cleanWhitespace(&action.Name)
  64. for i := range action.Arguments {
  65. action.Arguments[i].clean()
  66. }
  67. }
  68. func (action *Action) InputArguments() []*Argument {
  69. var result []*Argument
  70. for i := range action.Arguments {
  71. arg := &action.Arguments[i]
  72. if arg.IsInput() {
  73. result = append(result, arg)
  74. }
  75. }
  76. return result
  77. }
  78. func (action *Action) OutputArguments() []*Argument {
  79. var result []*Argument
  80. for i := range action.Arguments {
  81. arg := &action.Arguments[i]
  82. if arg.IsOutput() {
  83. result = append(result, arg)
  84. }
  85. }
  86. return result
  87. }
  88. type Argument struct {
  89. Name string `xml:"name"`
  90. Direction string `xml:"direction"` // in|out
  91. RelatedStateVariable string `xml:"relatedStateVariable"` // ?
  92. Retval string `xml:"retval"` // ?
  93. }
  94. func (arg *Argument) clean() {
  95. cleanWhitespace(&arg.Name)
  96. cleanWhitespace(&arg.Direction)
  97. cleanWhitespace(&arg.RelatedStateVariable)
  98. cleanWhitespace(&arg.Retval)
  99. }
  100. func (arg *Argument) IsInput() bool {
  101. return arg.Direction == "in"
  102. }
  103. func (arg *Argument) IsOutput() bool {
  104. return arg.Direction == "out"
  105. }
  106. type StateVariable struct {
  107. Name string `xml:"name"`
  108. SendEvents string `xml:"sendEvents,attr"` // yes|no
  109. Multicast string `xml:"multicast,attr"` // yes|no
  110. DataType DataType `xml:"dataType"`
  111. DefaultValue string `xml:"defaultValue"`
  112. AllowedValueRange *AllowedValueRange `xml:"allowedValueRange"`
  113. AllowedValues []string `xml:"allowedValueList>allowedValue"`
  114. }
  115. func (v *StateVariable) clean() {
  116. cleanWhitespace(&v.Name)
  117. cleanWhitespace(&v.SendEvents)
  118. cleanWhitespace(&v.Multicast)
  119. v.DataType.clean()
  120. cleanWhitespace(&v.DefaultValue)
  121. if v.AllowedValueRange != nil {
  122. v.AllowedValueRange.clean()
  123. }
  124. for i := range v.AllowedValues {
  125. cleanWhitespace(&v.AllowedValues[i])
  126. }
  127. }
  128. type AllowedValueRange struct {
  129. Minimum string `xml:"minimum"`
  130. Maximum string `xml:"maximum"`
  131. Step string `xml:"step"`
  132. }
  133. func (r *AllowedValueRange) clean() {
  134. cleanWhitespace(&r.Minimum)
  135. cleanWhitespace(&r.Maximum)
  136. cleanWhitespace(&r.Step)
  137. }
  138. type DataType struct {
  139. Name string `xml:",chardata"`
  140. Type string `xml:"type,attr"`
  141. }
  142. func (dt *DataType) clean() {
  143. cleanWhitespace(&dt.Name)
  144. cleanWhitespace(&dt.Type)
  145. }