first group fix

This commit is contained in:
Martin/Geno 2019-10-03 23:44:42 +02:00 committed by genofire
parent eef58dcfba
commit 91e2aa41b8
8 changed files with 216 additions and 51 deletions

View File

@ -72,7 +72,7 @@ func (c *Config) handleDiscoItems(s xmpp.Sender, p stanza.Packet) {
log.WithFields(map[string]interface{}{ log.WithFields(map[string]interface{}{
"type": c.Type, "type": c.Type,
"from": s, "id": attrs.Id,
"to": attrs.To, "to": attrs.To,
}).Debug("disco items") }).Debug("disco items")
s.Send(iq) s.Send(iq)
@ -92,28 +92,32 @@ func (c *Config) handleIQ(s xmpp.Sender, p stanza.Packet) {
log.WithFields(map[string]interface{}{ log.WithFields(map[string]interface{}{
"type": c.Type, "type": c.Type,
"from": s,
"to": attrs.To, "to": attrs.To,
}).Debugf("ignore: %s", iq.Payload) "id": attrs.Id,
}).Debugf("ignore: %v", iq.Payload)
s.Send(resp) s.Send(resp)
} }
func (c *Config) handleMessage(s xmpp.Sender, p stanza.Packet) { func (c *Config) handleMessage(s xmpp.Sender, p stanza.Packet) {
msg, ok := p.(stanza.Message) msg, ok := p.(stanza.Message)
attr := msg.Attrs attrs := msg.Attrs
if !ok { if !ok {
pr, ok := p.(stanza.Message) pr, ok := p.(stanza.Presence)
attr = pr.Attrs attrs = pr.Attrs
if !ok { if !ok {
return return
} }
} }
if c.XMPPDebug { logger := log.WithFields(map[string]interface{}{
log.WithFields(map[string]interface{}{
"type": c.Type, "type": c.Type,
"from": s, "id": attrs.Id,
"to": attr.To, "to": attrs.To,
"id": attr.Id, })
}).Debug(msg.XMPPFormat()) if attrs.Type == "error" {
logger.Error(msg.XMPPFormat())
return
}
if c.XMPPDebug {
logger.Debug(msg.XMPPFormat())
} }
c.comp.Send(p) c.comp.Send(p)
} }

View File

