summaryrefslogtreecommitdiffstats
path: root/vendor/go.mau.fi/whatsmeow/request.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/go.mau.fi/whatsmeow/request.go')
-rw-r--r--vendor/go.mau.fi/whatsmeow/request.go141
1 files changed, 141 insertions, 0 deletions
diff --git a/vendor/go.mau.fi/whatsmeow/request.go b/vendor/go.mau.fi/whatsmeow/request.go
new file mode 100644
index 00000000..62ef3477
--- /dev/null
+++ b/vendor/go.mau.fi/whatsmeow/request.go
@@ -0,0 +1,141 @@
+// Copyright (c) 2021 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package whatsmeow
+
+import (
+ "context"
+ "strconv"
+ "sync/atomic"
+ "time"
+
+ waBinary "go.mau.fi/whatsmeow/binary"
+ "go.mau.fi/whatsmeow/types"
+)
+
+func (cli *Client) generateRequestID() string {
+ return cli.uniqueID + strconv.FormatUint(uint64(atomic.AddUint32(&cli.idCounter, 1)), 10)
+}
+
+var closedNode = &waBinary.Node{Tag: "xmlstreamend"}
+
+func (cli *Client) clearResponseWaiters() {
+ cli.responseWaitersLock.Lock()
+ for _, waiter := range cli.responseWaiters {
+ select {
+ case waiter <- closedNode:
+ default:
+ close(waiter)
+ }
+ }
+ cli.responseWaiters = make(map[string]chan<- *waBinary.Node)
+ cli.responseWaitersLock.Unlock()
+}
+
+func (cli *Client) waitResponse(reqID string) chan *waBinary.Node {
+ ch := make(chan *waBinary.Node, 1)
+ cli.responseWaitersLock.Lock()
+ cli.responseWaiters[reqID] = ch
+ cli.responseWaitersLock.Unlock()
+ return ch
+}
+
+func (cli *Client) cancelResponse(reqID string, ch chan *waBinary.Node) {
+ cli.responseWaitersLock.Lock()
+ close(ch)
+ delete(cli.responseWaiters, reqID)
+ cli.responseWaitersLock.Unlock()
+}
+
+func (cli *Client) receiveResponse(data *waBinary.Node) bool {
+ id, ok := data.Attrs["id"].(string)
+ if !ok || (data.Tag != "iq" && data.Tag != "ack") {
+ return false
+ }
+ cli.responseWaitersLock.Lock()
+ waiter, ok := cli.responseWaiters[id]
+ if !ok {
+ cli.responseWaitersLock.Unlock()
+ return false
+ }
+ delete(cli.responseWaiters, id)
+ cli.responseWaitersLock.Unlock()
+ waiter <- data
+ return true
+}
+
+type infoQueryType string
+
+const (
+ iqSet infoQueryType = "set"
+ iqGet infoQueryType = "get"
+)
+
+type infoQuery struct {
+ Namespace string
+ Type infoQueryType
+ To types.JID
+ ID string
+ Content interface{}
+
+ Timeout time.Duration
+ Context context.Context
+}
+
+func (cli *Client) sendIQAsync(query infoQuery) (<-chan *waBinary.Node, error) {
+ if len(query.ID) == 0 {
+ query.ID = cli.generateRequestID()
+ }
+ waiter := cli.waitResponse(query.ID)
+ attrs := waBinary.Attrs{
+ "id": query.ID,
+ "xmlns": query.Namespace,
+ "type": string(query.Type),
+ }
+ if !query.To.IsEmpty() {
+ attrs["to"] = query.To
+ }
+ err := cli.sendNode(waBinary.Node{
+ Tag: "iq",
+ Attrs: attrs,
+ Content: query.Content,
+ })
+ if err != nil {
+ cli.cancelResponse(query.ID, waiter)
+ return nil, err
+ }
+ return waiter, nil
+}
+
+func (cli *Client) sendIQ(query infoQuery) (*waBinary.Node, error) {
+ resChan, err := cli.sendIQAsync(query)
+ if err != nil {
+ return nil, err
+ }
+ if query.Timeout == 0 {
+ query.Timeout = 1 * time.Minute
+ }
+ if query.Context == nil {
+ query.Context = context.Background()
+ }
+ select {
+ case res := <-resChan:
+ if res == closedNode {
+ return nil, ErrIQDisconnected
+ }
+ resType, _ := res.Attrs["type"].(string)
+ if res.Tag != "iq" || (resType != "result" && resType != "error") {
+ return res, &IQError{RawNode: res}
+ } else if resType == "error" {
+ return res, parseIQError(res)
+ }
+ return res, nil
+ case <-query.Context.Done():
+ return nil, query.Context.Err()
+ case <-time.After(query.Timeout):
+ return nil, ErrIQTimedOut
+ }
+}