2024-01-28 22:04:40 +00:00
|
|
|
package socket
|
|
|
|
|
2024-01-29 21:08:05 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
2024-01-29 23:46:22 +00:00
|
|
|
"reflect"
|
2024-01-29 21:08:05 +00:00
|
|
|
|
|
|
|
"github.com/beevik/etree"
|
|
|
|
"github.com/ectrc/snow/aid"
|
|
|
|
"github.com/ectrc/snow/person"
|
|
|
|
"github.com/gofiber/contrib/websocket"
|
|
|
|
)
|
|
|
|
|
|
|
|
type JabberData struct {
|
|
|
|
JabberID string
|
2024-02-14 20:30:55 +00:00
|
|
|
PartyID string
|
2024-01-31 00:16:57 +00:00
|
|
|
LastPresence string
|
2024-01-29 21:08:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var jabberHandlers = map[string]func(*Socket[JabberData], *etree.Document) error {
|
2024-02-16 01:14:14 +00:00
|
|
|
"stream": jabberStreamHandler,
|
2024-01-29 21:08:05 +00:00
|
|
|
"open": jabberOpenHandler,
|
|
|
|
"iq": jabberIqRootHandler,
|
2024-02-14 20:30:55 +00:00
|
|
|
"presence": jabberPresenceRootHandler,
|
2024-01-31 00:16:57 +00:00
|
|
|
"message": jabberMessageHandler,
|
2024-01-29 21:08:05 +00:00
|
|
|
}
|
2024-01-28 22:04:40 +00:00
|
|
|
|
|
|
|
func HandleNewJabberSocket(identifier string) {
|
2024-01-29 21:08:05 +00:00
|
|
|
socket, ok := JabberSockets.Get(identifier)
|
2024-01-28 22:04:40 +00:00
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
2024-01-31 00:16:57 +00:00
|
|
|
defer JabberSockets.Delete(socket.ID)
|
2024-01-29 21:08:05 +00:00
|
|
|
|
|
|
|
for {
|
|
|
|
_, message, failed := socket.Connection.ReadMessage()
|
|
|
|
if failed != nil {
|
|
|
|
break
|
|
|
|
}
|
2024-02-16 01:14:14 +00:00
|
|
|
|
|
|
|
JabberSocketOnMessage(socket, message)
|
|
|
|
}
|
|
|
|
}
|
2024-01-29 21:08:05 +00:00
|
|
|
|
2024-02-16 01:14:14 +00:00
|
|
|
func JabberSocketOnMessage(socket *Socket[JabberData], message []byte) {
|
|
|
|
parsed := etree.NewDocument()
|
|
|
|
if err := parsed.ReadFromString(string(message)); err != nil {
|
|
|
|
return
|
|
|
|
}
|
2024-01-29 21:08:05 +00:00
|
|
|
|
2024-02-16 01:14:14 +00:00
|
|
|
if handler, ok := jabberHandlers[parsed.Root().Tag]; ok {
|
|
|
|
if err := handler(socket, parsed); err != nil {
|
|
|
|
socket.Connection.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, err.Error()))
|
|
|
|
return
|
2024-01-29 21:08:05 +00:00
|
|
|
}
|
2024-02-16 01:14:14 +00:00
|
|
|
return
|
2024-01-29 21:08:05 +00:00
|
|
|
}
|
2024-02-16 01:14:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func jabberStreamHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
2024-02-16 01:35:12 +00:00
|
|
|
socket.Write([]byte(`<stream:stream id="`+socket.ID+`" from="prod.ol.epicgames.com" xml:lang="*" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client"/>`))
|
|
|
|
// socket.Write([]byte(`<open xmlns="urn:ietf:params:xml:ns:xmpp-framing" from="prod.ol.epicgames.com" version="1.0" id="`+ socket.ID +`" />`))
|
|
|
|
// socket.Write([]byte(`<stream:features xmlns:stream="http://etherx.jabber.org/streams" id="`+ socket.ID +`" />`))
|
2024-02-16 01:14:14 +00:00
|
|
|
return nil
|
2024-01-29 21:08:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func jabberOpenHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
2024-01-31 00:16:57 +00:00
|
|
|
socket.Write([]byte(`<open xmlns="urn:ietf:params:xml:ns:xmpp-framing" from="prod.ol.epicgames.com" version="1.0" id="`+ socket.ID +`" />`))
|
|
|
|
socket.Write([]byte(`<stream:features xmlns:stream="http://etherx.jabber.org/streams" />`))
|
2024-01-29 21:08:05 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func jabberIqRootHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
|
|
|
redirect := map[string]func(*Socket[JabberData], *etree.Document) error {
|
|
|
|
"set": jabberIqSetHandler,
|
|
|
|
"get": jabberIqGetHandler,
|
|
|
|
}
|
|
|
|
|
|
|
|
if handler, ok := redirect[parsed.Root().SelectAttr("type").Value]; ok {
|
|
|
|
if err := handler(socket, parsed); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func jabberIqSetHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
|
|
|
snowId, err := aid.GetSnowFromToken(parsed.FindElement("/iq/query/password").Text())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
person := person.Find(snowId)
|
|
|
|
if person == nil {
|
|
|
|
return fmt.Errorf("person not found")
|
|
|
|
}
|
|
|
|
|
2024-01-31 00:16:57 +00:00
|
|
|
JabberSockets.ChangeKey(socket.ID, person.ID)
|
|
|
|
socket.ID = person.ID
|
2024-01-29 21:08:05 +00:00
|
|
|
socket.Person = person
|
2024-01-31 00:16:57 +00:00
|
|
|
socket.Data.JabberID = snowId + "@prod.ol.epicgames.com/" + parsed.FindElement("/iq/query/resource").Text()
|
2024-02-14 20:30:55 +00:00
|
|
|
socket.Data.PartyID = person.DisplayName + ":" + person.ID + parsed.FindElement("/iq/query/resource").Text()
|
2024-01-29 21:08:05 +00:00
|
|
|
|
2024-01-31 00:16:57 +00:00
|
|
|
socket.Write([]byte(`<iq xmlns="jabber:client" type="result" id="_xmpp_auth1" from="prod.ol.epicgames.com" to="`+ socket.Data.JabberID +`" />`))
|
2024-01-29 21:08:05 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func jabberIqGetHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
2024-01-31 00:16:57 +00:00
|
|
|
socket.Write([]byte(`<iq xmlns="jabber:client" type="result" id="`+ parsed.Root().SelectAttr("id").Value +`" from="prod.ol.epicgames.com" to="`+ socket.Data.JabberID +`" />`))
|
|
|
|
socket.JabberNotifyFriends()
|
2024-01-29 21:08:05 +00:00
|
|
|
return nil
|
2024-01-29 23:46:22 +00:00
|
|
|
}
|
|
|
|
|
2024-02-14 20:30:55 +00:00
|
|
|
func jabberPresenceRootHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
2024-01-31 00:36:47 +00:00
|
|
|
status := parsed.FindElement("/presence/status")
|
|
|
|
if status == nil {
|
2024-02-14 20:30:55 +00:00
|
|
|
return jabberPresenceJoinGroupchat(socket, parsed)
|
2024-01-31 00:36:47 +00:00
|
|
|
}
|
2024-02-14 20:30:55 +00:00
|
|
|
|
2024-01-31 00:36:47 +00:00
|
|
|
socket.Data.LastPresence = status.Text()
|
2024-01-31 00:16:57 +00:00
|
|
|
socket.JabberNotifyFriends()
|
2024-02-14 20:30:55 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func jabberPresenceJoinGroupchat(socket *Socket[JabberData], parsed *etree.Document) error {
|
|
|
|
towards := parsed.FindElement("/presence").SelectAttr("to").Value
|
|
|
|
|
|
|
|
partyId := aid.Regex(towards, `Party-(.*?)@`)
|
|
|
|
if partyId == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
party, ok := person.Parties.Get(*partyId)
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, member := range party.Members {
|
|
|
|
if member.Person.ID == socket.ID {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
memberSocket, ok := JabberSockets.Get(member.Person.ID)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
memberPartyId := "Party-" + party.ID + "@muc.prod.ol.epicgames.com/" + memberSocket.Data.PartyID
|
|
|
|
memberRole := aid.Ternary[string](party.Captain.Person.ID == member.Person.ID, "moderator", "participant")
|
|
|
|
memberAffiliation := aid.Ternary[string](party.Captain.Person.ID == member.Person.ID, "owner", "none")
|
|
|
|
|
|
|
|
socket.Write([]byte(`<presence xmlns="jabber:client" from="`+ memberPartyId +`" to="`+ socket.Data.JabberID +`">
|
|
|
|
<x xmlns="http://jabber.org/protocol/muc#user">
|
|
|
|
<item
|
|
|
|
affiliation="`+ memberAffiliation +`"
|
|
|
|
role="`+ memberRole +`"
|
|
|
|
jid="`+ memberSocket.Data.JabberID +`"
|
|
|
|
nick="`+ memberPartyId +`"
|
|
|
|
/>
|
|
|
|
</x>
|
|
|
|
</presence>`))
|
|
|
|
}
|
|
|
|
|
|
|
|
socketPartyId := "Party-" + party.ID + "@muc.prod.ol.epicgames.com/" + socket.Data.PartyID
|
|
|
|
socketRole := aid.Ternary[string](party.Captain.Person.ID == socket.ID, "moderator", "participant")
|
|
|
|
socketAffiliation := aid.Ternary[string](party.Captain.Person.ID == socket.ID, "owner", "none")
|
|
|
|
|
|
|
|
socket.Write([]byte(`<presence xmlns="jabber:client" from="`+ socketPartyId +`" to="`+ socket.Data.JabberID +`">
|
|
|
|
<x xmlns="http://jabber.org/protocol/muc#user">
|
|
|
|
<item
|
|
|
|
affiliation="`+ socketAffiliation +`"
|
|
|
|
role="`+ socketRole +`"
|
|
|
|
jid="`+ socket.Data.JabberID +`"
|
|
|
|
nick="`+ socketPartyId +`"
|
|
|
|
/>
|
|
|
|
<status code="110"/>
|
|
|
|
<status code="100"/>
|
|
|
|
<status code="170"/>
|
|
|
|
</x>
|
|
|
|
</presence>`))
|
|
|
|
|
2024-01-31 00:16:57 +00:00
|
|
|
return nil
|
|
|
|
}
|
2024-01-29 23:46:22 +00:00
|
|
|
|
2024-01-31 00:16:57 +00:00
|
|
|
func jabberMessageHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
2024-02-14 22:20:17 +00:00
|
|
|
message := parsed.FindElement("/message")
|
|
|
|
if message.FindElement("/body") == nil {
|
2024-01-31 00:36:47 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-02-14 22:20:17 +00:00
|
|
|
towards := message.SelectAttr("to")
|
|
|
|
if towards == nil {
|
2024-01-31 00:36:47 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-02-14 22:20:17 +00:00
|
|
|
redirect := map[string]func(*Socket[JabberData], *etree.Document) error {
|
|
|
|
"chat": jabberMessageChatHandler,
|
|
|
|
"groupchat": jabberMessageGroupchatHandler,
|
|
|
|
}
|
|
|
|
|
|
|
|
if handler, ok := redirect[towards.Value]; ok {
|
|
|
|
if err := handler(socket, parsed); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-01-31 00:36:47 +00:00
|
|
|
}
|
2024-02-14 22:20:17 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: IMPLEMENT WHISPERING
|
|
|
|
|
|
|
|
func jabberMessageChatHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func jabberMessageGroupchatHandler(socket *Socket[JabberData], parsed *etree.Document) error {
|
2024-01-31 00:16:57 +00:00
|
|
|
return nil
|
2024-01-29 23:46:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Socket[T]) JabberSendMessageToPerson(data aid.JSON) {
|
|
|
|
if reflect.TypeOf(s.Data) != reflect.TypeOf(&JabberData{}) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
jabberSocket, ok := JabberSockets.Get(s.ID)
|
|
|
|
if !ok {
|
|
|
|
aid.Print("jabber socket not found even though it should be")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-01-31 00:16:57 +00:00
|
|
|
s.Write([]byte(`<message xmlns="jabber:client" from="xmpp-admin@prod.ol.epicgames.com" to="`+ jabberSocket.Data.JabberID +`">
|
2024-01-29 23:46:22 +00:00
|
|
|
<body>`+ string(data.ToBytes()) +`</body>
|
|
|
|
</message>`))
|
2024-01-31 00:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Socket[T]) JabberNotifyFriends() {
|
|
|
|
if reflect.TypeOf(s.Data) != reflect.TypeOf(&JabberData{}) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
jabberSocket, ok := JabberSockets.Get(s.ID)
|
|
|
|
if !ok {
|
|
|
|
aid.Print("jabber socket not found even though it should be")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
s.Person.Relationships.Range(func(key string, value *person.Relationship) bool {
|
|
|
|
friendSocket, found := JabberSockets.Get(value.From.ID)
|
|
|
|
if value.Direction == person.RelationshipOutboundDirection {
|
|
|
|
friendSocket, found = JabberSockets.Get(value.Towards.ID)
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
friendSocket.Write([]byte(`<presence xmlns="jabber:client" type="available" from="`+ jabberSocket.Data.JabberID +`" to="`+ friendSocket.Data.JabberID +`">
|
|
|
|
<status>`+ jabberSocket.Data.LastPresence +`</status>
|
|
|
|
</presence>`))
|
|
|
|
|
|
|
|
jabberSocket.Write([]byte(`<presence xmlns="jabber:client" type="available" from="`+ friendSocket.Data.JabberID +`" to="`+ jabberSocket.Data.JabberID +`">
|
|
|
|
<status>`+ friendSocket.Data.LastPresence +`</status>
|
|
|
|
</presence>`))
|
|
|
|
|
|
|
|
return true
|
|
|
|
})
|
2024-02-14 20:30:55 +00:00
|
|
|
|
|
|
|
jabberSocket.Write([]byte(`<presence xmlns="jabber:client" type="available" from="`+ jabberSocket.Data.JabberID +`" to="`+ jabberSocket.Data.JabberID +`">
|
|
|
|
<status>`+ jabberSocket.Data.LastPresence +`</status>
|
|
|
|
</presence>`))
|
2024-01-28 22:04:40 +00:00
|
|
|
}
|