@ -16,16 +16,32 @@ func (c *Config) sender(packets chan stanza.Packet) {
} }
} }
func (c *Config) fixAddr(addr string) string {
if addr == "" {
return c.Host
}
if strings.Contains(addr, "{{DOMAIN}}") {
return strings.Replace(addr, "{{DOMAIN}}", c.Host, 1)
}
if !strings.Contains(addr, "@") {
return addr + "@" + c.Host
}
return addr
}
func (c *Config) sending(packet stanza.Packet) stanza.Packet { func (c *Config) sending(packet stanza.Packet) stanza.Packet {
logger := log.WithField("type", c.Type) logger := log.WithField("type", c.Type)
switch p := packet.(type) { switch p := packet.(type) {
case stanza.Presence:
p.From = c.fixAddr(p.From)
if p.To != "" {
p.To = c.fixAddr(p.To)
}
return p
case stanza.Message: case stanza.Message:
if p.From == "" { p.From = c.fixAddr(p.From)
p.From = c.Host if p.To != "" {
} else if strings.Contains(p.From, "{{DOMAIN}}") { p.To = c.fixAddr(p.To)
p.From = strings.Replace(p.From, "{{DOMAIN}}", c.Host, 1)
} else {
p.From += "@" + c.Host
} }
if c.XMPPDebug { if c.XMPPDebug {
logger.WithFields(map[string]interface{}{ logger.WithFields(map[string]interface{}{

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"github.com/o3ma/o3" "github.com/o3ma/o3"
"gosrc.io/xmpp/stanza"
"dev.sum7.eu/genofire/golang-lib/database" "dev.sum7.eu/genofire/golang-lib/database"
@ -16,8 +17,10 @@ type Account struct {
ThreemaID *o3.ThreemaID ThreemaID *o3.ThreemaID
send chan<- o3.Message send chan<- o3.Message
receive <-chan o3.ReceivedMsg receive <-chan o3.ReceivedMsg
xmpp chan<- stanza.Packet
deliveredMSG map[uint64]string deliveredMSG map[uint64]string
readedMSG map[uint64]string readedMSG map[uint64]string
XMPPResource map[string]map[string]bool
} }
func (t *Threema) getAccount(jid *models.JID) (*Account, error) { func (t *Threema) getAccount(jid *models.JID) (*Account, error) {
@ -48,6 +51,10 @@ func (t *Threema) getAccount(jid *models.JID) (*Account, error) {
AccountThreema: account, AccountThreema: account,
ThreemaID: &tid, ThreemaID: &tid,
threema: t, threema: t,
xmpp: t.out,
XMPPResource: make(map[string]map[string]bool),
deliveredMSG: make(map[uint64]string),
readedMSG: make(map[uint64]string),
} }
session := o3.NewSessionContext(tid) session := o3.NewSessionContext(tid)
a.send, a.receive, err = session.Run() a.send, a.receive, err = session.Run()
@ -57,10 +64,8 @@ func (t *Threema) getAccount(jid *models.JID) (*Account, error) {
} }
a.XMPP = *jid a.XMPP = *jid
a.deliveredMSG = make(map[uint64]string)
a.readedMSG = make(map[uint64]string)
go a.receiver(t.out) go a.receiver()
t.accountJID[jid.String()] = a t.accountJID[jid.String()] = a
return a, nil return a, nil

View File

@ -0,0 +1,41 @@
package threema
import (
"encoding/base32"
"fmt"
"strings"
"github.com/o3ma/o3"
"gosrc.io/xmpp"
)
func strFromThreemaGroup(header *o3.GroupMessageHeader) string {
cid := strings.ToLower(header.CreatorID.String())
gid := strings.ToLower(base32.StdEncoding.EncodeToString(header.GroupID[:]))
return fmt.Sprintf("%s-%s", cid, gid)
}
func jidFromThreemaGroup(sender string, header *o3.GroupMessageHeader) string {
return fmt.Sprintf("%s@{{DOMAIN}}/%s", strFromThreemaGroup(header), sender)
}
func jidToThreemaGroup(jidS string) (string, *o3.GroupMessageHeader) {
jid, err := xmpp.NewJid(jidS)
if err != nil {
return "", nil
}
node := strings.ToUpper(jid.Node)
a := strings.SplitN(node, "-", 2)
if len(a) != 2 {
return "", nil
}
header := &o3.GroupMessageHeader{
CreatorID: o3.NewIDString(a[0]),
}
result, err := base32.StdEncoding.DecodeString(a[1])
if err != nil {
return "", nil
}
copy(header.GroupID[:], []byte(result))
return jid.Resource, header
}

View File

@ -68,11 +68,19 @@ func (t *Threema) Send(packet stanza.Packet) {
func (t *Threema) send(packet stanza.Packet) stanza.Packet { func (t *Threema) send(packet stanza.Packet) stanza.Packet {
switch p := packet.(type) { switch p := packet.(type) {
case stanza.Presence: case stanza.Presence:
log.Debug(p) from := models.ParseJID(p.Attrs.From)
account, err := t.getAccount(from)
if err != nil {
msg := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, To: from.String()})
msg.Body = "It was not possible to send, because we have no account for you.\nPlease generate one, by sending `generate` to this gateway"
return msg
}
account.handlePresence(p)
return nil return nil
case stanza.Message: case stanza.Message:
from := models.ParseJID(p.Attrs.From) from := models.ParseJID(p.Attrs.From)
to := models.ParseJID(p.Attrs.To) to := models.ParseJID(p.Attrs.To)
if p.Attrs.Type == stanza.MessageTypeError { if p.Attrs.Type == stanza.MessageTypeError {
msg := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, To: from.String()}) msg := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, To: from.String()})
if p.Error.Text == "User session not found" { if p.Error.Text == "User session not found" {

View File

@ -1,7 +1,6 @@
package threema package threema
import ( import (
"encoding/base32"
"errors" "errors"
"fmt" "fmt"
"strconv" "strconv"
@ -9,16 +8,17 @@ import (
"github.com/bdlm/log" "github.com/bdlm/log"
"github.com/o3ma/o3" "github.com/o3ma/o3"
"gosrc.io/xmpp"
"gosrc.io/xmpp/stanza" "gosrc.io/xmpp/stanza"
) )
func (a *Account) receiver(out chan<- stanza.Packet) { func (a *Account) receiver() {
for receivedMessage := range a.receive { for receivedMessage := range a.receive {
if receivedMessage.Err != nil { if receivedMessage.Err != nil {
log.Warnf("Error Receiving Message: %s\n", receivedMessage.Err) log.Warnf("Error Receiving Message: %s\n", receivedMessage.Err)
xMSG := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, To: a.XMPP.String()}) xMSG := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, To: a.XMPP.String()})
xMSG.Body = fmt.Sprintf("error on decoding message:\n%v", receivedMessage.Err) xMSG.Body = fmt.Sprintf("error on decoding message:\n%v", receivedMessage.Err)
out <- xMSG a.xmpp <- xMSG
continue continue
} }
header := receivedMessage.Msg.Header() header := receivedMessage.Msg.Header()
@ -26,12 +26,36 @@ func (a *Account) receiver(out chan<- stanza.Packet) {
if string(a.TID) == sender { if string(a.TID) == sender {
continue continue
} }
if p, err := a.receiving(receivedMessage.Msg); err != nil { if p, gh, err := a.receiving(receivedMessage.Msg); err != nil {
xMSG := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, From: sender, To: a.XMPP.String()}) xMSG := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, From: sender, To: a.XMPP.String()})
xMSG.Body = fmt.Sprintf("error on decoding message: %s\n%v", err, receivedMessage.Msg) xMSG.Body = fmt.Sprintf("error on decoding message: %s\n%v", err, receivedMessage.Msg)
out <- xMSG a.xmpp <- xMSG
} else if p != nil { } else {
out <- p if gh != nil {
xid := &xmpp.Jid{
Node: a.XMPP.Local,
Domain: a.XMPP.Domain,
}
id := strFromThreemaGroup(gh)
if len(a.XMPPResource[id]) == 0 {
xMSG := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, From: sender, To: a.XMPP.String()})
//TODO please join
xMSG.Body = fmt.Sprintf(`ERROR: group message not delievered, please join
xmpp:%s@{{DOMAIN}}?join`, id)
a.xmpp <- xMSG
continue
}
for r := range a.XMPPResource[id] {
xid.Resource = r
switch m := p.(type) {
case stanza.Message:
m.Attrs.To = xid.Full()
a.xmpp <- m
}
}
} else {
a.xmpp <- p
}
} }
} }
} }
@ -42,13 +66,7 @@ func requestExtensions(xMSG *stanza.Message) {
xMSG.Extensions = append(xMSG.Extensions, stanza.StateActive{}) xMSG.Extensions = append(xMSG.Extensions, stanza.StateActive{})
} }
func jidFromThreemaGroup(sender string, header *o3.GroupMessageHeader) string { func (a *Account) receiving(receivedMessage o3.Message) (stanza.Packet, *o3.GroupMessageHeader, error) {
cid := strings.ToLower(header.CreatorID.String())
gid := strings.ToLower(base32.StdEncoding.EncodeToString(header.GroupID[:]))
return fmt.Sprintf("%s-%s@{{DOMAIN}}/%s", cid, gid, sender)
}
func (a *Account) receiving(receivedMessage o3.Message) (stanza.Packet, error) {
header := receivedMessage.Header() header := receivedMessage.Header()
sender := header.Sender.String() sender := header.Sender.String()
logger := log.WithFields(map[string]interface{}{ logger := log.WithFields(map[string]interface{}{
@ -62,53 +80,65 @@ func (a *Account) receiving(receivedMessage o3.Message) (stanza.Packet, error) {
dbText := "recv text" dbText := "recv text"
xMSG := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, From: sender, To: a.XMPP.String(), Id: strconv.FormatUint(header.ID, 10)}) xMSG := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, From: sender, To: a.XMPP.String(), Id: strconv.FormatUint(header.ID, 10)})
if msg.GroupMessageHeader != nil { if msg.GroupMessageHeader != nil {
xMSG = stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeGroupchat, From: jidFromThreemaGroup(sender, msg.GroupMessageHeader), To: a.XMPP.String(), Id: strconv.FormatUint(header.ID, 10)}) to := a.XMPP.String()
ad := strings.SplitN(msg.Body, "=", 2)
from := sender
if len(ad) == 2 {
from = strings.ToLower(ad[0])[:len(ad[0])-3]
}
xMSG = stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeGroupchat, From: jidFromThreemaGroup(from, msg.GroupMessageHeader), To: to, Id: strconv.FormatUint(header.ID, 10)})
if len(ad) == 2 {
xMSG.Body = ad[1][4:]
} else {
xMSG.Body = msg.Body
}
dbText = "recv grouptext" dbText = "recv grouptext"
} else { } else {
xMSG.Body = msg.Body
requestExtensions(&xMSG) requestExtensions(&xMSG)
} }
xMSG.Body = msg.Body
logger.WithFields(map[string]interface{}{ logger.WithFields(map[string]interface{}{
"from_x": xMSG.From, "from_x": xMSG.From,
"id": xMSG.Id, "id": xMSG.Id,
"text": xMSG.Body, "text": xMSG.Body,
}).Debug(dbText) }).Debug(dbText)
return xMSG, nil return xMSG, msg.GroupMessageHeader, nil
case *o3.AudioMessage: case *o3.AudioMessage:
if a.threema.httpUploadPath == "" { if a.threema.httpUploadPath == "" {
return nil, errors.New("no place to store files at transport configurated") return nil, nil, errors.New("no place to store files at transport configurated")
} }
data, err := msg.GetData() data, err := msg.GetData()
if err != nil { if err != nil {
logger.Warnf("unable to read data from message: %s", err) logger.Warnf("unable to read data from message: %s", err)
return nil, err return nil, nil, err
} }
xMSG, err := a.FileToXMPP(sender, header.ID, "mp3", data) xMSG, err := a.FileToXMPP(sender, header.ID, "mp3", data)
if err != nil { if err != nil {
logger.Warnf("unable to create data from message: %s", err) logger.Warnf("unable to create data from message: %s", err)
return nil, err return nil, nil, err
} }
requestExtensions(&xMSG) requestExtensions(&xMSG)
logger.WithField("url", xMSG.Body).Debug("recv audio") logger.WithField("url", xMSG.Body).Debug("recv audio")
return xMSG, nil return xMSG, nil, nil
case *o3.ImageMessage: case *o3.ImageMessage:
if a.threema.httpUploadPath == "" { if a.threema.httpUploadPath == "" {
return nil, errors.New("no place to store files at transport configurated") return nil, nil, errors.New("no place to store files at transport configurated")
} }
data, err := msg.GetData(a.ThreemaID) data, err := msg.GetData(a.ThreemaID)
if err != nil { if err != nil {
logger.Warnf("unable to read data from message: %s", err) logger.Warnf("unable to read data from message: %s", err)
return nil, err return nil, nil, err
} }
xMSG, err := a.FileToXMPP(sender, header.ID, "jpg", data) xMSG, err := a.FileToXMPP(sender, header.ID, "jpg", data)
if err != nil { if err != nil {
logger.Warnf("unable to create data from message: %s", err) logger.Warnf("unable to create data from message: %s", err)
return nil, err return nil, nil, err
} }
requestExtensions(&xMSG) requestExtensions(&xMSG)
logger.WithField("url", xMSG.Body).Debug("recv image") logger.WithField("url", xMSG.Body).Debug("recv image")
return xMSG, nil return xMSG, nil, nil
case *o3.DeliveryReceiptMessage: case *o3.DeliveryReceiptMessage:
xMSG := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, From: sender, To: a.XMPP.String()}) xMSG := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, From: sender, To: a.XMPP.String()})
state := "" state := ""
@ -135,9 +165,9 @@ func (a *Account) receiving(receivedMessage o3.Message) (stanza.Packet, error) {
if len(xMSG.Extensions) > 0 { if len(xMSG.Extensions) > 0 {
logger.WithField("state", state).Debug("recv state") logger.WithField("state", state).Debug("recv state")
return xMSG, nil return xMSG, nil, nil
} }
return nil, nil return nil, nil, nil
case *o3.TypingNotificationMessage: case *o3.TypingNotificationMessage:
xMSG := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, From: sender, To: a.XMPP.String(), Id: strconv.FormatUint(header.ID, 10)}) xMSG := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, From: sender, To: a.XMPP.String(), Id: strconv.FormatUint(header.ID, 10)})
if msg.OnOff != 0 { if msg.OnOff != 0 {
@ -146,7 +176,7 @@ func (a *Account) receiving(receivedMessage o3.Message) (stanza.Packet, error) {
xMSG.Extensions = append(xMSG.Extensions, stanza.StateInactive{}) xMSG.Extensions = append(xMSG.Extensions, stanza.StateInactive{})
} }
logger.WithField("on", msg.OnOff).Debug("recv typing") logger.WithField("on", msg.OnOff).Debug("recv typing")
return xMSG, nil return xMSG, nil, nil
} }
return nil, errors.New("not known data format") return nil, nil, errors.New("not known data format")
} }

