Skip to content

Commit

Permalink
Clean up blocklist stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed Sep 9, 2023
1 parent 62aaec3 commit 97dbf9e
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 129 deletions.
5 changes: 0 additions & 5 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,6 @@ type Client struct {
userDevicesCache map[types.JID][]types.JID
userDevicesCacheLock sync.Mutex

blockedContactsCache map[types.JID]bool
blockedContactsCacheLock sync.Mutex

recentMessagesMap map[recentMessageKey]*waProto.Message
recentMessagesList [recentMessagesSize]recentMessageKey
recentMessagesPtr int
Expand Down Expand Up @@ -194,8 +191,6 @@ func NewClient(deviceStore *store.Device, log waLog.Logger) *Client {
groupParticipantsCache: make(map[types.JID][]types.JID),
userDevicesCache: make(map[types.JID][]types.JID),

blockedContactsCache: make(map[types.JID]bool),

recentMessagesMap: make(map[recentMessageKey]*waProto.Message, recentMessagesSize),
sessionRecreateHistory: make(map[types.JID]time.Time),
GetMessageForRetry: func(requester, to types.JID, id types.MessageID) *waProto.Message { return nil },
Expand Down
1 change: 0 additions & 1 deletion connectionevents.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ func (cli *Client) handleConnectSuccess(node *waBinary.Node) {
if err != nil {
cli.Log.Warnf("Failed to send post-connect passive IQ: %v", err)
}
cli.GetAllBlockedContacts()
cli.dispatchEvent(&events.Connected{})
cli.closeSocketWaitChan()
}()
Expand Down
1 change: 0 additions & 1 deletion errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ var (
ErrUnknownServer = errors.New("can't send message to unknown server")
ErrRecipientADJID = errors.New("message recipient must be a user JID with no device part")
ErrServerReturnedError = errors.New("server returned error")
ErrBlockedContact = errors.New("contact is blocked")
)

type DownloadHTTPError struct {
Expand Down
34 changes: 15 additions & 19 deletions mdtest/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -745,47 +745,43 @@ func handleCmd(cmd string, args []string) {
if err != nil {
log.Errorf("Error changing chat's pin state: %v", err)
}
case "listblocked":
blockedContacts, err := cli.GetAllBlockedContacts()
case "getblocklist":
blocklist, err := cli.GetBlocklist()
if err != nil {
log.Errorf("Failed to get blocked contacts list: %v", err)
} else {
for _, blockedContact := range blockedContacts {
log.Infof("%+v", blockedContact)
}
log.Infof("Blocklist: %+v", blocklist)
}
case "block":
if len(args) < 1 {
log.Errorf("Usage: block <jid>")
return
}
target, ok := parseJID(args[0])
jid, ok := parseJID(args[0])
if !ok {
return
}

blockedList, err := cli.BlockContact(target)
resp, err := cli.UpdateBlocklist(jid, events.BlocklistChangeActionBlock)
if err != nil {
log.Errorf("Error blocking contact: %v", err)
log.Errorf("Error updating blocklist: %v", err)
} else {
log.Infof("Blocklist updated: %+v", resp)
}

log.Infof("%+v", blockedList)
case "unblock":
if len(args) < 1 {
log.Errorf("Usage: unblock <jid>")
return
}
target, ok := parseJID(args[0])
jid, ok := parseJID(args[0])
if !ok {
return
}

blockedList, err := cli.UnblockContact(target)
resp, err := cli.UpdateBlocklist(jid, events.BlocklistChangeActionUnblock)
if err != nil {
log.Errorf("Error unblocking contact: %v", err)
log.Errorf("Error updating blocklist: %v", err)
} else {
log.Infof("Blocklist updated: %+v", resp)
}

log.Infof("%+v", blockedList)
}
}

Expand Down Expand Up @@ -917,7 +913,7 @@ func handler(rawEvt interface{}) {
log.Debugf("Keepalive timeout event: %+v", evt)
case *events.KeepAliveRestored:
log.Debugf("Keepalive restored")
case *events.ContactBlockedStatusChange:
log.Infof("ContactBlockedStatusChange event: %+v", evt)
case *events.Blocklist:
log.Infof("Blocklist event: %+v", evt)
}
}
44 changes: 15 additions & 29 deletions notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,40 +165,26 @@ func (cli *Client) handleOwnDevicesNotification(node *waBinary.Node) {
}
}

