| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- package server
- import (
- "bytes"
- "errors"
- "fmt"
- "github.com/mkaminski/goaim/oscar"
- "io"
- "time"
- )
- type OServiceHandler interface {
- WriteOServiceHostOnline(w io.Writer, sequence *uint32) error
- ClientOnlineHandler(snacPayloadIn oscar.SNAC_0x01_0x02_OServiceClientOnline, sess *Session, sm SessionManager, fm FeedbagManager, room ChatRoom) error
- ClientVersionsHandler(snacPayloadIn oscar.SNAC_0x01_0x17_OServiceClientVersions) XMessage
- IdleNotificationHandler(sess *Session, sm SessionManager, fm *FeedbagStore, snacPayloadIn oscar.SNAC_0x01_0x11_OServiceIdleNotification) error
- RateParamsQueryHandler() XMessage
- RateParamsSubAddHandler(oscar.SNAC_0x01_0x08_OServiceRateParamsSubAdd)
- ServiceRequestHandler(cfg Config, cr *ChatRegistry, sess *Session, snacPayloadIn oscar.SNAC_0x01_0x04_OServiceServiceRequest) (XMessage, error)
- SetUserInfoFieldsHandler(sess *Session, sm SessionManager, fm *FeedbagStore, snacPayloadIn oscar.SNAC_0x01_0x1E_OServiceSetUserInfoFields) (XMessage, error)
- UserInfoQueryHandler(sess *Session) XMessage
- }
- func NewOServiceRouter() OServiceRouter {
- return OServiceRouter{
- OServiceHandler: OServiceService{},
- }
- }
- type OServiceRouter struct {
- OServiceHandler
- }
- func (rt OServiceRouter) RouteOService(cfg Config, cr *ChatRegistry, sm SessionManager, fm *FeedbagStore, sess *Session, room ChatRoom, SNACFrame oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
- switch SNACFrame.SubGroup {
- case oscar.OServiceClientOnline:
- inSNAC := oscar.SNAC_0x01_0x02_OServiceClientOnline{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- return rt.ClientOnlineHandler(inSNAC, sess, sm, fm, room)
- case oscar.OServiceServiceRequest:
- inSNAC := oscar.SNAC_0x01_0x04_OServiceServiceRequest{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- outSNAC, err := rt.ServiceRequestHandler(cfg, cr, sess, inSNAC)
- switch {
- case errors.Is(err, ErrUnsupportedSubGroup):
- return sendInvalidSNACErr(SNACFrame, w, sequence)
- case err != nil:
- return err
- }
- return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
- case oscar.OServiceRateParamsQuery:
- outSNAC := rt.RateParamsQueryHandler()
- return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
- case oscar.OServiceRateParamsSubAdd:
- inSNAC := oscar.SNAC_0x01_0x08_OServiceRateParamsSubAdd{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- rt.RateParamsSubAddHandler(inSNAC)
- return oscar.Unmarshal(&inSNAC, r)
- case oscar.OServiceUserInfoQuery:
- outSNAC := rt.UserInfoQueryHandler(sess)
- return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
- case oscar.OServiceIdleNotification:
- inSNAC := oscar.SNAC_0x01_0x11_OServiceIdleNotification{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- return rt.IdleNotificationHandler(sess, sm, fm, inSNAC)
- case oscar.OServiceClientVersions:
- inSNAC := oscar.SNAC_0x01_0x17_OServiceClientVersions{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- outSNAC := rt.ClientVersionsHandler(inSNAC)
- return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
- case oscar.OServiceSetUserInfoFields:
- inSNAC := oscar.SNAC_0x01_0x1E_OServiceSetUserInfoFields{}
- if err := oscar.Unmarshal(&inSNAC, r); err != nil {
- return err
- }
- outSNAC, err := rt.SetUserInfoFieldsHandler(sess, sm, fm, inSNAC)
- if err != nil {
- return err
- }
- return writeOutSNAC(SNACFrame, outSNAC.snacFrame, outSNAC.snacOut, sequence, w)
- default:
- return ErrUnsupportedSubGroup
- }
- }
- type OServiceService struct {
- }
- func (s OServiceService) WriteOServiceHostOnline(w io.Writer, sequence *uint32) error {
- fmt.Println("writeOServiceHostOnline...")
- snacFrameOut := oscar.SnacFrame{
- FoodGroup: oscar.OSERVICE,
- SubGroup: oscar.OServiceHostOnline,
- }
- snacPayloadOut := oscar.SNAC_0x01_0x03_OServiceHostOnline{
- FoodGroups: []uint16{oscar.OSERVICE, oscar.LOCATE, oscar.BUDDY, oscar.ICBM, oscar.FEEDBAG, oscar.CHAT_NAV},
- }
- return writeOutSNAC(oscar.SnacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
- }
- func (s OServiceService) ClientVersionsHandler(snacPayloadIn oscar.SNAC_0x01_0x17_OServiceClientVersions) XMessage {
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.OSERVICE,
- SubGroup: oscar.OServiceHostVersions,
- },
- snacOut: oscar.SNAC_0x01_0x18_OServiceHostVersions{
- Versions: snacPayloadIn.Versions,
- },
- }
- }
- func (s OServiceService) RateParamsQueryHandler() XMessage {
- snacFrameOut := oscar.SnacFrame{
- FoodGroup: oscar.OSERVICE,
- SubGroup: oscar.OServiceRateParamsReply,
- }
- snacPayloadOut := oscar.SNAC_0x01_0x07_OServiceRateParamsReply{
- RateClasses: []struct {
- ID uint16
- WindowSize uint32
- ClearLevel uint32
- AlertLevel uint32
- LimitLevel uint32
- DisconnectLevel uint32
- CurrentLevel uint32
- MaxLevel uint32
- LastTime uint32 // protocol v2 only
- CurrentState uint8 // protocol v2 only
- }{
- {
- ID: 0x0001,
- WindowSize: 0x00000050,
- ClearLevel: 0x000009C4,
- AlertLevel: 0x000007D0,
- LimitLevel: 0x000005DC,
- DisconnectLevel: 0x00000320,
- CurrentLevel: 0x00000D69,
- MaxLevel: 0x00001770,
- LastTime: 0x00000000,
- CurrentState: 0x00,
- },
- },
- RateGroups: []struct {
- ID uint16
- Pairs []struct {
- FoodGroup uint16
- SubGroup uint16
- } `count_prefix:"uint16"`
- }{
- {
- ID: 1,
- Pairs: []struct {
- FoodGroup uint16
- SubGroup uint16
- }{},
- },
- },
- }
- for i := uint16(0); i < 24; i++ { // for each food group
- for j := uint16(0); j < 32; j++ { // for each subgroup
- snacPayloadOut.RateGroups[0].Pairs = append(snacPayloadOut.RateGroups[0].Pairs,
- struct {
- FoodGroup uint16
- SubGroup uint16
- }{
- FoodGroup: i,
- SubGroup: j,
- })
- }
- }
- return XMessage{
- snacFrame: snacFrameOut,
- snacOut: snacPayloadOut,
- }
- }
- func (s OServiceService) UserInfoQueryHandler(sess *Session) XMessage {
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.OSERVICE,
- SubGroup: oscar.OServiceUserInfoUpdate,
- },
- snacOut: oscar.SNAC_0x01_0x0F_OServiceUserInfoUpdate{
- TLVUserInfo: sess.GetTLVUserInfo(),
- },
- }
- }
- func (s OServiceService) ClientOnlineHandler(snacPayloadIn oscar.SNAC_0x01_0x02_OServiceClientOnline, sess *Session, sm SessionManager, fm FeedbagManager, room ChatRoom) error {
- for _, version := range snacPayloadIn.GroupVersions {
- fmt.Printf("hahaha ClientOnlineHandler read SNAC client messageType: %+v\n", version)
- }
- if err := BroadcastArrival(sess, sm, fm); err != nil {
- return err
- }
- buddies, err := fm.Buddies(sess.ScreenName)
- if err != nil {
- return err
- }
- for _, buddy := range buddies {
- err := UnicastArrival(buddy, sess.ScreenName, sm)
- switch {
- case errors.Is(err, ErrSessNotFound):
- continue
- case err != nil:
- return err
- }
- }
- return nil
- }
- func (s OServiceService) SetUserInfoFieldsHandler(sess *Session, sm SessionManager, fm *FeedbagStore, snacPayloadIn oscar.SNAC_0x01_0x1E_OServiceSetUserInfoFields) (XMessage, error) {
- if status, hasStatus := snacPayloadIn.GetUint32(0x06); hasStatus {
- switch status {
- case 0x000:
- sess.SetInvisible(false)
- if err := BroadcastArrival(sess, sm, fm); err != nil {
- return XMessage{}, err
- }
- case 0x100:
- sess.SetInvisible(true)
- if err := BroadcastDeparture(sess, sm, fm); err != nil {
- return XMessage{}, err
- }
- default:
- return XMessage{}, fmt.Errorf("don't know what to do with status %d", status)
- }
- }
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.OSERVICE,
- SubGroup: oscar.OServiceUserInfoUpdate,
- },
- snacOut: oscar.SNAC_0x01_0x0F_OServiceUserInfoUpdate{
- TLVUserInfo: sess.GetTLVUserInfo(),
- },
- }, nil
- }
- func (s OServiceService) IdleNotificationHandler(sess *Session, sm SessionManager, fm *FeedbagStore, snacPayloadIn oscar.SNAC_0x01_0x11_OServiceIdleNotification) error {
- if snacPayloadIn.IdleTime == 0 {
- sess.SetActive()
- } else {
- sess.SetIdle(time.Duration(snacPayloadIn.IdleTime) * time.Second)
- }
- return BroadcastArrival(sess, sm, fm)
- }
- func (s OServiceService) ServiceRequestHandler(cfg Config, cr *ChatRegistry, sess *Session, snacPayloadIn oscar.SNAC_0x01_0x04_OServiceServiceRequest) (XMessage, error) {
- if snacPayloadIn.FoodGroup != oscar.CHAT {
- return XMessage{}, ErrUnsupportedSubGroup
- }
- roomMeta, ok := snacPayloadIn.GetSlice(0x01)
- if !ok {
- return XMessage{}, errors.New("missing room info")
- }
- roomSnac := oscar.SNAC_0x01_0x04_TLVRoomInfo{}
- if err := oscar.Unmarshal(&roomSnac, bytes.NewBuffer(roomMeta)); err != nil {
- return XMessage{}, err
- }
- room, err := cr.Retrieve(string(roomSnac.Cookie))
- if err != nil {
- return XMessage{}, ErrUnsupportedSubGroup
- }
- room.NewSessionWithSN(sess.ID, sess.ScreenName)
- return XMessage{
- snacFrame: oscar.SnacFrame{
- FoodGroup: oscar.OSERVICE,
- SubGroup: oscar.OServiceServiceResponse,
- },
- snacOut: oscar.SNAC_0x01_0x05_OServiceServiceResponse{
- TLVRestBlock: oscar.TLVRestBlock{
- TLVList: oscar.TLVList{
- {
- TType: oscar.OServiceTLVTagsReconnectHere,
- Val: Address(cfg.OSCARHost, cfg.ChatPort),
- },
- {
- TType: oscar.OServiceTLVTagsLoginCookie,
- Val: ChatCookie{
- Cookie: []byte(room.Cookie),
- SessID: sess.ID,
- },
- },
- {
- TType: oscar.OServiceTLVTagsGroupID,
- Val: oscar.CHAT,
- },
- {
- TType: oscar.OServiceTLVTagsSSLCertName,
- Val: "",
- },
- {
- TType: oscar.OServiceTLVTagsSSLState,
- Val: uint8(0x00),
- },
- },
- },
- },
- }, nil
- }
- // RateParamsSubAddHandler exists to capture the SNAC input in unit tests to
- // verify it's correctly unmarshalled.
- func (s OServiceService) RateParamsSubAddHandler(oscar.SNAC_0x01_0x08_OServiceRateParamsSubAdd) {
- }
- func NewOServiceRouterForChat() OServiceRouter {
- return OServiceRouter{
- OServiceHandler: OServiceServiceForChat{},
- }
- }
- type OServiceServiceForChat struct {
- OServiceService
- }
- func (s OServiceServiceForChat) WriteOServiceHostOnline(w io.Writer, sequence *uint32) error {
- fmt.Println("writeOServiceHostOnline...")
- snacFrameOut := oscar.SnacFrame{
- FoodGroup: oscar.OSERVICE,
- SubGroup: oscar.OServiceHostOnline,
- }
- snacPayloadOut := oscar.SNAC_0x01_0x03_OServiceHostOnline{
- FoodGroups: []uint16{oscar.OSERVICE, oscar.CHAT},
- }
- return writeOutSNAC(oscar.SnacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
- }
- func (s OServiceServiceForChat) ClientOnlineHandler(snacPayloadIn oscar.SNAC_0x01_0x02_OServiceClientOnline, sess *Session, sm SessionManager, fm FeedbagManager, room ChatRoom) error {
- for _, version := range snacPayloadIn.GroupVersions {
- fmt.Printf("ClientOnlineHandler read SNAC client messageType: %+v\n", version)
- }
- SendChatRoomInfoUpdate(sess, sm, room)
- AlertUserJoined(sess, sm)
- SetOnlineChatUsers(sess, sm)
- return nil
- }
|