aim-oscar-server/message_delivery_routine.go
2021-12-26 21:56:59 -05:00

84 lines
2.6 KiB
Go

package main
import (
"aim-oscar/models"
"aim-oscar/oscar"
"aim-oscar/util"
"context"
"log"
"github.com/uptrace/bun"
)
type routineFn func(db *bun.DB)
func MessageDelivery(sm *SessionManager) (chan *models.Message, routineFn) {
commCh := make(chan *models.Message, 1)
routine := func(db *bun.DB) {
log.Printf("message delivery routine started")
for {
message, more := <-commCh
if !more {
log.Printf("message delivery routine shutting down")
return
}
log.Printf("got a message: %s", message)
if s := sm.GetSession(message.To); s != nil {
messageSnac := oscar.NewSNAC(4, 7)
messageSnac.Data.WriteUint64(message.Cookie)
messageSnac.Data.WriteUint16(1)
messageSnac.Data.WriteLPString(message.From)
messageSnac.Data.WriteUint16(0) // TODO: sender's warning level
tlvs := []*oscar.TLV{
oscar.NewTLV(1, util.Word(0)), // TODO: user class
oscar.NewTLV(6, util.Dword(0)), // TODO: user status
oscar.NewTLV(0xf, util.Dword(0)), // TODO: user idle time
oscar.NewTLV(3, util.Dword(0)), // TODO: user creation time
// oscar.NewTLV(4, []byte{}), // TODO: this TLV appears in automated responses like away messages
}
// Length of TLVs in fixed part
messageSnac.Data.WriteUint16(uint16(len(tlvs)))
// Write all of the TLVs to the SNAC
for _, tlv := range tlvs {
messageSnac.Data.WriteBinary(tlv)
}
frag := oscar.Buffer{}
frag.Write([]byte{5, 1, 0, 4, 1, 1, 1, 2}) // TODO: first fragment [id, version, len, len, (cap * len)... ]
frag.Write([]byte{1, 1}) // message text fragment start (this is a busted "TLV")
frag.WriteUint16(uint16(len(message.Contents) + 4)) // length of TLV
frag.Write([]byte{0, 0, 0, 0}) // TODO: message charset number, message charset subset
frag.WriteString(message.Contents)
// Append the fragments
messageSnac.Data.WriteBinary(oscar.NewTLV(2, frag.Bytes()))
messageFlap := oscar.NewFLAP(2)
messageFlap.Data.WriteBinary(messageSnac)
if err := s.Send(messageFlap); err != nil {
log.Panicf("could not deliver message %d: %s", message.Cookie, err.Error())
continue
} else {
log.Printf("sent message %d to user %s at %s", message.Cookie, message.To, s.RemoteAddr())
}
if message.StoreOffline {
if err := message.MarkDelivered(context.Background(), db); err != nil {
log.Panicf("could not mark message %d as delivered: %s", message.Cookie, err.Error())
}
}
} else {
log.Printf("could not find session for user %s", message.To)
}
}
}
return commCh, routine
}