xtraz.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. package wire
  2. import (
  3. "encoding/xml"
  4. "errors"
  5. "html"
  6. "strconv"
  7. "strings"
  8. )
  9. const (
  10. XtrazFuncInvitation uint16 = 0x0001 // chat invitation
  11. XtrazFuncData uint16 = 0x0002 // greeting cards, custom data
  12. XtrazFuncUserRemove uint16 = 0x0004 // user removal notification
  13. XtrazFuncNotify uint16 = 0x0008 // XStatus notifications
  14. )
  15. const (
  16. XStatusAngry uint8 = 1
  17. XStatusDuck uint8 = 2
  18. XStatusTired uint8 = 3
  19. XStatusParty uint8 = 4
  20. XStatusBeer uint8 = 5
  21. XStatusThinking uint8 = 6
  22. XStatusEating uint8 = 7
  23. XStatusTV uint8 = 8
  24. XStatusFriends uint8 = 9
  25. XStatusCoffee uint8 = 10
  26. XStatusMusic uint8 = 11
  27. XStatusBusiness uint8 = 12
  28. XStatusCamera uint8 = 13
  29. XStatusFunny uint8 = 14
  30. XStatusPhone uint8 = 15
  31. XStatusGames uint8 = 16
  32. XStatusCollege uint8 = 17
  33. XStatusShopping uint8 = 18
  34. XStatusSick uint8 = 19
  35. XStatusSleeping uint8 = 20
  36. XStatusSurfing uint8 = 21
  37. XStatusInternet uint8 = 22
  38. XStatusEngineering uint8 = 23
  39. XStatusTyping uint8 = 24
  40. XStatusPPC uint8 = 25
  41. XStatusMobile uint8 = 26
  42. XStatusLove uint8 = 27
  43. XStatusSearching uint8 = 28
  44. XStatusEvil uint8 = 29
  45. XStatusDepression uint8 = 30
  46. XStatusParty2 uint8 = 31
  47. XStatusCoffee2 uint8 = 32
  48. )
  49. // UnmangleXtrazXML decodes the HTML entity encoded XML used in Xtraz messages.
  50. // Xtraz uses HTML entity encoding for transport: < > & "
  51. func UnmangleXtrazXML(mangled string) string {
  52. return html.UnescapeString(mangled)
  53. }
  54. // MangleXtrazXML encodes XML for Xtraz transport using HTML entities.
  55. func MangleXtrazXML(plain string) string {
  56. return html.EscapeString(plain)
  57. }
  58. // XtrazNotifyRequest represents a parsed Xtraz notification request (<N> type).
  59. type XtrazNotifyRequest struct {
  60. PluginID string
  61. ServiceID string
  62. RequestID string
  63. TransID string // transaction ID
  64. SenderID string // sender's UIN
  65. }
  66. // XtrazNotifyResponse represents a parsed Xtraz notification response (<NR> type).
  67. type XtrazNotifyResponse struct {
  68. UIN string
  69. Index uint8
  70. Title string
  71. Message string
  72. }
  73. // xmlNotifyRequest is the internal XML structure for parsing <N> requests.
  74. type xmlNotifyRequest struct {
  75. XMLName xml.Name `xml:"N"`
  76. Query struct {
  77. PluginID string `xml:"PluginID"`
  78. } `xml:"QUERY"`
  79. Notify struct {
  80. Srv struct {
  81. ID string `xml:"id"`
  82. Req struct {
  83. ID string `xml:"id"`
  84. Trans string `xml:"trans"`
  85. SenderID string `xml:"senderId"`
  86. } `xml:"req"`
  87. } `xml:"srv"`
  88. } `xml:"NOTIFY"`
  89. }
  90. // xmlNotifyResponseRoot is the internal XML structure for the <Root> element in responses.
  91. type xmlNotifyResponseRoot struct {
  92. UIN string `xml:"uin"`
  93. Index uint8 `xml:"index"`
  94. Title string `xml:"title"`
  95. Desc string `xml:"desc"`
  96. }
  97. // ParseXtrazNotifyRequest parses an Xtraz notification request from XML.
  98. // The input is expected to be unmangled.
  99. func ParseXtrazNotifyRequest(xmlData []byte) (*XtrazNotifyRequest, error) {
  100. var req xmlNotifyRequest
  101. if err := xml.Unmarshal(xmlData, &req); err != nil {
  102. return nil, err
  103. }
  104. return &XtrazNotifyRequest{
  105. PluginID: req.Query.PluginID,
  106. ServiceID: req.Notify.Srv.ID,
  107. RequestID: req.Notify.Srv.Req.ID,
  108. TransID: req.Notify.Srv.Req.Trans,
  109. SenderID: req.Notify.Srv.Req.SenderID,
  110. }, nil
  111. }
  112. // ErrXtrazRootNotFound is returned when the Root element is not found in an Xtraz response.
  113. var ErrXtrazRootNotFound = errors.New("xtraz: Root element not found in response")
  114. // ParseXtrazNotifyResponse parses an Xtraz notification response from XML.
  115. // The input should be unmangled.
  116. func ParseXtrazNotifyResponse(xmlData []byte) (*XtrazNotifyResponse, error) {
  117. // The response XML has a nested structure, we need to extract the Root element
  118. xmlStr := string(xmlData)
  119. rootStart := strings.Index(xmlStr, "<Root>")
  120. rootEnd := strings.Index(xmlStr, "</Root>")
  121. if rootStart == -1 || rootEnd == -1 {
  122. return nil, ErrXtrazRootNotFound
  123. }
  124. rootXML := xmlStr[rootStart : rootEnd+len("</Root>")]
  125. var root xmlNotifyResponseRoot
  126. if err := xml.Unmarshal([]byte(rootXML), &root); err != nil {
  127. return nil, err
  128. }
  129. return &XtrazNotifyResponse{
  130. UIN: root.UIN,
  131. Index: root.Index,
  132. Title: root.Title,
  133. Message: root.Desc,
  134. }, nil
  135. }
  136. // BuildXtrazNotifyResponse builds an Xtraz notification response XML string.
  137. func BuildXtrazNotifyResponse(uin string, index uint8, title, message string) string {
  138. xmlStr := `<NR><RES><ret event="OnRemoteNotification"><srv><id></id>` +
  139. `<val srv_id="cAwaySrv"><Root><CASXtraSetAwayMessage></CASXtraSetAwayMessage>` +
  140. `<uin>` + uin + `</uin>` +
  141. `<index>` + strconv.Itoa(int(index)) + `</index>` +
  142. `<title>` + MangleXtrazXML(title) + `</title>` +
  143. `<desc>` + MangleXtrazXML(message) + `</desc>` +
  144. `</Root></val></srv></ret></RES></NR>`
  145. return MangleXtrazXML(xmlStr)
  146. }
  147. // BuildXtrazNotifyRequest builds an Xtraz notification request XML string.
  148. func BuildXtrazNotifyRequest(senderUIN string) string {
  149. xml := `<N><QUERY><PluginID>srvMng</PluginID></QUERY>` +
  150. `<NOTIFY><srv><id>cAwaySrv</id>` +
  151. `<req><id>AwayStat</id><trans>1</trans>` +
  152. `<senderId>` + senderUIN + `</senderId></req></srv></NOTIFY></N>`
  153. return MangleXtrazXML(xml)
  154. }