node.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. // Copyright (c) 2021 Tulir Asokan
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. // Package binary implements encoding and decoding documents in WhatsApp's binary XML format.
  7. package binary
  8. import (
  9. "encoding/json"
  10. "fmt"
  11. "git.bobomao.top/joey/testwh/types"
  12. )
  13. // Attrs is a type alias for the attributes of an XML element (Node).
  14. type Attrs = map[string]any
  15. // Node represents an XML element.
  16. type Node struct {
  17. Tag string // The tag of the element.
  18. Attrs Attrs // The attributes of the element.
  19. Content interface{} // The content inside the element. Can be nil, a list of Nodes, or a byte array.
  20. }
  21. type marshalableNode struct {
  22. Tag string
  23. Attrs Attrs
  24. Content json.RawMessage
  25. }
  26. func (n *Node) UnmarshalJSON(data []byte) error {
  27. var mn marshalableNode
  28. err := json.Unmarshal(data, &mn)
  29. if err != nil {
  30. return err
  31. }
  32. for key, val := range mn.Attrs {
  33. switch typedVal := val.(type) {
  34. case string:
  35. parsed, err := types.ParseJID(typedVal)
  36. if err == nil && parsed.Server == types.DefaultUserServer || parsed.Server == types.NewsletterServer || parsed.Server == types.GroupServer || parsed.Server == types.BroadcastServer {
  37. mn.Attrs[key] = parsed
  38. }
  39. case float64:
  40. mn.Attrs[key] = int64(typedVal)
  41. }
  42. }
  43. n.Tag = mn.Tag
  44. n.Attrs = mn.Attrs
  45. if len(mn.Content) > 0 {
  46. if mn.Content[0] == '[' {
  47. var nodes []Node
  48. err = json.Unmarshal(mn.Content, &nodes)
  49. if err != nil {
  50. return err
  51. }
  52. n.Content = nodes
  53. } else if mn.Content[0] == '"' {
  54. var binaryContent []byte
  55. err = json.Unmarshal(mn.Content, &binaryContent)
  56. if err != nil {
  57. return err
  58. }
  59. n.Content = binaryContent
  60. } else {
  61. return fmt.Errorf("node content must be an array of nodes or a base64 string")
  62. }
  63. }
  64. return nil
  65. }
  66. // GetChildren returns the Content of the node as a list of nodes. If the content is not a list of nodes, this returns nil.
  67. func (n *Node) GetChildren() []Node {
  68. if n.Content == nil {
  69. return nil
  70. }
  71. children, ok := n.Content.([]Node)
  72. if !ok {
  73. return nil
  74. }
  75. return children
  76. }
  77. // GetChildrenByTag returns the same list as GetChildren, but filters it by tag first.
  78. func (n *Node) GetChildrenByTag(tag string) (children []Node) {
  79. for _, node := range n.GetChildren() {
  80. if node.Tag == tag {
  81. children = append(children, node)
  82. }
  83. }
  84. return
  85. }
  86. // GetOptionalChildByTag finds the first child with the given tag and returns it.
  87. // Each provided tag will recurse in, so this is useful for getting a specific nested element.
  88. func (n *Node) GetOptionalChildByTag(tags ...string) (val Node, ok bool) {
  89. val = *n
  90. Outer:
  91. for _, tag := range tags {
  92. for _, child := range val.GetChildren() {
  93. if child.Tag == tag {
  94. val = child
  95. continue Outer
  96. }
  97. }
  98. // If no matching children are found, return false
  99. return
  100. }
  101. // All iterations of loop found a matching child, return it
  102. ok = true
  103. return
  104. }
  105. // GetChildByTag does the same thing as GetOptionalChildByTag, but returns the Node directly without the ok boolean.
  106. func (n *Node) GetChildByTag(tags ...string) Node {
  107. node, _ := n.GetOptionalChildByTag(tags...)
  108. return node
  109. }
  110. // Marshal encodes an XML element (Node) into WhatsApp's binary XML representation.
  111. func Marshal(n Node) ([]byte, error) {
  112. w := newEncoder()
  113. w.writeNode(n)
  114. return w.getData(), nil
  115. }
  116. // Unmarshal decodes WhatsApp's binary XML representation into a Node.
  117. func Unmarshal(data []byte) (*Node, error) {
  118. r := newDecoder(data)
  119. n, err := r.readNode()
  120. if err != nil {
  121. return nil, err
  122. } else if r.index != len(r.data) {
  123. return n, fmt.Errorf("%d leftover bytes after decoding", len(r.data)-r.index)
  124. }
  125. return n, nil
  126. }