View File

@ -20,6 +20,7 @@ func createDummyAccount() Account {
a := Account{ a := Account{
deliveredMSG: make(map[uint64]string), deliveredMSG: make(map[uint64]string),
readedMSG: make(map[uint64]string), readedMSG: make(map[uint64]string),
xmpp: make(chan<- stanza.Packet),
} }
a.TID = make([]byte, len(threemaFromIDByte)) a.TID = make([]byte, len(threemaFromIDByte))
copy(a.TID, threemaFromIDByte[:]) copy(a.TID, threemaFromIDByte[:])

View File

@ -2,14 +2,73 @@ package threema
import ( import (
"encoding/base32" "encoding/base32"
"encoding/xml"
"strconv" "strconv"
"strings" "strings"
"github.com/bdlm/log" "github.com/bdlm/log"
"github.com/o3ma/o3" "github.com/o3ma/o3"
"gosrc.io/xmpp"
"gosrc.io/xmpp/stanza" "gosrc.io/xmpp/stanza"
) )
type PresMUCUserItem struct {
XMLName xml.Name `xml:"item"`
Affiliation string `xml:"affiliation,attr"`
Role string `xml:"role,attr"`
}
type PresMUCUserStatus struct {
XMLName xml.Name `xml:"status"`
Code int `xml:"code,attr"`
}
type PresMUCUserList struct {
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
Items []PresMUCUserItem
Status *PresMUCUserStatus
}
func (a *Account) handlePresence(p stanza.Presence) error {
logger := log.WithFields(map[string]interface{}{
"from": p.Attrs.From,
"to": p.Attrs.To,
})
_, header := jidToThreemaGroup(p.To)
if header == nil {
logger.Debug("no group presence")
return nil
}
from, _ := xmpp.NewJid(p.From)
to, _ := xmpp.NewJid(p.To)
if a.XMPPResource[to.Node] == nil {
a.XMPPResource[to.Node] = make(map[string]bool)
}
a.XMPPResource[to.Node][from.Resource] = true
ownsender := strings.ToLower(a.ThreemaID.ID.String())
//TODO list current users
senders := []string{header.CreatorID.String(), ownsender}
for _, sender := range senders {
sender = strings.ToLower(sender)
pres := stanza.NewPresence(stanza.Attrs{To: p.Attrs.From, From: jidFromThreemaGroup(sender, header)})
presMUCUserList := PresMUCUserList{
Items: []PresMUCUserItem{
{
Affiliation: "admin",
Role: "moderator",
},
},
}
if sender == ownsender {
presMUCUserList.Status = &PresMUCUserStatus{Code: 110}
}
pres.Extensions = append(pres.Extensions, presMUCUserList)
a.xmpp <- pres
}
return nil
}
func (a *Account) Send(to string, msg stanza.Message) error { func (a *Account) Send(to string, msg stanza.Message) error {
m, err := a.sending(to, msg) m, err := a.sending(to, msg)
if err != nil { if err != nil {
@ -125,6 +184,7 @@ func (a *Account) sending(to string, msg stanza.Message) (o3.Message, error) {
if groupHeader != nil { if groupHeader != nil {
logger.Debug("send grouptext") logger.Debug("send grouptext")
// TODO iterate of all occupants // TODO iterate of all occupants
//msg3.GroupMessageHeader.Recipient: o3.NewIDString(to),
return msg3, nil return msg3, nil
} }
a.deliveredMSG[msg3ID] = msg.Id a.deliveredMSG[msg3ID] = msg.Id