| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- package server
- import (
- "errors"
- "io"
- "time"
- "github.com/mkaminski/goaim/oscar"
- )
- type FeedbagHandler interface {
- DeleteItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x0A_FeedbagDeleteItem) (XMessage, error)
- InsertItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x08_FeedbagInsertItem) (XMessage, error)
- QueryHandler(sess *Session, fm FeedbagManager) (XMessage, error)
- QueryIfModifiedHandler(sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x05_FeedbagQueryIfModified) (XMessage, error)
- RightsQueryHandler() XMessage
- StartClusterHandler(oscar.SNAC_0x13_0x11_FeedbagStartCluster)
- UpdateItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x09_FeedbagUpdateItem) (XMessage, error)
- }
- func NewFeedbagRouter() FeedbagRouter {
- return FeedbagRouter{
- FeedbagHandler: FeedbagService{},
- }
- }
- type FeedbagRouter struct {
- FeedbagHandler
- }
- func (rt FeedbagRouter) RouteFeedbag(sm SessionManager, sess *Session, fm FeedbagManager, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
- switch snac.SubGroup {
- case oscar.FeedbagRightsQuery:
- inSNAC := oscar.SNAC_0x13_0x02_FeedbagRightsQuery{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- outSNAC := rt.RightsQueryHandler()
- return writeOutSNAC(snac, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
- case oscar.FeedbagQuery:
- inSNAC, err := rt.QueryHandler(sess, fm)
- if err != nil {
- return err
- }
- return writeOutSNAC(snac, inSNAC.snacFrame, inSNAC.snacOut, sequence, w)
- case oscar.FeedbagQueryIfModified:
- inSNAC := oscar.SNAC_0x13_0x05_FeedbagQueryIfModified{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- outSNAC, err := rt.QueryIfModifiedHandler(sess, fm, inSNAC)
- if err != nil {
- return err
- }
- return writeOutSNAC(snac, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
- case oscar.FeedbagUse:
- return nil
- case oscar.FeedbagInsertItem:
- inSNAC := oscar.SNAC_0x13_0x08_FeedbagInsertItem{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- outSNAC, err := rt.InsertItemHandler(sm, sess, fm, inSNAC)
- if err != nil {
- return err
- }
- return writeOutSNAC(snac, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
- case oscar.FeedbagUpdateItem:
- inSNAC := oscar.SNAC_0x13_0x09_FeedbagUpdateItem{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- outSNAC, err := rt.UpdateItemHandler(sm, sess, fm, inSNAC)
- if err != nil {
- return err
- }
- return writeOutSNAC(snac, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
- case oscar.FeedbagDeleteItem:
- inSNAC := oscar.SNAC_0x13_0x0A_FeedbagDeleteItem{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- outSNAC, err := rt.DeleteItemHandler(sm, sess, fm, inSNAC)
- if err != nil {
- return err
- }
- return writeOutSNAC(snac, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
- case oscar.FeedbagStartCluster:
- inSNAC := oscar.SNAC_0x13_0x11_FeedbagStartCluster{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- rt.StartClusterHandler(inSNAC)
- return nil
- case oscar.FeedbagEndCluster:
- return nil
- default:
- return ErrUnsupportedSubGroup
- }
- }
- type FeedbagService struct {
- }
- func (s FeedbagService) RightsQueryHandler() XMessage {
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.FEEDBAG,
- SubGroup: oscar.FeedbagRightsReply,
- },
- snacOut: oscar.SNAC_0x13_0x03_FeedbagRightsReply{
- TLVRestBlock: oscar.TLVRestBlock{
- TLVList: oscar.TLVList{
- {
- TType: 0x03,
- Val: uint16(200),
- },
- {
- TType: 0x04,
- Val: []uint16{
- 0x3D,
- 0x3D,
- 0x64,
- 0x64,
- 0x01,
- 0x01,
- 0x32,
- 0x00,
- 0x00,
- 0x03,
- 0x00,
- 0x00,
- 0x00,
- 0x80,
- 0xFF,
- 0x14,
- 0xC8,
- 0x01,
- 0x00,
- 0x01,
- 0x00,
- },
- },
- {
- TType: 0x05,
- Val: uint16(200),
- },
- {
- TType: 0x06,
- Val: uint16(200),
- },
- {
- TType: 0x07,
- Val: uint16(200),
- },
- {
- TType: 0x08,
- Val: uint16(200),
- },
- {
- TType: 0x09,
- Val: uint16(200),
- },
- {
- TType: 0x0A,
- Val: uint16(200),
- },
- {
- TType: 0x0C,
- Val: uint16(200),
- },
- {
- TType: 0x0D,
- Val: uint16(200),
- },
- {
- TType: 0x0E,
- Val: uint16(100),
- },
- },
- },
- },
- }
- }
- func (s FeedbagService) QueryHandler(sess *Session, fm FeedbagManager) (XMessage, error) {
- fb, err := fm.Retrieve(sess.ScreenName)
- if err != nil {
- return XMessage{}, err
- }
- lm := time.UnixMilli(0)
- if len(fb) > 0 {
- lm, err = fm.LastModified(sess.ScreenName)
- if err != nil {
- return XMessage{}, err
- }
- }
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.FEEDBAG,
- SubGroup: oscar.FeedbagReply,
- },
- snacOut: oscar.SNAC_0x13_0x06_FeedbagReply{
- Version: 0,
- Items: fb,
- LastUpdate: uint32(lm.Unix()),
- },
- }, nil
- }
- func (s FeedbagService) QueryIfModifiedHandler(sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x05_FeedbagQueryIfModified) (XMessage, error) {
- fb, err := fm.Retrieve(sess.ScreenName)
- if err != nil {
- return XMessage{}, err
- }
- lm := time.UnixMilli(0)
- if len(fb) > 0 {
- lm, err = fm.LastModified(sess.ScreenName)
- if err != nil {
- return XMessage{}, err
- }
- if lm.Before(time.Unix(int64(snacPayloadIn.LastUpdate), 0)) {
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.FEEDBAG,
- SubGroup: oscar.FeedbagReplyNotModified,
- },
- snacOut: oscar.SNAC_0x13_0x05_FeedbagQueryIfModified{
- LastUpdate: uint32(lm.Unix()),
- Count: uint8(len(fb)),
- },
- }, nil
- }
- }
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.FEEDBAG,
- SubGroup: oscar.FeedbagReply,
- },
- snacOut: oscar.SNAC_0x13_0x06_FeedbagReply{
- Version: 0,
- Items: fb,
- LastUpdate: uint32(lm.Unix()),
- },
- }, nil
- }
- func (s FeedbagService) InsertItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x08_FeedbagInsertItem) (XMessage, error) {
- for _, item := range snacPayloadIn.Items {
- // don't let users block themselves, it causes the AIM client to go
- // into a weird state.
- if item.ClassID == 3 && item.Name == sess.ScreenName {
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.FEEDBAG,
- SubGroup: oscar.FeedbagErr,
- },
- snacOut: oscar.SnacError{
- Code: ErrorCodeNotSupportedByHost,
- },
- }, nil
- }
- }
- if err := fm.Upsert(sess.ScreenName, snacPayloadIn.Items); err != nil {
- return XMessage{}, nil
- }
- for _, item := range snacPayloadIn.Items {
- switch item.ClassID {
- case oscar.FeedbagClassIdBuddy, oscar.FeedbagClassIDPermit: // add new buddy
- err := UnicastArrival(item.Name, sess.ScreenName, sm)
- switch {
- case errors.Is(err, ErrSessNotFound):
- continue
- case err != nil:
- return XMessage{}, err
- }
- case oscar.FeedbagClassIDDeny: // block buddy
- // notify this user that buddy is offline
- err := UnicastDeparture(item.Name, sess.ScreenName, sm)
- switch {
- case errors.Is(err, ErrSessNotFound):
- continue
- case err != nil:
- return XMessage{}, err
- }
- // notify former buddy that this user is offline
- if err := UnicastDeparture(sess.ScreenName, item.Name, sm); err != nil {
- return XMessage{}, err
- }
- }
- }
- snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
- for range snacPayloadIn.Items {
- snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000)
- }
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.FEEDBAG,
- SubGroup: oscar.FeedbagStatus,
- },
- snacOut: snacPayloadOut,
- }, nil
- }
- func (s FeedbagService) UpdateItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x09_FeedbagUpdateItem) (XMessage, error) {
- if err := fm.Upsert(sess.ScreenName, snacPayloadIn.Items); err != nil {
- return XMessage{}, nil
- }
- for _, item := range snacPayloadIn.Items {
- switch item.ClassID {
- case oscar.FeedbagClassIdBuddy, oscar.FeedbagClassIDPermit:
- err := UnicastArrival(item.Name, sess.ScreenName, sm)
- switch {
- case errors.Is(err, ErrSessNotFound):
- continue
- case err != nil:
- return XMessage{}, err
- }
- }
- }
- snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
- for range snacPayloadIn.Items {
- snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000)
- }
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.FEEDBAG,
- SubGroup: oscar.FeedbagStatus,
- },
- snacOut: snacPayloadOut,
- }, nil
- }
- func (s FeedbagService) DeleteItemHandler(sm SessionManager, sess *Session, fm FeedbagManager, snacPayloadIn oscar.SNAC_0x13_0x0A_FeedbagDeleteItem) (XMessage, error) {
- if err := fm.Delete(sess.ScreenName, snacPayloadIn.Items); err != nil {
- return XMessage{}, err
- }
- for _, item := range snacPayloadIn.Items {
- if item.ClassID == oscar.FeedbagClassIDDeny {
- err := UnicastArrival(item.Name, sess.ScreenName, sm)
- switch {
- case errors.Is(err, ErrSessNotFound):
- continue
- case err != nil:
- return XMessage{}, err
- }
- err = UnicastArrival(sess.ScreenName, item.Name, sm)
- switch {
- case errors.Is(err, ErrSessNotFound):
- continue
- case err != nil:
- return XMessage{}, err
- }
- }
- }
- snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
- for range snacPayloadIn.Items {
- snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000) // success by default
- }
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.FEEDBAG,
- SubGroup: oscar.FeedbagStatus,
- },
- snacOut: snacPayloadOut,
- }, nil
- }
- // StartClusterHandler exists to capture the SNAC input in unit tests to verify
- // it's correctly unmarshalled.
- func (s FeedbagService) StartClusterHandler(oscar.SNAC_0x13_0x11_FeedbagStartCluster) {
- }
|