From 570593522cfee39d2c797d5c8c4ea2ce33a67f02 Mon Sep 17 00:00:00 2001 From: Konstantin Dudkov Date: Sun, 22 Oct 2023 01:39:56 +0300 Subject: [PATCH] . --- cmd/goatak_server/main.go | 21 +++++-- cmd/goatak_server/processors.go | 6 +- cmd/webclient/http_server.go | 2 +- cmd/webclient/processors.go | 7 +-- pkg/cot/cotmessage.go | 19 ++++++- pkg/cot/node.go | 6 +- pkg/cot/util.go | 4 +- pkg/model/chat.go | 53 ++++-------------- pkg/model/chat_test.go | 99 +++++++++++++++++++++++++++++++++ 9 files changed, 156 insertions(+), 61 deletions(-) create mode 100644 pkg/model/chat_test.go diff --git a/cmd/goatak_server/main.go b/cmd/goatak_server/main.go index a5c1d2a..be33d21 100644 --- a/cmd/goatak_server/main.go +++ b/cmd/goatak_server/main.go @@ -323,12 +323,12 @@ func (app *App) MessageProcessor() { } func (app *App) route(msg *cot.CotMessage) { - if len(msg.Detail.GetDest()) > 0 { - for _, s := range msg.Detail.GetDest() { + if dest := msg.Detail.GetDest(); len(dest) > 0 { + for _, s := range dest { app.SendToCallsign(s, msg.TakMessage) } } else { - app.SendToAllOther(msg) + app.SendBroadcast(msg) } } @@ -366,7 +366,7 @@ func (app *App) cleanOldUnits() { } } -func (app *App) SendToAllOther(msg *cot.CotMessage) { +func (app *App) SendBroadcast(msg *cot.CotMessage) { app.ForAllClients(func(ch client.ClientHandler) bool { if ch.GetName() != msg.From && ch.CanSeeScope(msg.Scope) { if err := ch.SendMsg(msg.TakMessage); err != nil { @@ -384,6 +384,19 @@ func (app *App) SendToCallsign(callsign string, msg *cotproto.TakMessage) { if err := ch.SendMsg(msg); err != nil { app.Logger.Errorf("error: %v", err) } + } + } + return true + }) +} + +func (app *App) SendToUid(uid string, msg *cotproto.TakMessage) { + app.ForAllClients(func(ch client.ClientHandler) bool { + for c := range ch.GetUids() { + if c == uid { + if err := ch.SendMsg(msg); err != nil { + app.Logger.Errorf("error: %v", err) + } return false } } diff --git a/cmd/goatak_server/processors.go b/cmd/goatak_server/processors.go index 7627fb8..7ab281b 100644 --- a/cmd/goatak_server/processors.go +++ b/cmd/goatak_server/processors.go @@ -44,9 +44,9 @@ func (app *App) loggerProcessor(msg *cot.CotMessage) { func (app *App) removeItemProcessor(msg *cot.CotMessage) { // t-x-d-d - if msg.Detail != nil && msg.Detail.Has("link") { - uid := msg.Detail.GetFirst("link").GetAttr("uid") - typ := msg.Detail.GetFirst("link").GetAttr("type") + if link := msg.GetFirstLink("p-p"); link != nil { + uid := link.GetAttr("uid") + typ := link.GetAttr("type") if uid == "" { app.Logger.Warnf("invalid remove message: %s", msg.Detail) return diff --git a/cmd/webclient/http_server.go b/cmd/webclient/http_server.go index 7155fe9..236a0a0 100644 --- a/cmd/webclient/http_server.go +++ b/cmd/webclient/http_server.go @@ -180,7 +180,7 @@ func addMessageHandler(app *App) func(req *air.Request, res *air.Response) error } if msg.Id == "" { - msg.Id = uuid.New().String() + msg.Id = uuid.NewString() } app.SendMsg(model.MakeChatMessage(msg)) app.messages.Add(msg) diff --git a/cmd/webclient/processors.go b/cmd/webclient/processors.go index 83983a0..661885a 100644 --- a/cmd/webclient/processors.go +++ b/cmd/webclient/processors.go @@ -48,9 +48,9 @@ func (app *App) loggerProcessor(msg *cot.CotMessage) { func (app *App) removeItemProcessor(msg *cot.CotMessage) { // t-x-d-d - if msg.Detail != nil && msg.Detail.Has("link") { - uid := msg.Detail.GetFirst("link").GetAttr("uid") - typ := msg.Detail.GetFirst("link").GetAttr("type") + if link := msg.GetFirstLink("p-p"); link != nil { + uid := link.GetAttr("uid") + typ := link.GetAttr("type") if uid == "" { app.Logger.Warnf("invalid remove message: %s", msg.Detail) return @@ -60,7 +60,6 @@ func (app *App) removeItemProcessor(msg *cot.CotMessage) { case model.CONTACT: app.Logger.Debugf("remove %s by message", uid) v.SetOffline() - app.processChange(v) return case model.UNIT, model.POINT: app.Logger.Debugf("remove unit/point %s type %s by message", uid, typ) diff --git a/pkg/cot/cotmessage.go b/pkg/cot/cotmessage.go index 8baa875..71a8245 100644 --- a/pkg/cot/cotmessage.go +++ b/pkg/cot/cotmessage.go @@ -113,14 +113,14 @@ func (m *CotMessage) IsChat() bool { if m == nil || m.TakMessage == nil { return false } - return m.GetType() == "b-t-f" && m.Detail != nil && m.Detail.Has("__chat") + return m.GetType() == "b-t-f" } func (m *CotMessage) IsChatReceipt() bool { if m == nil || m.TakMessage == nil { return false } - return (m.GetType() == "b-t-f-r" || m.GetType() == "b-t-f-d") && m.Detail != nil && m.Detail.Has("__chatreceipt") + return m.GetType() == "b-t-f-r" || m.GetType() == "b-t-f-d" } func (m *CotMessage) PrintChat() string { @@ -161,6 +161,9 @@ func (m *CotMessage) GetLon() float64 { } func (m *CotMessage) GetParent() (string, string) { + if m.Detail == nil { + return "", "" + } for _, link := range m.Detail.GetAll("link") { if link.GetAttr("relation") == "p-p" { return link.GetAttr("uid"), link.GetAttr("parent_callsign") @@ -169,6 +172,18 @@ func (m *CotMessage) GetParent() (string, string) { return "", "" } +func (m *CotMessage) GetFirstLink(relation string) *Node { + if m.Detail == nil { + return nil + } + for _, link := range m.Detail.GetAll("link") { + if link.GetAttr("relation") == relation { + return link + } + } + return nil +} + func TimeFromMillis(ms uint64) time.Time { return time.Unix(0, 1000000*int64(ms)) } diff --git a/pkg/cot/node.go b/pkg/cot/node.go index 9385b2b..4bc66d1 100644 --- a/pkg/cot/node.go +++ b/pkg/cot/node.go @@ -45,7 +45,7 @@ func DetailsFromString(s string) (*Node, error) { return x, err } -func (n *Node) AddLink(uid, typ, parent string) { +func (n *Node) AddPpLink(uid, typ, callsign string) { params := make(map[string]string) if uid != "" { params["uid"] = uid @@ -53,8 +53,8 @@ func (n *Node) AddLink(uid, typ, parent string) { if typ != "" { params["type"] = typ } - if parent != "" { - params["parent_callsign"] = parent + if callsign != "" { + params["parent_callsign"] = callsign } //params["production_time"] = prodTime.UTC().Format(time.RFC3339) params["relation"] = "p-p" diff --git a/pkg/cot/util.go b/pkg/cot/util.go index 04cf973..ded83c8 100644 --- a/pkg/cot/util.go +++ b/pkg/cot/util.go @@ -43,7 +43,7 @@ func MakeOfflineMsg(uid string, typ string) *cotproto.TakMessage { msg := BasicMsg("t-x-d-d", uuid.New().String(), time.Minute*3) msg.CotEvent.How = "h-g-i-g-o" xd := NewXmlDetails() - xd.AddLink(uid, typ, "") + xd.AddPpLink(uid, typ, "") msg.CotEvent.Detail = &cotproto.Detail{XmlDetail: xd.AsXMLString()} return msg } @@ -54,7 +54,7 @@ func MakeDpMsg(uid string, typ string, name string, lat float64, lon float64) *c msg.CotEvent.Lat = lat msg.CotEvent.Lon = lon xd := NewXmlDetails() - xd.AddLink(uid, typ, "") + xd.AddPpLink(uid, typ, "") msg.CotEvent.Detail = &cotproto.Detail{ XmlDetail: xd.AsXMLString(), Contact: &cotproto.Contact{Callsign: name}, diff --git a/pkg/model/chat.go b/pkg/model/chat.go index 7e4b393..7324578 100644 --- a/pkg/model/chat.go +++ b/pkg/model/chat.go @@ -78,41 +78,6 @@ func (m *ChatMessage) String() string { return fmt.Sprintf("Chat %s (%s) -> %s (%s) \"%s\"", m.From, m.FromUid, m.Chatroom, m.ToUid, m.Text) } -// direct -// <__chat parent="RootContactGroup" groupOwner="false" chatroom="Cl1" id="{{uid_to}}" senderCallsign="Kott"> -// -// -// Roger -// <__serverdestination destinations="192.168.0.15:4242:tcp:{{uid_from}}"/> -// - -// chatroom -// <__chat parent="RootContactGroup" groupOwner="false" chatroom="All Chat Rooms" id="All Chat Rooms" senderCallsign="Kott"> -// -// -// Roger -// <__serverdestination destinations="192.168.0.15:4242:tcp:{{uid_from}}"/> - -// red -// <__chat parent="TeamGroups" groupOwner="false" chatroom="Red" id="Red" senderCallsign="Kott"> -// -// -// at VDO -// <__serverdestination destinations="192.168.0.15:4242:tcp:ANDROID-dc4a1fb7ad4180be"/> -// - -// add contact to group -// <__chat parent="UserGroups" groupOwner="true" messageId="82741635-04dc-413b-9b66-289fde3e22f0" chatroom="j" id="c06c2986-f122-4e85-b213-88498a6fe8bb" senderCallsign="Kott"> -// -// -// -// -// -// -// -// <__serverdestination destinations="192.168.1.72:4242:tcp:ANDROID-765a942cbe30d010"/> -// [UPDATED CONTACTS] - func MsgToChat(m *cot.CotMessage) *ChatMessage { chat := m.Detail.GetFirst("__chat") if chat == nil { @@ -125,19 +90,23 @@ func MsgToChat(m *cot.CotMessage) *ChatMessage { Parent: chat.GetAttr("parent"), Chatroom: chat.GetAttr("chatroom"), From: chat.GetAttr("senderCallsign"), + ToUid: chat.GetAttr("id"), } if cg := chat.GetFirst("chatgrp"); cg != nil { c.FromUid = cg.GetAttr("uid0") - c.ToUid = cg.GetAttr("uid1") } - for _, dest := range m.Detail.GetFirst("marti").GetAll("dest") { - if dest.GetAttr("callsign") != "" { - c.Direct = true + if link := m.GetFirstLink("p-p"); link != nil { + if uid := link.GetAttr("uid"); uid != "" { + c.FromUid = uid } } + if c.Chatroom != c.ToUid { + c.Direct = true + } + if rem := m.Detail.GetFirst("remarks"); rem != nil { c.Text = html.UnescapeString(rem.GetText()) } else { @@ -152,14 +121,14 @@ func MakeChatMessage(c *ChatMessage) *cotproto.TakMessage { msgUid := fmt.Sprintf("GeoChat.%s.%s.%s", c.FromUid, c.ToUid, c.Id) msg := cot.BasicMsg("b-t-f", msgUid, time.Second*10) xd := cot.NewXmlDetails() - xd.AddLink(c.FromUid, "", "") + xd.AddPpLink(c.FromUid, "", "") - chat := xd.AddChild("__chat", map[string]string{"parent": "RootContactGroup", "groupOwner": "false", "chatroom": c.Chatroom, "senderCallsign": c.From, "id": c.ToUid, "messageId": c.Id}, "") + chat := xd.AddChild("__chat", map[string]string{"parent": c.Parent, "groupOwner": "false", "chatroom": c.Chatroom, "senderCallsign": c.From, "id": c.ToUid, "messageId": c.Id}, "") chat.AddChild("chatgrp", map[string]string{"uid0": c.FromUid, "uid1": c.ToUid, "id": c.ToUid}, "") xd.AddChild("remarks", map[string]string{"source": "BAO.F.ATAK." + c.FromUid, "to": c.ToUid, "time": t}, html.EscapeString(c.Text)) - if c.Chatroom != c.ToUid { + if c.Direct { marti := xd.AddChild("marti", nil, "") marti.AddChild("dest", map[string]string{"callsign": c.Chatroom}, "") } diff --git a/pkg/model/chat_test.go b/pkg/model/chat_test.go new file mode 100644 index 0000000..233ae0c --- /dev/null +++ b/pkg/model/chat_test.go @@ -0,0 +1,99 @@ +package model + +import ( + "github.com/kdudkov/goatak/pkg/cot" + "github.com/kdudkov/goatak/pkg/cotproto" + "github.com/magiconair/properties/assert" + "testing" + "time" +) + +func TestChat(t *testing.T) { + // user1 (uid1) -> user2 (uid2) + + d := "<__chat parent=\"RootContactGroup\" groupOwner=\"false\" messageId=\"4de0262c-633f-46eb-b8e5-5ef1eb1e5e22\" chatroom=\"user2\" id=\"uid2\" senderCallsign=\"user1\">" + + "" + + "" + + "<__serverdestination destinations=\"1.1.1.1:4242:tcp:uid1\"/>" + + "at breach" + + "" + + m := cot.BasicMsg("b-t-f", "GeoChat.uid1.uid2.4de0262c-633f-46eb-b8e5-5ef1eb1e5e22", time.Minute) + xd, _ := cot.DetailsFromString(d) + m.CotEvent.Detail = &cotproto.Detail{XmlDetail: xd.AsXMLString()} + msg := cot.CotMessage{TakMessage: m, Detail: xd} + + assert.Equal(t, msg.IsChat(), true) + + cm := MsgToChat(&msg) + + assert.Equal(t, cm.From, "user1") + assert.Equal(t, cm.FromUid, "uid1") + assert.Equal(t, cm.ToUid, "uid2") + assert.Equal(t, cm.Chatroom, "user2") + assert.Equal(t, cm.Direct, true) +} + +func TestBtfd(t *testing.T) { + d := "<__chatreceipt parent=\"RootContactGroup\" groupOwner=\"false\" messageId=\"4de0262c-633f-46eb-b8e5-5ef1eb1e5e22\" chatroom=\"user1\" id=\"uid1\" senderCallsign=\"user2\">" + + "" + + "" + + "<__serverdestination destinations=\"2.2.2.2:4242:tcp:uid2\"/>" + + "" + + m := cot.BasicMsg("b-t-f-d", "4de0262c-633f-46eb-b8e5-5ef1eb1e5e22", time.Minute) + xd, _ := cot.DetailsFromString(d) + m.CotEvent.Detail = &cotproto.Detail{XmlDetail: xd.AsXMLString()} + msg := cot.CotMessage{TakMessage: m, Detail: xd} + + assert.Equal(t, msg.IsChatReceipt(), true) + + //cm := MsgToChat(&msg) + // + //assert.Equal(t, cm.From, "user1") + //assert.Equal(t, cm.FromUid, "uid1") + //assert.Equal(t, cm.ToUid, "uid2") + //assert.Equal(t, cm.Chatroom, "user2") + //assert.Equal(t, cm.Direct, true) +} + +func TestBtfr(t *testing.T) { + d := "<__chatreceipt parent=\"RootContactGroup\" groupOwner=\"false\" messageId=\"4de0262c-633f-46eb-b8e5-5ef1eb1e5e22\" chatroom=\"user1\" id=\"uid1\" senderCallsign=\"user2\">" + + "" + + "" + + "<__serverdestination destinations=\"2.2.2.2:4242:tcp:uid2\"/>" + + "" + + m := cot.BasicMsg("b-t-f-r", "4de0262c-633f-46eb-b8e5-5ef1eb1e5e22", time.Minute) + xd, _ := cot.DetailsFromString(d) + m.CotEvent.Detail = &cotproto.Detail{XmlDetail: xd.AsXMLString()} + msg := cot.CotMessage{TakMessage: m, Detail: xd} + + assert.Equal(t, msg.IsChatReceipt(), true) +} + +func TestMsgRed(t *testing.T) { + // user1 - Red + d := "<__chat parent=\"TeamGroups\" groupOwner=\"false\" messageId=\"9f46716f-c875-43b0-8162-3da74196353f\" chatroom=\"Red\" id=\"Red\" senderCallsign=\"user1\">" + + "" + + "" + + "<__serverdestination destinations=\"1.1.1.1:4242:tcp:uid1\"/>" + + "Roger" + + "" + + m := cot.BasicMsg("b-t-f", "GeoChat.uid1.Red.9f46716f-c875-43b0-8162-3da74196353f", time.Minute) + xd, _ := cot.DetailsFromString(d) + m.CotEvent.Detail = &cotproto.Detail{XmlDetail: xd.AsXMLString()} + msg := cot.CotMessage{TakMessage: m, Detail: xd} + + assert.Equal(t, msg.IsChat(), true) + + cm := MsgToChat(&msg) + + assert.Equal(t, cm.From, "user1") + assert.Equal(t, cm.FromUid, "uid1") + assert.Equal(t, cm.ToUid, "Red") + assert.Equal(t, cm.Chatroom, "Red") + assert.Equal(t, cm.Direct, false) + assert.Equal(t, cm.Text, "Roger") +}