From 060650a7aa7023b457e976f5121b40e6bc76fb0c Mon Sep 17 00:00:00 2001 From: Artem Titoulenko Date: Fri, 11 Feb 2022 13:28:20 -0500 Subject: [PATCH] fix offline status and adding buddies --- .gitignore | 1 + online_routine.go | 59 +++++++++++++++++--------- oscar/server.go | 4 ++ services/0x03_buddy_list_management.go | 17 +++++++- 4 files changed, 60 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 4dfe302..e0ec8a8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ aim-oscar aim.db vendor .DS_Store +env/dev.env diff --git a/online_routine.go b/online_routine.go index db90728..6d75254 100644 --- a/online_routine.go +++ b/online_routine.go @@ -44,31 +44,50 @@ func OnlineNotification(sm *SessionManager) (chan *models.User, routineFn) { if buddy.Source.Status == models.UserStatusAway || buddy.Source.Status == models.UserStatusDnd { continue } - log.Printf("telling %s that %s has a new status: %d!", buddy.Source.ScreenName, user.ScreenName, user.Status) + log.Printf("telling %s that %s has a new status: %d", buddy.Source.ScreenName, user.ScreenName, user.Status) if s := sm.GetSession(buddy.Source.ScreenName); s != nil { - onlineSnac := oscar.NewSNAC(3, 0xb) - onlineSnac.Data.WriteLPString(user.ScreenName) - onlineSnac.Data.WriteUint16(0) // TODO: user warning level + if user.Status == models.UserStatusOnline { + onlineSnac := oscar.NewSNAC(3, 0xb) + onlineSnac.Data.WriteLPString(user.ScreenName) + onlineSnac.Data.WriteUint16(0) // TODO: user warning level - tlvs := []*oscar.TLV{ - oscar.NewTLV(1, util.Word(0)), // TODO: user class - oscar.NewTLV(0x06, util.Dword(uint32(user.Status))), // TODO: User Status - // oscar.NewTLV(0x0a, util.Dword(binary.BigEndian.Uint32([]byte(OSCAR_HOST)))), // TODO: External IP of the client? - oscar.NewTLV(0x0f, util.Dword(uint32(time.Since(user.LastActivityAt).Seconds()))), // Idle Time - oscar.NewTLV(0x03, util.Dword(uint32(time.Now().Unix()))), // Client Signon Time - oscar.NewTLV(0x05, util.Dword(uint32(user.CreatedAt.Unix()))), // Member since - } + tlvs := []*oscar.TLV{ + oscar.NewTLV(1, util.Word(0)), // TODO: user class + oscar.NewTLV(0x06, util.Dword(uint32(user.Status))), + // oscar.NewTLV(0x0a, util.Dword(binary.BigEndian.Uint32([]byte(OSCAR_HOST)))), // TODO: External IP of the client? + oscar.NewTLV(0x0f, util.Dword(uint32(time.Since(user.LastActivityAt).Seconds()))), // Idle Time + oscar.NewTLV(0x03, util.Dword(uint32(time.Now().Unix()))), // Client Signon Time + oscar.NewTLV(0x05, util.Dword(uint32(user.CreatedAt.Unix()))), // Member since + } - onlineSnac.Data.WriteUint16(uint16(len(tlvs))) - for _, tlv := range tlvs { - onlineSnac.Data.WriteBinary(tlv) - } + onlineSnac.Data.WriteUint16(uint16(len(tlvs))) + for _, tlv := range tlvs { + onlineSnac.Data.WriteBinary(tlv) + } - onlineFlap := oscar.NewFLAP(2) - onlineFlap.Data.WriteBinary(onlineSnac) - if err := s.Send(onlineFlap); err != nil { - log.Printf("could not tell %s that %s is online", buddy.Source.ScreenName, user.ScreenName) + onlineFlap := oscar.NewFLAP(2) + onlineFlap.Data.WriteBinary(onlineSnac) + if err := s.Send(onlineFlap); err != nil { + log.Printf("could not tell %s that %s is online", buddy.Source.ScreenName, user.ScreenName) + } + } else if user.Status == models.UserStatusAway { + offlineSnac := oscar.NewSNAC(3, 0xc) + offlineSnac.Data.WriteLPString(user.ScreenName) + offlineSnac.Data.WriteUint16(0) // TODO: user warning level + tlvs := []*oscar.TLV{ + oscar.NewTLV(1, util.Word(0)), + } + offlineSnac.Data.WriteUint16(uint16(len(tlvs))) + for _, tlv := range tlvs { + offlineSnac.Data.WriteBinary(tlv) + } + + offlineFlap := oscar.NewFLAP(2) + offlineFlap.Data.WriteBinary(offlineSnac) + if err := s.Send(offlineFlap); err != nil { + log.Printf("could not tell %s that %s is offline", buddy.Source.ScreenName, user.ScreenName) + } } } } diff --git a/oscar/server.go b/oscar/server.go index ff6afa2..4413b4e 100644 --- a/oscar/server.go +++ b/oscar/server.go @@ -10,6 +10,7 @@ import ( "log" "net" "strings" + "time" ) type HandlerFunc func(context.Context, *FLAP) context.Context @@ -41,6 +42,9 @@ func (h *Handler) Handle(conn net.Conn) { session.GreetedClient = true } + // Never timeout + conn.SetReadDeadline(time.Time{}) + incoming := make([]byte, 512) n, err := conn.Read(incoming) if err != nil && err != io.EOF { diff --git a/services/0x03_buddy_list_management.go b/services/0x03_buddy_list_management.go index 2d0a5da..f9a9a6e 100644 --- a/services/0x03_buddy_list_management.go +++ b/services/0x03_buddy_list_management.go @@ -6,13 +6,16 @@ import ( "aim-oscar/oscar" "aim-oscar/util" "context" + "database/sql" "log" "github.com/pkg/errors" "github.com/uptrace/bun" ) -type BuddyListManagement struct{} +type BuddyListManagement struct { + OnlineCh chan *models.User +} func (b *BuddyListManagement) HandleSNAC(ctx context.Context, db *bun.DB, snac *oscar.SNAC) (context.Context, error) { session, _ := oscar.SessionFromContext(ctx) @@ -61,11 +64,23 @@ func (b *BuddyListManagement) HandleSNAC(ctx context.Context, db *bun.DB, snac * WithUIN: buddy.UIN, } + count, err := db.NewSelect().Model((*models.Buddy)(nil)).Where("source_uin = ?", user.UIN).Where("with_uin = ?", buddy.UIN).Count(ctx) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return ctx, err + } + + // Already buddies + if count > 0 { + return ctx, nil + } + _, err = db.NewInsert().Model(rel).Exec(ctx) if err != nil { return ctx, err } + b.OnlineCh <- buddy + log.Printf("%s added buddy %s to buddy list", user.ScreenName, buddyScreename) }