You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
matterbridge/vendor/go.mau.fi/whatsmeow/types/jid.go

209 lines
5.7 KiB
Go

// 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 types contains various structs and other types used by whatsmeow.
package types
import (
"database/sql"
"database/sql/driver"
"fmt"
"strconv"
"strings"
signalProtocol "go.mau.fi/libsignal/protocol"
)
// Known JID servers on WhatsApp
const (
DefaultUserServer = "s.whatsapp.net"
GroupServer = "g.us"
LegacyUserServer = "c.us"
BroadcastServer = "broadcast"
)
// Some JIDs that are contacted often.
var (
EmptyJID = JID{}
GroupServerJID = NewJID("", GroupServer)
ServerJID = NewJID("", DefaultUserServer)
BroadcastServerJID = NewJID("", BroadcastServer)
StatusBroadcastJID = NewJID("status", BroadcastServer)
PSAJID = NewJID("0", LegacyUserServer)
OfficialBusinessJID = NewJID("16505361212", LegacyUserServer)
)
// MessageID is the internal ID of a WhatsApp message.
type MessageID = string
// JID represents a WhatsApp user ID.
//
// There are two types of JIDs: regular JID pairs (user and server) and AD-JIDs (user, agent and device).
// AD JIDs are only used to refer to specific devices of users, so the server is always s.whatsapp.net (DefaultUserServer).
// Regular JIDs can be used for entities on any servers (users, groups, broadcasts).
type JID struct {
User string
Agent uint8
Device uint8
Server string
AD bool
}
// UserInt returns the user as an integer. This is only safe to run on normal users, not on groups or broadcast lists.
func (jid JID) UserInt() uint64 {
number, _ := strconv.ParseUint(jid.User, 10, 64)
return number
}
// ToNonAD returns a version of the JID struct that doesn't have the agent and device set.
func (jid JID) ToNonAD() JID {
if jid.AD {
return JID{
User: jid.User,
Server: DefaultUserServer,
}
} else {
return jid
}
}
// SignalAddress returns the Signal protocol address for the user.
func (jid JID) SignalAddress() *signalProtocol.SignalAddress {
user := jid.User
if jid.Agent != 0 {
user = fmt.Sprintf("%s_%d", jid.User, jid.Agent)
}
return signalProtocol.NewSignalAddress(user, uint32(jid.Device))
}
// IsBroadcastList returns true if the JID is a broadcast list, but not the status broadcast.
func (jid JID) IsBroadcastList() bool {
return jid.Server == BroadcastServer && jid.User != StatusBroadcastJID.User
}
// NewADJID creates a new AD JID.
func NewADJID(user string, agent, device uint8) JID {
return JID{
User: user,
Agent: agent,
Device: device,
Server: DefaultUserServer,
AD: true,
}
}
func parseADJID(user string) (JID, error) {
var fullJID JID
fullJID.AD = true
fullJID.Server = DefaultUserServer
dotIndex := strings.IndexRune(user, '.')
colonIndex := strings.IndexRune(user, ':')
if dotIndex < 0 || colonIndex < 0 || colonIndex+1 <= dotIndex {
return fullJID, fmt.Errorf("failed to parse ADJID: missing separators")
}
fullJID.User = user[:dotIndex]
agent, err := strconv.Atoi(user[dotIndex+1 : colonIndex])
if err != nil {
return fullJID, fmt.Errorf("failed to parse agent from JID: %w", err)
} else if agent < 0 || agent > 255 {
return fullJID, fmt.Errorf("failed to parse agent from JID: invalid value (%d)", agent)
}
device, err := strconv.Atoi(user[colonIndex+1:])
if err != nil {
return fullJID, fmt.Errorf("failed to parse device from JID: %w", err)
} else if device < 0 || device > 255 {
return fullJID, fmt.Errorf("failed to parse device from JID: invalid value (%d)", device)
}
fullJID.Agent = uint8(agent)
fullJID.Device = uint8(device)
return fullJID, nil
}
// ParseJID parses a JID out of the given string. It supports both regular and AD JIDs.
func ParseJID(jid string) (JID, error) {
parts := strings.Split(jid, "@")
if len(parts) == 1 {
return NewJID("", parts[0]), nil
} else if strings.ContainsRune(parts[0], ':') && strings.ContainsRune(parts[0], '.') && parts[1] == DefaultUserServer {
return parseADJID(parts[0])
}
return NewJID(parts[0], parts[1]), nil
}
// NewJID creates a new regular JID.
func NewJID(user, server string) JID {
return JID{
User: user,
Server: server,
}
}
// String converts the JID to a string representation.
// The output string can be parsed with ParseJID, except for JIDs with no User part specified.
func (jid JID) String() string {
if jid.AD {
return fmt.Sprintf("%s.%d:%d@%s", jid.User, jid.Agent, jid.Device, jid.Server)
} else if len(jid.User) > 0 {
return fmt.Sprintf("%s@%s", jid.User, jid.Server)
} else {
return jid.Server
}
}
// MarshalText implements encoding.TextMarshaler for JID
func (jid JID) MarshalText() ([]byte, error) {
return []byte(jid.String()), nil
}
// UnmarshalText implements encoding.TextUnmarshaler for JID
func (jid *JID) UnmarshalText(val []byte) error {
out, err := ParseJID(string(val))
if err != nil {
return err
}
*jid = out
return nil
}
// IsEmpty returns true if the JID has no server (which is required for all JIDs).
func (jid JID) IsEmpty() bool {
return len(jid.Server) == 0
}
var _ sql.Scanner = (*JID)(nil)
// Scan scans the given SQL value into this JID.
func (jid *JID) Scan(src interface{}) error {
if src == nil {
return nil
}
var out JID
var err error
switch val := src.(type) {
case string:
out, err = ParseJID(val)
case []byte:
out, err = ParseJID(string(val))
default:
err = fmt.Errorf("unsupported type %T for scanning JID", val)
}
if err != nil {
return err
}
*jid = out
return nil
}
// Value returns the string representation of the JID as a value that the SQL package can use.
func (jid JID) Value() (driver.Value, error) {
if len(jid.Server) == 0 {
return nil, nil
}
return jid.String(), nil
}