func (cli *Client) handleContactBlockedStatusChangeNotification(node *waBinary.Node) {
childArray := node.GetChildren()
if len(childArray) == 0 {
ag := node.AttrGetter()
action := ag.String("action")
if !ag.OK() {
cli.Log.Debugf("Ignoring contact blocked status change notification with unexpected attributes: %v", ag.Error())
return
}
if action == "modify" {
cli.GetAllBlockedContacts()
return
}
func (cli *Client) handleBlocklist(node *waBinary.Node) {
ag := node.AttrGetter()
evt := events.Blocklist{
Action: events.BlocklistAction(ag.OptionalString("action")),
DHash: ag.String("dhash"),
PrevDHash: ag.OptionalString("prev_dhash"),
}

cli.blockedContactsCacheLock.Lock()
defer cli.blockedContactsCacheLock.Unlock()
for _, child := range childArray {
for _, child := range node.GetChildren() {
ag := child.AttrGetter()
var evt events.ContactBlockedStatusChange
evt.From = ag.JID("jid")
evt.Blocked = ag.String("action") == "block"
change := events.BlocklistChange{
JID: ag.JID("jid"),
Action: events.BlocklistChangeAction(ag.String("action")),
}
if !ag.OK() {
cli.Log.Debugf("Ignoring contact blocked status change notification with unexpected attributes: %v", ag.Error())
cli.Log.Warnf("Unexpected data in blocklist event child %v: %v", child.XMLString(), ag.Error())
continue
}

if evt.Blocked {
cli.blockedContactsCache[evt.From] = true
} else if _, exists := cli.blockedContactsCache[evt.From]; exists {
delete(cli.blockedContactsCache, evt.From)
}
cli.dispatchEvent(&evt)
evt.Changes = append(evt.Changes, change)
}
cli.dispatchEvent(&evt)
}

func (cli *Client) handleAccountSyncNotification(node *waBinary.Node) {
Expand All @@ -214,7 +200,7 @@ func (cli *Client) handleAccountSyncNotification(node *waBinary.Node) {
JID: cli.getOwnID().ToNonAD(),
})
case "blocklist":
cli.handleContactBlockedStatusChangeNotification(&child)
cli.handleBlocklist(&child)
default:
cli.Log.Debugf("Unhandled account sync item %s", child.Tag)
}
Expand Down
8 changes: 0 additions & 8 deletions send.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,6 @@ func (cli *Client) SendMessage(ctx context.Context, to types.JID, message *waPro
return
}

cli.blockedContactsCacheLock.Lock()
_, isBlockedContact := cli.blockedContactsCache[to]
cli.blockedContactsCacheLock.Unlock()
if isBlockedContact {
err = ErrBlockedContact
return
}

if len(req.ID) == 0 {
req.ID = cli.GenerateMessageID()
}
Expand Down
33 changes: 27 additions & 6 deletions types/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,10 +461,31 @@ type MediaRetry struct {
FromMe bool // Whether the message was sent by the current user or someone else.
}

// ContactBlockedStatusChange is emitted when the contact blocked status change is received.
type ContactBlockedStatusChange struct {
// The user whose blocked state change event this is
From types.JID
// True if the user is blocked
Blocked bool
type BlocklistAction string

const (
BlocklistActionDefault BlocklistAction = ""
BlocklistActionModify BlocklistAction = "modify"
)

// Blocklist is emitted when the user's blocked user list is changed.
type Blocklist struct {
// Action specifies what happened. If it's empty, there should be a list of changes in the Changes list.
// If it's "modify", then the Changes list will be empty and the whole blocklist should be re-requested.
Action BlocklistAction
DHash string
PrevDHash string
Changes []BlocklistChange
}

type BlocklistChangeAction string

const (
BlocklistChangeActionBlock BlocklistChangeAction = "block"
BlocklistChangeActionUnblock BlocklistChangeAction = "unblock"
)

type BlocklistChange struct {
JID types.JID
Action BlocklistChangeAction
}
6 changes: 6 additions & 0 deletions types/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,9 @@ type StatusPrivacy struct {

IsDefault bool
}

// Blocklist contains the user's current list of blocked users.
type Blocklist struct {
DHash string // TODO is this just a timestamp?
JIDs []JID
}
88 changes: 28 additions & 60 deletions user.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,31 +518,25 @@ func (cli *Client) usync(ctx context.Context, jids []types.JID, mode, context st
}
}

// cacheBlockedContacts sync the cache with blocked contacts
func (cli *Client) cacheBlockedContacts(blockedContactsResp *waBinary.Node) []types.JID {
blockedContacts := []types.JID{}
if blockedList, ok := blockedContactsResp.GetOptionalChildByTag("list"); ok {
cli.blockedContactsCacheLock.Lock()
defer cli.blockedContactsCacheLock.Unlock()
cli.blockedContactsCache = make(map[types.JID]bool)
for _, child := range blockedList.GetChildren() {
ag := child.AttrGetter()
blockedJID := ag.JID("jid")
if !ag.OK() {
cli.Log.Debugf("Ignoring contact blocked data with unexpected attributes: %v", ag.Error())
continue
}

cli.blockedContactsCache[blockedJID] = true
blockedContacts = append(blockedContacts, blockedJID)
func (cli *Client) parseBlocklist(node *waBinary.Node) *types.Blocklist {
output := &types.Blocklist{
DHash: node.AttrGetter().String("dhash"),
}
for _, child := range node.GetChildren() {
ag := child.AttrGetter()
blockedJID := ag.JID("jid")
if !ag.OK() {
cli.Log.Debugf("Ignoring contact blocked data with unexpected attributes: %v", ag.Error())
continue
}
}

return blockedContacts
output.JIDs = append(output.JIDs, blockedJID)
}
return output
}

// GetAllBlockedContacts gets all the contacts that was blocked on connected Whatsapp user phone
func (cli *Client) GetAllBlockedContacts() ([]types.JID, error) {
// GetBlocklist gets the list of users that this user has blocked.
func (cli *Client) GetBlocklist() (*types.Blocklist, error) {
resp, err := cli.sendIQ(infoQuery{
Namespace: "blocklist",
Type: iqGet,
Expand All @@ -551,56 +545,30 @@ func (cli *Client) GetAllBlockedContacts() ([]types.JID, error) {
if err != nil {
return nil, err
}

blockedContacts := cli.cacheBlockedContacts(resp)

return blockedContacts, nil
}

// setContactBlockedStatus updates the contact blocked status
func (cli *Client) setContactBlockedStatus(user types.JID, block bool) ([]types.JID, error) {
blockAction := "unblock"
if block {
blockAction = "block"
list, ok := resp.GetOptionalChildByTag("list")
if !ok {
return nil, &ElementMissingError{Tag: "list", In: "response to blocklist query"}
}
return cli.parseBlocklist(&list), nil
}

// UpdateBlocklist updates the user's block list and returns the updated list.
func (cli *Client) UpdateBlocklist(jid types.JID, action events.BlocklistChangeAction) (*types.Blocklist, error) {
resp, err := cli.sendIQ(infoQuery{
Namespace: "blocklist",
Type: iqSet,
To: types.ServerJID,
Content: []waBinary.Node{{
Tag: "item",
Attrs: waBinary.Attrs{
"action": blockAction,
"jid": user,
"jid": jid,
"action": string(action),
},
}},
})
if err != nil {
return nil, err
}

blockedContacts := cli.cacheBlockedContacts(resp)

return blockedContacts, nil
}

// BlockContact updates the contact blocked status to true
func (cli *Client) BlockContact(user types.JID) ([]types.JID, error) {
blockedList, err := cli.setContactBlockedStatus(user, true)
if err != nil {
return nil, err
}

return blockedList, nil
}

// UnblockContact updates the contact blocked status to false
func (cli *Client) UnblockContact(user types.JID) ([]types.JID, error) {
blockedList, err := cli.setContactBlockedStatus(user, false)
if err != nil {
return nil, err
list, ok := resp.GetOptionalChildByTag("list")
if !ok {
return nil, &ElementMissingError{Tag: "list", In: "response to blocklist update"}
}

return blockedList, nil
return cli.parseBlocklist(&list), err
}

0 comments on commit 97dbf9e

Please sign in to comment.