소스 검색

split up server and encoding/decoding logic

Mike 2 년 전
부모
커밋
5b67ab7055
22개의 변경된 파일1305개의 추가작업 그리고 1297개의 파일을 삭제
  1. 32 47
      cmd/main.go
  2. 3 3
      oscar/decode.go
  3. 3 3
      oscar/encode.go
  4. 113 0
      oscar/frames.go
  5. 0 665
      oscar/protocol.go
  6. 2 2
      oscar/snacs.go
  7. 2 2
      oscar/snacs_test.go
  8. 203 0
      oscar/tlv.go
  9. 43 42
      server/bucp.go
  10. 32 31
      server/buddy.go
  11. 48 43
      server/chat.go
  12. 60 59
      server/chat_nav.go
  13. 89 88
      server/feedbag.go
  14. 13 14
      server/feedbag_store.go
  15. 11 10
      server/feedbag_store_test.go
  16. 78 77
      server/icbm.go
  17. 76 75
      server/locate.go
  18. 83 82
      server/oservice.go
  19. 17 16
      server/pd.go
  20. 358 0
      server/protocol.go
  21. 38 37
      server/session.go
  22. 1 1
      server/session_test.go

+ 32 - 47
cmd/main.go

@@ -3,39 +3,38 @@ package main
 import (
 import (
 	"fmt"
 	"fmt"
 	"github.com/kelseyhightower/envconfig"
 	"github.com/kelseyhightower/envconfig"
-	"github.com/mkaminski/goaim/oscar"
+	"github.com/mkaminski/goaim/server"
 	"io"
 	"io"
 	"log"
 	"log"
 	"net"
 	"net"
-	"net/http"
 )
 )
 
 
 func main() {
 func main() {
 
 
-	var cfg oscar.Config
+	var cfg server.Config
 	err := envconfig.Process("", &cfg)
 	err := envconfig.Process("", &cfg)
 	if err != nil {
 	if err != nil {
 		log.Fatal(err.Error())
 		log.Fatal(err.Error())
 	}
 	}
 
 
-	fm, err := oscar.NewFeedbagStore(cfg.DBPath)
+	fm, err := server.NewFeedbagStore(cfg.DBPath)
 	if err != nil {
 	if err != nil {
 		log.Fatal(err)
 		log.Fatal(err)
 	}
 	}
 
 
-	sm := oscar.NewSessionManager()
-	cr := oscar.NewChatRegistry()
+	sm := server.NewSessionManager()
+	cr := server.NewChatRegistry()
 
 
 	go listenBOS(cfg, sm, fm, cr)
 	go listenBOS(cfg, sm, fm, cr)
 	go listenChat(cfg, fm, cr)
 	go listenChat(cfg, fm, cr)
 
 
-	listener, err := net.Listen("tcp", oscar.Address("", cfg.OSCARPort))
+	listener, err := net.Listen("tcp", server.Address("", cfg.OSCARPort))
 	if err != nil {
 	if err != nil {
 		log.Fatal(err)
 		log.Fatal(err)
 	}
 	}
 	defer listener.Close()
 	defer listener.Close()
 
 
-	fmt.Printf("OSCAR server listening on %s\n", oscar.Address(cfg.OSCARHost, cfg.OSCARPort))
+	fmt.Printf("OSCAR server listening on %s\n", server.Address(cfg.OSCARHost, cfg.OSCARPort))
 
 
 	for {
 	for {
 		conn, err := listener.Accept()
 		conn, err := listener.Accept()
@@ -48,25 +47,14 @@ func main() {
 	}
 	}
 }
 }
 
 
-func webServer(ch chan string) {
-	http.HandleFunc("/send-im", func(w http.ResponseWriter, r *http.Request) {
-		body, _ := io.ReadAll(r.Body)
-		ch <- string(body)
-	})
-
-	if err := http.ListenAndServe(":3333", nil); err != nil {
-		panic(err.Error())
-	}
-}
-
-func listenBOS(cfg oscar.Config, sm *oscar.SessionManager, fm *oscar.FeedbagStore, cr *oscar.ChatRegistry) {
-	listener, err := net.Listen("tcp", oscar.Address("", cfg.BOSPort))
+func listenBOS(cfg server.Config, sm *server.SessionManager, fm *server.FeedbagStore, cr *server.ChatRegistry) {
+	listener, err := net.Listen("tcp", server.Address("", cfg.BOSPort))
 	if err != nil {
 	if err != nil {
 		log.Fatal(err)
 		log.Fatal(err)
 	}
 	}
 	defer listener.Close()
 	defer listener.Close()
 
 
-	fmt.Printf("BOS server listening on %s\n", oscar.Address(cfg.OSCARHost, cfg.BOSPort))
+	fmt.Printf("BOS server listening on %s\n", server.Address(cfg.OSCARHost, cfg.BOSPort))
 
 
 	for {
 	for {
 		conn, err := listener.Accept()
 		conn, err := listener.Accept()
@@ -78,17 +66,16 @@ func listenBOS(cfg oscar.Config, sm *oscar.SessionManager, fm *oscar.FeedbagStor
 	}
 	}
 }
 }
 
 
-func listenChat(cfg oscar.Config, fm *oscar.FeedbagStore, cr *oscar.ChatRegistry) {
-	listener, err := net.Listen("tcp", oscar.Address("", cfg.ChatPort))
+func listenChat(cfg server.Config, fm *server.FeedbagStore, cr *server.ChatRegistry) {
+	listener, err := net.Listen("tcp", server.Address("", cfg.ChatPort))
 	if err != nil {
 	if err != nil {
 		log.Fatal(err)
 		log.Fatal(err)
 	}
 	}
 	defer listener.Close()
 	defer listener.Close()
 
 
-	fmt.Printf("Chat server listening on %s\n", oscar.Address(cfg.OSCARHost, cfg.ChatPort))
+	fmt.Printf("Chat server listening on %s\n", server.Address(cfg.OSCARHost, cfg.ChatPort))
 
 
 	for {
 	for {
-		// Accept incoming connections
 		conn, err := listener.Accept()
 		conn, err := listener.Accept()
 		if err != nil {
 		if err != nil {
 			log.Println(err)
 			log.Println(err)
@@ -98,10 +85,10 @@ func listenChat(cfg oscar.Config, fm *oscar.FeedbagStore, cr *oscar.ChatRegistry
 	}
 	}
 }
 }
 
 
-func handleAuthConnection(cfg oscar.Config, sm *oscar.SessionManager, fm *oscar.FeedbagStore, conn net.Conn) {
+func handleAuthConnection(cfg server.Config, sm *server.SessionManager, fm *server.FeedbagStore, conn net.Conn) {
 	defer conn.Close()
 	defer conn.Close()
 	seq := uint32(100)
 	seq := uint32(100)
-	_, err := oscar.SendAndReceiveSignonFrame(conn, &seq)
+	_, err := server.SendAndReceiveSignonFrame(conn, &seq)
 	if err != nil {
 	if err != nil {
 		log.Println(err)
 		log.Println(err)
 		return
 		return
@@ -112,21 +99,21 @@ func handleAuthConnection(cfg oscar.Config, sm *oscar.SessionManager, fm *oscar.
 		log.Fatal(err.Error())
 		log.Fatal(err.Error())
 	}
 	}
 
 
-	err = oscar.ReceiveAndSendAuthChallenge(sess, conn, conn, &seq)
+	err = server.ReceiveAndSendAuthChallenge(sess, conn, conn, &seq)
 	if err != nil {
 	if err != nil {
 		log.Println(err)
 		log.Println(err)
 		return
 		return
 	}
 	}
 
 
-	err = oscar.ReceiveAndSendBUCPLoginRequest(cfg, sess, fm, conn, conn, &seq)
+	err = server.ReceiveAndSendBUCPLoginRequest(cfg, sess, fm, conn, conn, &seq)
 	if err != nil {
 	if err != nil {
 		log.Println(err)
 		log.Println(err)
 		return
 		return
 	}
 	}
 }
 }
 
 
-func handleBOSConnection(cfg oscar.Config, sm *oscar.SessionManager, fm *oscar.FeedbagStore, cr *oscar.ChatRegistry, conn net.Conn) {
-	sess, seq, err := oscar.VerifyLogin(sm, conn)
+func handleBOSConnection(cfg server.Config, sm *server.SessionManager, fm *server.FeedbagStore, cr *server.ChatRegistry, conn net.Conn) {
+	sess, seq, err := server.VerifyLogin(sm, conn)
 	if err != nil {
 	if err != nil {
 		fmt.Printf("user disconnected with error: %s\n", err.Error())
 		fmt.Printf("user disconnected with error: %s\n", err.Error())
 		return
 		return
@@ -135,21 +122,19 @@ func handleBOSConnection(cfg oscar.Config, sm *oscar.SessionManager, fm *oscar.F
 	defer sess.Close()
 	defer sess.Close()
 	go func() {
 	go func() {
 		<-sess.Closed()
 		<-sess.Closed()
-		oscar.Signout(sess, sm, fm)
+		server.Signout(sess, sm, fm)
 		conn.Close()
 		conn.Close()
 	}()
 	}()
 
 
-	onClientReady := func(sess *oscar.Session, sm *oscar.SessionManager, r io.Reader, w io.Writer, sequence *uint32) error {
-		err := oscar.NotifyArrival(sess, sm, fm)
-		if err != nil {
+	onClientReady := func(sess *server.Session, sm *server.SessionManager, r io.Reader, w io.Writer, sequence *uint32) error {
+		if err := server.NotifyArrival(sess, sm, fm); err != nil {
 			return err
 			return err
 		}
 		}
-
-		return oscar.GetOnlineBuddies(w, sess, sm, fm, sequence)
+		return server.GetOnlineBuddies(w, sess, sm, fm, sequence)
 	}
 	}
 
 
 	foodGroups := []uint16{0x0001, 0x0002, 0x0003, 0x0004, 0x0009, 0x0013, 0x000D}
 	foodGroups := []uint16{0x0001, 0x0002, 0x0003, 0x0004, 0x0009, 0x0013, 0x000D}
-	if err := oscar.ReadBos(cfg, onClientReady, sess, seq, sm, fm, cr, conn, foodGroups); err != nil && err != io.EOF {
+	if err := server.ReadBos(cfg, onClientReady, sess, seq, sm, fm, cr, conn, foodGroups); err != nil && err != io.EOF {
 		if err != io.EOF {
 		if err != io.EOF {
 			fmt.Printf("user disconnected with error: %s\n", err.Error())
 			fmt.Printf("user disconnected with error: %s\n", err.Error())
 		} else {
 		} else {
@@ -158,8 +143,8 @@ func handleBOSConnection(cfg oscar.Config, sm *oscar.SessionManager, fm *oscar.F
 	}
 	}
 }
 }
 
 
-func handleChatConnection(cfg oscar.Config, fm *oscar.FeedbagStore, cr *oscar.ChatRegistry, conn net.Conn) {
-	cookie, seq, err := oscar.VerifyChatLogin(conn)
+func handleChatConnection(cfg server.Config, fm *server.FeedbagStore, cr *server.ChatRegistry, conn net.Conn) {
+	cookie, seq, err := server.VerifyChatLogin(conn)
 	if err != nil {
 	if err != nil {
 		fmt.Printf("user disconnected with error: %s\n", err.Error())
 		fmt.Printf("user disconnected with error: %s\n", err.Error())
 		return
 		return
@@ -176,7 +161,7 @@ func handleChatConnection(cfg oscar.Config, fm *oscar.FeedbagStore, cr *oscar.Ch
 	defer chatSess.Close()
 	defer chatSess.Close()
 	go func() {
 	go func() {
 		<-chatSess.Closed()
 		<-chatSess.Closed()
-		oscar.AlertUserLeft(chatSess, room.SessionManager)
+		server.AlertUserLeft(chatSess, room.SessionManager)
 		room.SessionManager.Remove(chatSess)
 		room.SessionManager.Remove(chatSess)
 		cr.MaybeRemoveRoom(room.ID)
 		cr.MaybeRemoveRoom(room.ID)
 		conn.Close()
 		conn.Close()
@@ -184,15 +169,15 @@ func handleChatConnection(cfg oscar.Config, fm *oscar.FeedbagStore, cr *oscar.Ch
 
 
 	foodGroups := []uint16{0x0001, 0x0002, 0x0003, 0x0004, 0x0009, 0x0013, 0x000D, 0x000E}
 	foodGroups := []uint16{0x0001, 0x0002, 0x0003, 0x0004, 0x0009, 0x0013, 0x000D, 0x000E}
 
 
-	onClientReady := func(sess *oscar.Session, sm *oscar.SessionManager, r io.Reader, w io.Writer, sequence *uint32) error {
-		if err := oscar.SendChatRoomInfoUpdate(room, w, sequence); err != nil {
+	onClientReady := func(sess *server.Session, sm *server.SessionManager, r io.Reader, w io.Writer, sequence *uint32) error {
+		if err := server.SendChatRoomInfoUpdate(room, w, sequence); err != nil {
 			return err
 			return err
 		}
 		}
-		oscar.AlertUserJoined(sess, sm)
-		return oscar.SetOnlineChatUsers(sm, w, sequence)
+		server.AlertUserJoined(sess, sm)
+		return server.SetOnlineChatUsers(sm, w, sequence)
 	}
 	}
 
 
-	if err := oscar.ReadBos(cfg, onClientReady, chatSess, seq, room.SessionManager, fm, cr, conn, foodGroups); err != nil && err != io.EOF {
+	if err := server.ReadBos(cfg, onClientReady, chatSess, seq, room.SessionManager, fm, cr, conn, foodGroups); err != nil && err != io.EOF {
 		if err != io.EOF {
 		if err != io.EOF {
 			fmt.Printf("user disconnected with error: %s\n", err.Error())
 			fmt.Printf("user disconnected with error: %s\n", err.Error())
 		} else {
 		} else {

+ 3 - 3
oscar/decode.go

@@ -18,19 +18,19 @@ func unmarshal(t reflect.Type, v reflect.Value, tag reflect.StructTag, r io.Read
 		switch v.Interface().(type) {
 		switch v.Interface().(type) {
 		case TLVRestBlock:
 		case TLVRestBlock:
 			val := TLVRestBlock{}
 			val := TLVRestBlock{}
-			if err := val.read(r); err != nil {
+			if err := val.Read(r); err != nil {
 				return err
 				return err
 			}
 			}
 			v.Set(reflect.ValueOf(val))
 			v.Set(reflect.ValueOf(val))
 		case TLVLBlock:
 		case TLVLBlock:
 			val := TLVLBlock{}
 			val := TLVLBlock{}
-			if err := val.read(r); err != nil {
+			if err := val.Read(r); err != nil {
 				return err
 				return err
 			}
 			}
 			v.Set(reflect.ValueOf(val))
 			v.Set(reflect.ValueOf(val))
 		case TLVBlock:
 		case TLVBlock:
 			val := TLVBlock{}
 			val := TLVBlock{}
-			if err := val.read(r); err != nil {
+			if err := val.Read(r); err != nil {
 				return err
 				return err
 			}
 			}
 			v.Set(reflect.ValueOf(val))
 			v.Set(reflect.ValueOf(val))

+ 3 - 3
oscar/encode.go

@@ -17,15 +17,15 @@ func marshal(t reflect.Type, v reflect.Value, tag reflect.StructTag, w io.Writer
 	case reflect.Struct:
 	case reflect.Struct:
 		switch sVal := v.Interface().(type) {
 		switch sVal := v.Interface().(type) {
 		case TLVRestBlock:
 		case TLVRestBlock:
-			if err := sVal.writeTLV(w); err != nil {
+			if err := sVal.WriteTLV(w); err != nil {
 				return err
 				return err
 			}
 			}
 		case TLVLBlock:
 		case TLVLBlock:
-			if err := sVal.writeTLV(w); err != nil {
+			if err := sVal.WriteTLV(w); err != nil {
 				return err
 				return err
 			}
 			}
 		case TLVBlock:
 		case TLVBlock:
-			if err := sVal.writeTLV(w); err != nil {
+			if err := sVal.WriteTLV(w); err != nil {
 				return err
 				return err
 			}
 			}
 		default:
 		default:

+ 113 - 0
oscar/frames.go

@@ -0,0 +1,113 @@
+package oscar
+
+import (
+	"bytes"
+	"encoding/binary"
+	"io"
+)
+
+type SnacError struct {
+	Code uint16
+	TLVRestBlock
+}
+
+type FlapFrame struct {
+	StartMarker   uint8
+	FrameType     uint8
+	Sequence      uint16
+	PayloadLength uint16
+}
+
+func (f FlapFrame) Write(w io.Writer) error {
+	if err := binary.Write(w, binary.BigEndian, f.StartMarker); err != nil {
+		return err
+	}
+	if err := binary.Write(w, binary.BigEndian, f.FrameType); err != nil {
+		return err
+	}
+	if err := binary.Write(w, binary.BigEndian, f.Sequence); err != nil {
+		return err
+	}
+	return binary.Write(w, binary.BigEndian, f.PayloadLength)
+}
+
+func (f *FlapFrame) Read(r io.Reader) error {
+	if err := binary.Read(r, binary.BigEndian, &f.StartMarker); err != nil {
+		return err
+	}
+	if err := binary.Read(r, binary.BigEndian, &f.FrameType); err != nil {
+		return err
+	}
+	if err := binary.Read(r, binary.BigEndian, &f.Sequence); err != nil {
+		return err
+	}
+	return binary.Read(r, binary.BigEndian, &f.PayloadLength)
+}
+
+type SnacFrame struct {
+	FoodGroup uint16
+	SubGroup  uint16
+	Flags     uint16
+	RequestID uint32
+}
+
+func (s SnacFrame) Write(w io.Writer) error {
+	if err := binary.Write(w, binary.BigEndian, s.FoodGroup); err != nil {
+		return err
+	}
+	if err := binary.Write(w, binary.BigEndian, s.SubGroup); err != nil {
+		return err
+	}
+	if err := binary.Write(w, binary.BigEndian, s.Flags); err != nil {
+		return err
+	}
+	if err := binary.Write(w, binary.BigEndian, s.RequestID); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (s *SnacFrame) Read(r io.Reader) error {
+	if err := binary.Read(r, binary.BigEndian, &s.FoodGroup); err != nil {
+		return err
+	}
+	if err := binary.Read(r, binary.BigEndian, &s.SubGroup); err != nil {
+		return err
+	}
+	if err := binary.Read(r, binary.BigEndian, &s.Flags); err != nil {
+		return err
+	}
+	return binary.Read(r, binary.BigEndian, &s.RequestID)
+}
+
+type FlapSignonFrame struct {
+	FlapFrame
+	FlapVersion uint32
+	TLVRestBlock
+}
+
+func (f FlapSignonFrame) Write(w io.Writer) error {
+	if err := f.FlapFrame.Write(w); err != nil {
+		return err
+	}
+	return binary.Write(w, binary.BigEndian, f.FlapVersion)
+}
+
+func (f *FlapSignonFrame) Read(r io.Reader) error {
+	if err := f.FlapFrame.Read(r); err != nil {
+		return err
+	}
+
+	// todo: combine b+buf?
+	b := make([]byte, f.PayloadLength)
+	if _, err := r.Read(b); err != nil {
+		return err
+	}
+
+	buf := bytes.NewBuffer(b)
+	if err := binary.Read(buf, binary.BigEndian, &f.FlapVersion); err != nil {
+		return err
+	}
+
+	return f.TLVRestBlock.Read(buf)
+}

+ 0 - 665
oscar/protocol.go

@@ -1,665 +0,0 @@
-package oscar
-
-import (
-	"bytes"
-	"encoding/binary"
-	"errors"
-	"fmt"
-	"github.com/google/uuid"
-	"io"
-)
-
-const (
-	ErrorCodeInvalidSnac          uint16 = 0x01
-	ErrorCodeRateToHost           uint16 = 0x02
-	ErrorCodeRateToClient         uint16 = 0x03
-	ErrorCodeNotLoggedOn          uint16 = 0x04
-	ErrorCodeServiceUnavailable   uint16 = 0x05
-	ErrorCodeServiceNotDefined    uint16 = 0x06
-	ErrorCodeObsoleteSnac         uint16 = 0x07
-	ErrorCodeNotSupportedByHost   uint16 = 0x08
-	ErrorCodeNotSupportedByClient uint16 = 0x09
-	ErrorCodeRefusedByClient      uint16 = 0x0A
-	ErrorCodeReplyTooBig          uint16 = 0x0B
-	ErrorCodeResponsesLost        uint16 = 0x0C
-	ErrorCodeRequestDenied        uint16 = 0x0D
-	ErrorCodeBustedSnacPayload    uint16 = 0x0E
-	ErrorCodeInsufficientRights   uint16 = 0x0F
-	ErrorCodeInLocalPermitDeny    uint16 = 0x10
-	ErrorCodeTooEvilSender        uint16 = 0x11
-	ErrorCodeTooEvilReceiver      uint16 = 0x12
-	ErrorCodeUserTempUnavail      uint16 = 0x13
-	ErrorCodeNoMatch              uint16 = 0x14
-	ErrorCodeListOverflow         uint16 = 0x15
-	ErrorCodeRequestAmbigous      uint16 = 0x16
-	ErrorCodeQueueFull            uint16 = 0x17
-	ErrorCodeNotWhileOnAol        uint16 = 0x18
-	ErrorCodeQueryFail            uint16 = 0x19
-	ErrorCodeTimeout              uint16 = 0x1A
-	ErrorCodeErrorText            uint16 = 0x1B
-	ErrorCodeGeneralFailure       uint16 = 0x1C
-	ErrorCodeProgress             uint16 = 0x1D
-	ErrorCodeInFreeArea           uint16 = 0x1E
-	ErrorCodeRestrictedByPc       uint16 = 0x1F
-	ErrorCodeRemoteRestrictedByPc uint16 = 0x20
-)
-
-const (
-	ErrorTagsFailUrl        = 0x04
-	ErrorTagsErrorSubcode   = 0x08
-	ErrorTagsErrorText      = 0x1B
-	ErrorTagsErrorInfoClsid = 0x29
-	ErrorTagsErrorInfoData  = 0x2A
-)
-
-var (
-	CapChat, _ = uuid.MustParse("748F2420-6287-11D1-8222-444553540000").MarshalBinary()
-)
-
-type Config struct {
-	OSCARHost string `envconfig:"OSCAR_HOST" required:"true"`
-	OSCARPort int    `envconfig:"OSCAR_PORT" default:"5190"`
-	BOSPort   int    `envconfig:"BOS_PORT" default:"5191"`
-	ChatPort  int    `envconfig:"CHAT_PORT" default:"5192"`
-	DBPath    string `envconfig:"DB_PATH" required:"true"`
-}
-
-func Address(host string, port int) string {
-	return fmt.Sprintf("%s:%d", host, port)
-}
-
-type snacError struct {
-	Code uint16
-	TLVRestBlock
-}
-
-type flapFrame struct {
-	startMarker   uint8
-	frameType     uint8
-	sequence      uint16
-	payloadLength uint16
-}
-
-const (
-	TLV_SCREEN_NAME = 0x01
-)
-
-func (f flapFrame) write(w io.Writer) error {
-	if err := binary.Write(w, binary.BigEndian, f.startMarker); err != nil {
-		return err
-	}
-	if err := binary.Write(w, binary.BigEndian, f.frameType); err != nil {
-		return err
-	}
-	if err := binary.Write(w, binary.BigEndian, f.sequence); err != nil {
-		return err
-	}
-	return binary.Write(w, binary.BigEndian, f.payloadLength)
-}
-
-func (f *flapFrame) read(r io.Reader) error {
-	if err := binary.Read(r, binary.BigEndian, &f.startMarker); err != nil {
-		return err
-	}
-	if err := binary.Read(r, binary.BigEndian, &f.frameType); err != nil {
-		return err
-	}
-	if err := binary.Read(r, binary.BigEndian, &f.sequence); err != nil {
-		return err
-	}
-	return binary.Read(r, binary.BigEndian, &f.payloadLength)
-}
-
-type snacFrame struct {
-	foodGroup uint16
-	subGroup  uint16
-	flags     uint16
-	requestID uint32
-}
-
-func (s snacFrame) write(w io.Writer) error {
-	if err := binary.Write(w, binary.BigEndian, s.foodGroup); err != nil {
-		return err
-	}
-	if err := binary.Write(w, binary.BigEndian, s.subGroup); err != nil {
-		return err
-	}
-	if err := binary.Write(w, binary.BigEndian, s.flags); err != nil {
-		return err
-	}
-	if err := binary.Write(w, binary.BigEndian, s.requestID); err != nil {
-		return err
-	}
-	return nil
-}
-
-func (s *snacFrame) read(r io.Reader) error {
-	if err := binary.Read(r, binary.BigEndian, &s.foodGroup); err != nil {
-		return err
-	}
-	if err := binary.Read(r, binary.BigEndian, &s.subGroup); err != nil {
-		return err
-	}
-	if err := binary.Read(r, binary.BigEndian, &s.flags); err != nil {
-		return err
-	}
-	return binary.Read(r, binary.BigEndian, &s.requestID)
-}
-
-type TLVRestBlock struct {
-	TLVList
-}
-
-// read consumes the remainder of the read buffer
-func (s *TLVRestBlock) read(r io.Reader) error {
-	for {
-		tlv := TLV{}
-		if err := tlv.read(r); err != nil {
-			if err == io.EOF {
-				return nil
-			}
-			return err
-		}
-		s.TLVList = append(s.TLVList, tlv)
-	}
-	return nil
-}
-
-type TLVBlock struct {
-	TLVList
-}
-
-// read consumes up to n TLVs, as specified in payload
-func (s *TLVBlock) read(r io.Reader) error {
-	var tlvCount uint16
-	if err := binary.Read(r, binary.BigEndian, &tlvCount); err != nil {
-		return err
-	}
-	if tlvCount == 0 {
-		return nil
-	}
-	for i := uint16(0); i < tlvCount; i++ {
-		tlv := TLV{}
-		if err := tlv.read(r); err != nil {
-			return err
-		}
-		s.TLVList = append(s.TLVList, tlv)
-	}
-	return nil
-}
-
-func (s TLVBlock) writeTLV(w io.Writer) error {
-	if err := binary.Write(w, binary.BigEndian, uint16(len(s.TLVList))); err != nil {
-		return err
-	}
-	return s.TLVList.writeTLV(w)
-}
-
-type TLVLBlock struct {
-	TLVList
-}
-
-// read consumes up to n bytes, as specified in the payload
-func (s *TLVLBlock) read(r io.Reader) error {
-	var tlvLen uint16
-	if err := binary.Read(r, binary.BigEndian, &tlvLen); err != nil {
-		return err
-	}
-	p := make([]byte, tlvLen)
-	if _, err := r.Read(p); err != nil {
-		return err
-	}
-	buf := bytes.NewBuffer(p)
-	for buf.Len() > 0 {
-		tlv := TLV{}
-		if err := tlv.read(buf); err != nil {
-			return err
-		}
-		s.TLVList = append(s.TLVList, tlv)
-	}
-	return nil
-}
-
-func (s TLVLBlock) writeTLV(w io.Writer) error {
-	buf := &bytes.Buffer{}
-	if err := s.TLVList.writeTLV(buf); err != nil {
-		return err
-	}
-	if err := binary.Write(w, binary.BigEndian, uint16(buf.Len())); err != nil {
-		return err
-	}
-	_, err := w.Write(buf.Bytes())
-	return err
-}
-
-type TLVList []TLV
-
-func (s *TLVList) addTLV(tlv TLV) {
-	*s = append(*s, tlv)
-}
-
-func (s TLVList) writeTLV(w io.Writer) error {
-	for _, tlv := range s {
-		if err := tlv.writeTLV(w); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func (s TLVList) getString(tType uint16) (string, bool) {
-	for _, tlv := range s {
-		if tType == tlv.tType {
-			return string(tlv.val.([]byte)), true
-		}
-	}
-	return "", false
-}
-
-func (s TLVList) getTLV(tType uint16) (TLV, bool) {
-	for _, tlv := range s {
-		if tType == tlv.tType {
-			return tlv, true
-		}
-	}
-	return TLV{}, false
-}
-
-func (s TLVList) getSlice(tType uint16) ([]byte, bool) {
-	for _, tlv := range s {
-		if tType == tlv.tType {
-			return tlv.val.([]byte), true
-		}
-	}
-	return nil, false
-}
-
-func (s TLVList) getUint32(tType uint16) (uint32, bool) {
-	for _, tlv := range s {
-		if tType == tlv.tType {
-			return binary.BigEndian.Uint32(tlv.val.([]byte)), true
-		}
-	}
-	return 0, false
-}
-
-type TLV struct {
-	tType uint16
-	val   any
-}
-
-func (t TLV) writeTLV(w io.Writer) error {
-	if err := binary.Write(w, binary.BigEndian, t.tType); err != nil {
-		return err
-	}
-
-	var valLen uint16
-	val := t.val
-
-	switch t.val.(type) {
-	case uint8:
-		valLen = 1
-	case uint16:
-		valLen = 2
-	case uint32:
-		valLen = 4
-	case []uint16:
-		valLen = uint16(len(t.val.([]uint16)) * 2)
-	case []byte:
-		valLen = uint16(len(t.val.([]byte)))
-	case string:
-		valLen = uint16(len(t.val.(string)))
-		val = []byte(t.val.(string))
-	default:
-		// move this logic to SNAC_0x0E_0x04_ChatUsersLeft
-		buf := &bytes.Buffer{}
-		if err := Marshal(t.val, buf); err != nil {
-			return err
-		}
-		valLen = uint16(buf.Len())
-		val = buf.Bytes()
-	}
-
-	if err := binary.Write(w, binary.BigEndian, valLen); err != nil {
-		return err
-	}
-
-	return binary.Write(w, binary.BigEndian, val)
-}
-
-func (t *TLV) read(r io.Reader) error {
-	if err := binary.Read(r, binary.BigEndian, &t.tType); err != nil {
-		return err
-	}
-	var tlvValLen uint16
-	if err := binary.Read(r, binary.BigEndian, &tlvValLen); err != nil {
-		return err
-	}
-	buf := make([]byte, tlvValLen)
-	if _, err := r.Read(buf); err != nil {
-		return err
-	}
-	t.val = buf
-	return nil
-}
-
-type flapSignonFrame struct {
-	flapFrame
-	flapVersion uint32
-	TLVRestBlock
-}
-
-func (f flapSignonFrame) write(w io.Writer) error {
-	if err := f.flapFrame.write(w); err != nil {
-		return err
-	}
-	return binary.Write(w, binary.BigEndian, f.flapVersion)
-}
-
-func (f *flapSignonFrame) read(r io.Reader) error {
-	if err := f.flapFrame.read(r); err != nil {
-		return err
-	}
-
-	// todo: combine b+buf?
-	b := make([]byte, f.payloadLength)
-	if _, err := r.Read(b); err != nil {
-		return err
-	}
-
-	buf := bytes.NewBuffer(b)
-	if err := binary.Read(buf, binary.BigEndian, &f.flapVersion); err != nil {
-		return err
-	}
-
-	return f.TLVRestBlock.read(buf)
-}
-
-func SendAndReceiveSignonFrame(rw io.ReadWriter, sequence *uint32) (*flapSignonFrame, error) {
-	flapOut := flapSignonFrame{
-		flapFrame: flapFrame{
-			startMarker:   42,
-			frameType:     1,
-			sequence:      uint16(*sequence),
-			payloadLength: 4, // size of flapVersion
-		},
-		flapVersion: 1,
-	}
-
-	if err := flapOut.write(rw); err != nil {
-		return nil, err
-	}
-
-	fmt.Printf("SendAndReceiveSignonFrame read FLAP: %+v\n", flapOut)
-
-	// receive
-	flapIn := flapSignonFrame{}
-	if err := flapIn.read(rw); err != nil {
-		return nil, err
-	}
-
-	fmt.Printf("SendAndReceiveSignonFrame write FLAP: %+v\n", flapIn)
-
-	*sequence++
-
-	return &flapIn, nil
-}
-
-func VerifyLogin(sm *SessionManager, rw io.ReadWriter) (*Session, uint32, error) {
-	seq := uint32(100)
-	fmt.Println("VerifyLogin...")
-
-	flap, err := SendAndReceiveSignonFrame(rw, &seq)
-	if err != nil {
-		return nil, 0, err
-	}
-
-	var ok bool
-	ID, ok := flap.getSlice(OserviceTlvTagsLoginCookie)
-	if !ok {
-		return nil, 0, errors.New("unable to get session ID from payload")
-	}
-
-	sess, ok := sm.Retrieve(string(ID))
-	if !ok {
-		return nil, 0, fmt.Errorf("unable to find session by ID %s", ID)
-	}
-
-	return sess, seq, nil
-}
-
-func VerifyChatLogin(rw io.ReadWriter) (*ChatCookie, uint32, error) {
-	seq := uint32(100)
-	fmt.Println("VerifyChatLogin...")
-
-	flap, err := SendAndReceiveSignonFrame(rw, &seq)
-	if err != nil {
-		return nil, 0, err
-	}
-
-	var ok bool
-	buf, ok := flap.getSlice(OserviceTlvTagsLoginCookie)
-	if !ok {
-		return nil, 0, errors.New("unable to get session ID from payload")
-	}
-
-	cookie := ChatCookie{}
-	err = cookie.read(bytes.NewBuffer(buf))
-
-	return &cookie, seq, err
-}
-
-const (
-	OSERVICE      uint16 = 0x0001
-	LOCATE               = 0x0002
-	BUDDY                = 0x0003
-	ICBM                 = 0x0004
-	ADVERT               = 0x0005
-	INVITE               = 0x0006
-	ADMIN                = 0x0007
-	POPUP                = 0x0008
-	PD                   = 0x0009
-	USER_LOOKUP          = 0x000A
-	STATS                = 0x000B
-	TRANSLATE            = 0x000C
-	CHAT_NAV             = 0x000D
-	CHAT                 = 0x000E
-	ODIR                 = 0x000F
-	BART                 = 0x0010
-	FEEDBAG              = 0x0013
-	ICQ                  = 0x0015
-	BUCP                 = 0x0017
-	ALERT                = 0x0018
-	PLUGIN               = 0x0022
-	UNNAMED_FG_24        = 0x0024
-	MDIR                 = 0x0025
-	ARS                  = 0x044A
-)
-
-type IncomingMessage struct {
-	flap flapFrame
-	snac snacFrame
-	buf  io.Reader
-}
-
-type XMessage struct {
-	snacFrame snacFrame
-	snacOut   any
-}
-
-const (
-	FlapFrameSignon    uint8 = 0x01
-	FlapFrameData            = 0x02
-	FlapFrameError           = 0x03
-	FlapFrameSignoff         = 0x04
-	FlapFrameKeepAlive       = 0x05
-)
-
-func readIncomingRequests(rw io.Reader, msCh chan IncomingMessage, errCh chan error) {
-	defer close(msCh)
-	defer close(errCh)
-
-	for {
-		flap := flapFrame{}
-		if err := flap.read(rw); err != nil {
-			errCh <- err
-			return
-		}
-
-		switch flap.frameType {
-		case FlapFrameSignon:
-			errCh <- errors.New("shouldn't get FlapFrameSignon")
-			return
-		case FlapFrameData:
-			b := make([]byte, flap.payloadLength)
-			if _, err := rw.Read(b); err != nil {
-				errCh <- err
-				return
-			}
-
-			buf := bytes.NewBuffer(b)
-
-			snac := snacFrame{}
-			if err := snac.read(buf); err != nil {
-				errCh <- err
-				return
-			}
-
-			msCh <- IncomingMessage{
-				flap: flap,
-				snac: snac,
-				buf:  buf,
-			}
-		case FlapFrameError:
-			errCh <- fmt.Errorf("got FlapFrameError: %v", flap)
-			return
-		case FlapFrameSignoff:
-			errCh <- fmt.Errorf("got signoff: %v", flap)
-			return
-		case FlapFrameKeepAlive:
-			fmt.Println("keepalive heartbeat")
-		default:
-			errCh <- fmt.Errorf("unknown frame type: %v", flap)
-			return
-		}
-	}
-}
-
-func Signout(sess *Session, sm *SessionManager, fm *FeedbagStore) {
-	if err := NotifyDeparture(sess, sm, fm); err != nil {
-		fmt.Printf("error notifying departure: %s", err.Error())
-	}
-	sm.Remove(sess)
-}
-
-func ReadBos(cfg Config, ready OnReadyCB, sess *Session, seq uint32, sm *SessionManager, fm *FeedbagStore, cr *ChatRegistry, rwc io.ReadWriter, foodGroups []uint16) error {
-	if err := WriteOServiceHostOnline(foodGroups, rwc, &seq); err != nil {
-		return err
-	}
-
-	// buffered so that the go routine has room to exit
-	msgCh := make(chan IncomingMessage, 1)
-	errCh := make(chan error, 1)
-	go readIncomingRequests(rwc, msgCh, errCh)
-
-	for {
-		select {
-		case m := <-msgCh:
-			if err := routeIncomingRequests(cfg, ready, sm, sess, fm, cr, rwc, &seq, m.snac, m.flap, m.buf); err != nil {
-				return err
-			}
-		case m := <-sess.RecvMessage():
-			if err := writeOutSNAC(snacFrame{}, m.snacFrame, m.snacOut, &seq, rwc); err != nil {
-				return err
-			}
-		case err := <-errCh:
-			return err
-		}
-	}
-}
-
-func routeIncomingRequests(cfg Config, ready OnReadyCB, sm *SessionManager, sess *Session, fm *FeedbagStore, cr *ChatRegistry, rw io.ReadWriter, sequence *uint32, snac snacFrame, flap flapFrame, buf io.Reader) error {
-	switch snac.foodGroup {
-	case OSERVICE:
-		if err := routeOService(cfg, ready, cr, sm, fm, sess, snac, buf, rw, sequence); err != nil {
-			return err
-		}
-	case LOCATE:
-		if err := routeLocate(sess, sm, fm, snac, buf, rw, sequence); err != nil {
-			return err
-		}
-	case BUDDY:
-		if err := routeBuddy(snac, buf, rw, sequence); err != nil {
-			return err
-		}
-	case ICBM:
-		if err := routeICBM(sm, fm, sess, snac, buf, rw, sequence); err != nil {
-			return err
-		}
-	case PD:
-		if err := routePD(snac, buf, rw, sequence); err != nil {
-			return err
-		}
-	case CHAT_NAV:
-		if err := routeChatNav(sess, cr, snac, buf, rw, sequence); err != nil {
-			return err
-		}
-	case FEEDBAG:
-		if err := routeFeedbag(sm, sess, fm, snac, buf, rw, sequence); err != nil {
-			return err
-		}
-	case BUCP:
-		if err := routeBUCP(snac); err != nil {
-			return err
-		}
-	case CHAT:
-		if err := routeChat(sess, sm, snac, buf, rw, sequence); err != nil {
-			return err
-		}
-	default:
-		panic(fmt.Sprintf("unsupported food group: %d", snac.foodGroup))
-	}
-
-	return nil
-}
-
-func writeOutSNAC(originsnac snacFrame, snacFrame snacFrame, snacOut any, sequence *uint32, w io.Writer) error {
-	if originsnac.requestID != 0 {
-		snacFrame.requestID = originsnac.requestID
-	}
-
-	snacBuf := &bytes.Buffer{}
-	if err := snacFrame.write(snacBuf); err != nil {
-		return err
-	}
-	if err := Marshal(snacOut, snacBuf); err != nil {
-		return err
-	}
-
-	flap := flapFrame{
-		startMarker:   42,
-		frameType:     2,
-		sequence:      uint16(*sequence),
-		payloadLength: uint16(snacBuf.Len()),
-	}
-
-	fmt.Printf(" write FLAP: %+v\n", flap)
-
-	if err := flap.write(w); err != nil {
-		return err
-	}
-
-	fmt.Printf(" write SNAC: %+v\n", snacOut)
-
-	expectLen := snacBuf.Len()
-	c, err := w.Write(snacBuf.Bytes())
-	if err != nil {
-		return err
-	}
-	if c != expectLen {
-		panic("did not write the expected # of bytes")
-	}
-
-	*sequence++
-	return nil
-}

+ 2 - 2
oscar/snacs.go

@@ -42,8 +42,8 @@ type SNAC_0x01_0x07_OServiceRateParamsReply struct {
 		DisconnectLevel uint32
 		DisconnectLevel uint32
 		CurrentLevel    uint32
 		CurrentLevel    uint32
 		MaxLevel        uint32
 		MaxLevel        uint32
-		LastTime        uint32 // protocol v2 only
-		CurrentState    uint8  // protocol v2 only
+		LastTime        uint32
+		CurrentState    uint8
 	} `count_prefix:"uint16"`
 	} `count_prefix:"uint16"`
 	RateGroups []struct {
 	RateGroups []struct {
 		ID    uint16
 		ID    uint16

+ 2 - 2
oscar/snacs_test.go

@@ -37,8 +37,8 @@ func TestUnmarshal(t *testing.T) {
 		TLVRestBlock: TLVRestBlock{
 		TLVRestBlock: TLVRestBlock{
 			TLVList: TLVList{
 			TLVList: TLVList{
 				{
 				{
-					tType: 0x0B,
-					val:   []byte{1, 2, 3, 4},
+					TType: 0x0B,
+					Val:   []byte{1, 2, 3, 4},
 				},
 				},
 			},
 			},
 		},
 		},

+ 203 - 0
oscar/tlv.go

@@ -0,0 +1,203 @@
+package oscar
+
+import (
+	"bytes"
+	"encoding/binary"
+	"io"
+)
+
+type TLVRestBlock struct {
+	TLVList
+}
+
+// read consumes the remainder of the read buffer
+func (s *TLVRestBlock) Read(r io.Reader) error {
+	for {
+		tlv := TLV{}
+		if err := tlv.Read(r); err != nil {
+			if err == io.EOF {
+				return nil
+			}
+			return err
+		}
+		s.TLVList = append(s.TLVList, tlv)
+	}
+	return nil
+}
+
+type TLVBlock struct {
+	TLVList
+}
+
+// read consumes up to n TLVs, as specified in payload
+func (s *TLVBlock) Read(r io.Reader) error {
+	var tlvCount uint16
+	if err := binary.Read(r, binary.BigEndian, &tlvCount); err != nil {
+		return err
+	}
+	if tlvCount == 0 {
+		return nil
+	}
+	for i := uint16(0); i < tlvCount; i++ {
+		tlv := TLV{}
+		if err := tlv.Read(r); err != nil {
+			return err
+		}
+		s.TLVList = append(s.TLVList, tlv)
+	}
+	return nil
+}
+
+func (s TLVBlock) WriteTLV(w io.Writer) error {
+	if err := binary.Write(w, binary.BigEndian, uint16(len(s.TLVList))); err != nil {
+		return err
+	}
+	return s.TLVList.WriteTLV(w)
+}
+
+type TLVLBlock struct {
+	TLVList
+}
+
+// read consumes up to n bytes, as specified in the payload
+func (s *TLVLBlock) Read(r io.Reader) error {
+	var tlvLen uint16
+	if err := binary.Read(r, binary.BigEndian, &tlvLen); err != nil {
+		return err
+	}
+	p := make([]byte, tlvLen)
+	if _, err := r.Read(p); err != nil {
+		return err
+	}
+	buf := bytes.NewBuffer(p)
+	for buf.Len() > 0 {
+		tlv := TLV{}
+		if err := tlv.Read(buf); err != nil {
+			return err
+		}
+		s.TLVList = append(s.TLVList, tlv)
+	}
+	return nil
+}
+
+func (s TLVLBlock) WriteTLV(w io.Writer) error {
+	buf := &bytes.Buffer{}
+	if err := s.TLVList.WriteTLV(buf); err != nil {
+		return err
+	}
+	if err := binary.Write(w, binary.BigEndian, uint16(buf.Len())); err != nil {
+		return err
+	}
+	_, err := w.Write(buf.Bytes())
+	return err
+}
+
+type TLVList []TLV
+
+func (s *TLVList) AddTLV(tlv TLV) {
+	*s = append(*s, tlv)
+}
+
+func (s TLVList) WriteTLV(w io.Writer) error {
+	for _, tlv := range s {
+		if err := tlv.WriteTLV(w); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (s TLVList) GetString(tType uint16) (string, bool) {
+	for _, tlv := range s {
+		if tType == tlv.TType {
+			return string(tlv.Val.([]byte)), true
+		}
+	}
+	return "", false
+}
+
+func (s TLVList) GetTLV(tType uint16) (TLV, bool) {
+	for _, tlv := range s {
+		if tType == tlv.TType {
+			return tlv, true
+		}
+	}
+	return TLV{}, false
+}
+
+func (s TLVList) GetSlice(tType uint16) ([]byte, bool) {
+	for _, tlv := range s {
+		if tType == tlv.TType {
+			return tlv.Val.([]byte), true
+		}
+	}
+	return nil, false
+}
+
+func (s TLVList) GetUint32(tType uint16) (uint32, bool) {
+	for _, tlv := range s {
+		if tType == tlv.TType {
+			return binary.BigEndian.Uint32(tlv.Val.([]byte)), true
+		}
+	}
+	return 0, false
+}
+
+type TLV struct {
+	TType uint16
+	Val   any
+}
+
+func (t TLV) WriteTLV(w io.Writer) error {
+	if err := binary.Write(w, binary.BigEndian, t.TType); err != nil {
+		return err
+	}
+
+	var valLen uint16
+	val := t.Val
+
+	switch t.Val.(type) {
+	case uint8:
+		valLen = 1
+	case uint16:
+		valLen = 2
+	case uint32:
+		valLen = 4
+	case []uint16:
+		valLen = uint16(len(t.Val.([]uint16)) * 2)
+	case []byte:
+		valLen = uint16(len(t.Val.([]byte)))
+	case string:
+		valLen = uint16(len(t.Val.(string)))
+		val = []byte(t.Val.(string))
+	default:
+		buf := &bytes.Buffer{}
+		if err := Marshal(t.Val, buf); err != nil {
+			return err
+		}
+		valLen = uint16(buf.Len())
+		val = buf.Bytes()
+	}
+
+	if err := binary.Write(w, binary.BigEndian, valLen); err != nil {
+		return err
+	}
+
+	return binary.Write(w, binary.BigEndian, val)
+}
+
+func (t *TLV) Read(r io.Reader) error {
+	if err := binary.Read(r, binary.BigEndian, &t.TType); err != nil {
+		return err
+	}
+	var tlvValLen uint16
+	if err := binary.Read(r, binary.BigEndian, &tlvValLen); err != nil {
+		return err
+	}
+	buf := make([]byte, tlvValLen)
+	if _, err := r.Read(buf); err != nil {
+		return err
+	}
+	t.Val = buf
+	return nil
+}

+ 43 - 42
oscar/bucp.go → server/bucp.go

@@ -1,9 +1,10 @@
-package oscar
+package server
 
 
 import (
 import (
 	"bytes"
 	"bytes"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"github.com/mkaminski/goaim/oscar"
 	"io"
 	"io"
 )
 )
 
 
@@ -17,8 +18,8 @@ const (
 	BUCPRegistrationImageRequest        = 0x000C
 	BUCPRegistrationImageRequest        = 0x000C
 )
 )
 
 
-func routeBUCP(snac snacFrame) error {
-	switch snac.subGroup {
+func routeBUCP(snac oscar.SnacFrame) error {
+	switch snac.SubGroup {
 	case BUCPErr:
 	case BUCPErr:
 		panic("not implemented")
 		panic("not implemented")
 	case BUCPLoginRequest:
 	case BUCPLoginRequest:
@@ -39,34 +40,34 @@ func routeBUCP(snac snacFrame) error {
 }
 }
 
 
 func ReceiveAndSendAuthChallenge(s *Session, r io.Reader, w io.Writer, sequence *uint32) error {
 func ReceiveAndSendAuthChallenge(s *Session, r io.Reader, w io.Writer, sequence *uint32) error {
-	flap := flapFrame{}
-	if err := flap.read(r); err != nil {
+	flap := oscar.FlapFrame{}
+	if err := flap.Read(r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	b := make([]byte, flap.payloadLength)
+	b := make([]byte, flap.PayloadLength)
 	if _, err := r.Read(b); err != nil {
 	if _, err := r.Read(b); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	buf := bytes.NewBuffer(b)
 	buf := bytes.NewBuffer(b)
-	snac := snacFrame{}
-	if err := snac.read(buf); err != nil {
+	snac := oscar.SnacFrame{}
+	if err := snac.Read(buf); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	snacPayloadIn := SNAC_0x17_0x06_BUCPChallengeRequest{}
-	if err := Unmarshal(&snacPayloadIn, buf); err != nil {
+	snacPayloadIn := oscar.SNAC_0x17_0x06_BUCPChallengeRequest{}
+	if err := oscar.Unmarshal(&snacPayloadIn, buf); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	fmt.Printf("ReceiveAndSendAuthChallenge read SNAC payload: %+v\n", snacPayloadIn)
 	fmt.Printf("ReceiveAndSendAuthChallenge read SNAC payload: %+v\n", snacPayloadIn)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: 0x17,
-		subGroup:  0x07,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: 0x17,
+		SubGroup:  0x07,
 	}
 	}
-	snacPayloadOut := SNAC_0x17_0x07_BUCPChallengeResponse{
+	snacPayloadOut := oscar.SNAC_0x17_0x07_BUCPChallengeResponse{
 		AuthKey: s.ID,
 		AuthKey: s.ID,
 	}
 	}
 
 
@@ -74,31 +75,31 @@ func ReceiveAndSendAuthChallenge(s *Session, r io.Reader, w io.Writer, sequence
 }
 }
 
 
 func ReceiveAndSendBUCPLoginRequest(cfg Config, sess *Session, fm *FeedbagStore, r io.Reader, w io.Writer, sequence *uint32) error {
 func ReceiveAndSendBUCPLoginRequest(cfg Config, sess *Session, fm *FeedbagStore, r io.Reader, w io.Writer, sequence *uint32) error {
-	flap := flapFrame{}
-	if err := flap.read(r); err != nil {
+	flap := oscar.FlapFrame{}
+	if err := flap.Read(r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	b := make([]byte, flap.payloadLength)
+	b := make([]byte, flap.PayloadLength)
 	if _, err := r.Read(b); err != nil {
 	if _, err := r.Read(b); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	buf := bytes.NewBuffer(b)
 	buf := bytes.NewBuffer(b)
-	snac := snacFrame{}
-	if err := snac.read(buf); err != nil {
+	snac := oscar.SnacFrame{}
+	if err := snac.Read(buf); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	snacPayloadIn := SNAC_0x17_0x02_BUCPLoginRequest{}
-	if err := Unmarshal(&snacPayloadIn, buf); err != nil {
+	snacPayloadIn := oscar.SNAC_0x17_0x02_BUCPLoginRequest{}
+	if err := oscar.Unmarshal(&snacPayloadIn, buf); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	fmt.Printf("ReceiveAndSendBUCPLoginRequest read SNAC: %+v\n", snacPayloadIn)
 	fmt.Printf("ReceiveAndSendBUCPLoginRequest read SNAC: %+v\n", snacPayloadIn)
 
 
 	var found bool
 	var found bool
-	sess.ScreenName, found = snacPayloadIn.getString(TLV_SCREEN_NAME)
+	sess.ScreenName, found = snacPayloadIn.GetString(0x01)
 	if !found {
 	if !found {
 		return errors.New("unable to find screen name")
 		return errors.New("unable to find screen name")
 	}
 	}
@@ -107,41 +108,41 @@ func ReceiveAndSendBUCPLoginRequest(cfg Config, sess *Session, fm *FeedbagStore,
 		return err
 		return err
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: 0x17,
-		subGroup:  0x03,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: 0x17,
+		SubGroup:  0x03,
 	}
 	}
 
 
-	snacPayloadOut := SNAC_0x17_0x02_BUCPLoginRequest{
-		TLVRestBlock: TLVRestBlock{
-			TLVList: TLVList{
+	snacPayloadOut := oscar.SNAC_0x17_0x02_BUCPLoginRequest{
+		TLVRestBlock: oscar.TLVRestBlock{
+			TLVList: oscar.TLVList{
 				{
 				{
-					tType: TLV_SCREEN_NAME,
-					val:   sess.ScreenName,
+					TType: 0x01,
+					Val:   sess.ScreenName,
 				},
 				},
 				{
 				{
-					tType: 0x08,
-					val:   uint16(0x00),
+					TType: 0x08,
+					Val:   uint16(0x00),
 				},
 				},
 				{
 				{
-					tType: 0x04,
-					val:   "",
+					TType: 0x04,
+					Val:   "",
 				},
 				},
 				{
 				{
-					tType: 0x05,
-					val:   Address(cfg.OSCARHost, cfg.BOSPort),
+					TType: 0x05,
+					Val:   Address(cfg.OSCARHost, cfg.BOSPort),
 				},
 				},
 				{
 				{
-					tType: 0x06,
-					val:   []byte(sess.ID),
+					TType: 0x06,
+					Val:   []byte(sess.ID),
 				},
 				},
 				{
 				{
-					tType: 0x11,
-					val:   "mike@localhost",
+					TType: 0x11,
+					Val:   "mike@localhost",
 				},
 				},
 				{
 				{
-					tType: 0x54,
-					val:   "http://localhost",
+					TType: 0x54,
+					Val:   "http://localhost",
 				},
 				},
 			},
 			},
 		},
 		},

+ 32 - 31
oscar/buddy.go → server/buddy.go

@@ -1,7 +1,8 @@
-package oscar
+package server
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"github.com/mkaminski/goaim/oscar"
 	"io"
 	"io"
 )
 )
 
 
@@ -20,9 +21,9 @@ const (
 	BuddyDelTempBuddies             = 0x0010
 	BuddyDelTempBuddies             = 0x0010
 )
 )
 
 
-func routeBuddy(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func routeBuddy(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 
 
-	switch snac.subGroup {
+	switch snac.SubGroup {
 	case BuddyErr:
 	case BuddyErr:
 		panic("not implemented")
 		panic("not implemented")
 	case BuddyRightsQuery:
 	case BuddyRightsQuery:
@@ -51,38 +52,38 @@ func routeBuddy(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) erro
 	return nil
 	return nil
 }
 }
 
 
-func SendAndReceiveBuddyRights(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceiveBuddyRights(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("sendAndReceiveBuddyRights read SNAC frame: %+v\n", snac)
 	fmt.Printf("sendAndReceiveBuddyRights read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x03_0x02_BuddyRightsQuery{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x03_0x02_BuddyRightsQuery{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	fmt.Printf("sendAndReceiveBuddyRights read SNAC payload: %+v\n", snacPayloadIn)
 	fmt.Printf("sendAndReceiveBuddyRights read SNAC payload: %+v\n", snacPayloadIn)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: 0x03,
-		subGroup:  0x03,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: 0x03,
+		SubGroup:  0x03,
 	}
 	}
-	snacPayloadOut := SNAC_0x03_0x03_BuddyRightsReply{
-		TLVRestBlock: TLVRestBlock{
-			TLVList: TLVList{
+	snacPayloadOut := oscar.SNAC_0x03_0x03_BuddyRightsReply{
+		TLVRestBlock: oscar.TLVRestBlock{
+			TLVList: oscar.TLVList{
 				{
 				{
-					tType: 0x01,
-					val:   uint16(100),
+					TType: 0x01,
+					Val:   uint16(100),
 				},
 				},
 				{
 				{
-					tType: 0x02,
-					val:   uint16(100),
+					TType: 0x02,
+					Val:   uint16(100),
 				},
 				},
 				{
 				{
-					tType: 0x03,
-					val:   uint16(100),
+					TType: 0x03,
+					Val:   uint16(100),
 				},
 				},
 				{
 				{
-					tType: 0x04,
-					val:   uint16(100),
+					TType: 0x04,
+					Val:   uint16(100),
 				},
 				},
 			},
 			},
 		},
 		},
@@ -98,15 +99,15 @@ func NotifyArrival(sess *Session, sm *SessionManager, fm *FeedbagStore) error {
 	}
 	}
 
 
 	sm.BroadcastToScreenNames(screenNames, XMessage{
 	sm.BroadcastToScreenNames(screenNames, XMessage{
-		snacFrame: snacFrame{
-			foodGroup: BUDDY,
-			subGroup:  BuddyArrived,
+		snacFrame: oscar.SnacFrame{
+			FoodGroup: BUDDY,
+			SubGroup:  BuddyArrived,
 		},
 		},
-		snacOut: SNAC_0x03_0x0A_BuddyArrived{
-			TLVUserInfo: TLVUserInfo{
+		snacOut: oscar.SNAC_0x03_0x0A_BuddyArrived{
+			TLVUserInfo: oscar.TLVUserInfo{
 				ScreenName:   sess.ScreenName,
 				ScreenName:   sess.ScreenName,
 				WarningLevel: sess.GetWarning(),
 				WarningLevel: sess.GetWarning(),
-				TLVBlock: TLVBlock{
+				TLVBlock: oscar.TLVBlock{
 					TLVList: sess.GetUserInfo(),
 					TLVList: sess.GetUserInfo(),
 				},
 				},
 			},
 			},
@@ -123,12 +124,12 @@ func NotifyDeparture(sess *Session, sm *SessionManager, fm *FeedbagStore) error
 	}
 	}
 
 
 	sm.BroadcastToScreenNames(screenNames, XMessage{
 	sm.BroadcastToScreenNames(screenNames, XMessage{
-		snacFrame: snacFrame{
-			foodGroup: BUDDY,
-			subGroup:  BuddyDeparted,
+		snacFrame: oscar.SnacFrame{
+			FoodGroup: BUDDY,
+			SubGroup:  BuddyDeparted,
 		},
 		},
-		snacOut: SNAC_0x03_0x0B_BuddyDeparted{
-			TLVUserInfo: TLVUserInfo{
+		snacOut: oscar.SNAC_0x03_0x0B_BuddyDeparted{
+			TLVUserInfo: oscar.TLVUserInfo{
 				ScreenName:   sess.ScreenName,
 				ScreenName:   sess.ScreenName,
 				WarningLevel: sess.GetWarning(),
 				WarningLevel: sess.GetWarning(),
 			},
 			},

+ 48 - 43
oscar/chat.go → server/chat.go

@@ -1,7 +1,8 @@
-package oscar
+package server
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"github.com/mkaminski/goaim/oscar"
 	"io"
 	"io"
 )
 )
 
 
@@ -50,8 +51,8 @@ const (
 	ChatRoomInfoOwner             = 0x0030
 	ChatRoomInfoOwner             = 0x0030
 )
 )
 
 
-func routeChat(sess *Session, sm *SessionManager, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
-	switch snac.subGroup {
+func routeChat(sess *Session, sm *SessionManager, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+	switch snac.SubGroup {
 	case ChatErr:
 	case ChatErr:
 		panic("not implemented")
 		panic("not implemented")
 	case ChatRoomInfoUpdate:
 	case ChatRoomInfoUpdate:
@@ -140,30 +141,34 @@ func routeChat(sess *Session, sm *SessionManager, snac snacFrame, r io.Reader, w
 	return nil
 	return nil
 }
 }
 
 
-func SendAndReceiveChatChannelMsgTohost(sess *Session, sm *SessionManager, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceiveChatChannelMsgTohost(sess *Session, sm *SessionManager, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("SendAndReceiveChatChannelMsgTohost read SNAC frame: %+v\n", snac)
 	fmt.Printf("SendAndReceiveChatChannelMsgTohost read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x0E_0x05_ChatChannelMsgToHost{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x0E_0x05_ChatChannelMsgToHost{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: CHAT,
-		subGroup:  ChatChannelMsgToclient,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: CHAT,
+		SubGroup:  ChatChannelMsgToclient,
 	}
 	}
-	snacPayloadOut := SNAC_0x0E_0x06_ChatChannelMsgToClient{
-		Cookie:       snacPayloadIn.Cookie,
-		Channel:      snacPayloadIn.Channel,
-		TLVRestBlock: TLVRestBlock{snacPayloadIn.TLVList},
+	snacPayloadOut := oscar.SNAC_0x0E_0x06_ChatChannelMsgToClient{
+		Cookie:  snacPayloadIn.Cookie,
+		Channel: snacPayloadIn.Channel,
+		TLVRestBlock: oscar.TLVRestBlock{
+			TLVList: snacPayloadIn.TLVList,
+		},
 	}
 	}
 
 
-	snacPayloadOut.addTLV(TLV{
-		tType: 0x03,
-		val: TLVUserInfo{
+	snacPayloadOut.AddTLV(oscar.TLV{
+		TType: 0x03,
+		Val: oscar.TLVUserInfo{
 			ScreenName:   sess.ScreenName,
 			ScreenName:   sess.ScreenName,
 			WarningLevel: sess.GetWarning(),
 			WarningLevel: sess.GetWarning(),
-			TLVBlock:     TLVBlock{sess.GetUserInfo()},
+			TLVBlock: oscar.TLVBlock{
+				TLVList: sess.GetUserInfo(),
+			},
 		},
 		},
 	})
 	})
 
 
@@ -173,7 +178,7 @@ func SendAndReceiveChatChannelMsgTohost(sess *Session, sm *SessionManager, snac
 		snacOut:   snacPayloadOut,
 		snacOut:   snacPayloadOut,
 	})
 	})
 
 
-	if _, ackMsg := snacPayloadIn.getTLV(0x06); !ackMsg {
+	if _, ackMsg := snacPayloadIn.GetTLV(0x06); !ackMsg {
 		return nil
 		return nil
 	}
 	}
 
 
@@ -182,39 +187,39 @@ func SendAndReceiveChatChannelMsgTohost(sess *Session, sm *SessionManager, snac
 }
 }
 
 
 func SetOnlineChatUsers(sm *SessionManager, w io.Writer, sequence *uint32) error {
 func SetOnlineChatUsers(sm *SessionManager, w io.Writer, sequence *uint32) error {
-	snacFrameOut := snacFrame{
-		foodGroup: CHAT,
-		subGroup:  ChatUsersJoined,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: CHAT,
+		SubGroup:  ChatUsersJoined,
 	}
 	}
-	snacPayloadOut := SNAC_0x0E_0x03_ChatUsersJoined{}
+	snacPayloadOut := oscar.SNAC_0x0E_0x03_ChatUsersJoined{}
 
 
 	sessions := sm.All()
 	sessions := sm.All()
 
 
 	for _, uSess := range sessions {
 	for _, uSess := range sessions {
-		snacPayloadOut.Users = append(snacPayloadOut.Users, TLVUserInfo{
+		snacPayloadOut.Users = append(snacPayloadOut.Users, oscar.TLVUserInfo{
 			ScreenName:   uSess.ScreenName,
 			ScreenName:   uSess.ScreenName,
 			WarningLevel: uSess.GetWarning(),
 			WarningLevel: uSess.GetWarning(),
-			TLVBlock: TLVBlock{
+			TLVBlock: oscar.TLVBlock{
 				TLVList: uSess.GetUserInfo(),
 				TLVList: uSess.GetUserInfo(),
 			},
 			},
 		})
 		})
 	}
 	}
 
 
-	return writeOutSNAC(snacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
+	return writeOutSNAC(oscar.SnacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
 func AlertUserJoined(sess *Session, sm *SessionManager) {
 func AlertUserJoined(sess *Session, sm *SessionManager) {
 	sm.BroadcastExcept(sess, XMessage{
 	sm.BroadcastExcept(sess, XMessage{
-		snacFrame: snacFrame{
-			foodGroup: CHAT,
-			subGroup:  ChatUsersJoined,
+		snacFrame: oscar.SnacFrame{
+			FoodGroup: CHAT,
+			SubGroup:  ChatUsersJoined,
 		},
 		},
-		snacOut: SNAC_0x0E_0x03_ChatUsersJoined{
-			Users: []TLVUserInfo{
+		snacOut: oscar.SNAC_0x0E_0x03_ChatUsersJoined{
+			Users: []oscar.TLVUserInfo{
 				{
 				{
 					ScreenName:   sess.ScreenName,
 					ScreenName:   sess.ScreenName,
 					WarningLevel: sess.GetWarning(),
 					WarningLevel: sess.GetWarning(),
-					TLVBlock: TLVBlock{
+					TLVBlock: oscar.TLVBlock{
 						TLVList: sess.GetUserInfo(),
 						TLVList: sess.GetUserInfo(),
 					},
 					},
 				},
 				},
@@ -225,16 +230,16 @@ func AlertUserJoined(sess *Session, sm *SessionManager) {
 
 
 func AlertUserLeft(sess *Session, sm *SessionManager) {
 func AlertUserLeft(sess *Session, sm *SessionManager) {
 	sm.BroadcastExcept(sess, XMessage{
 	sm.BroadcastExcept(sess, XMessage{
-		snacFrame: snacFrame{
-			foodGroup: CHAT,
-			subGroup:  ChatUsersLeft,
+		snacFrame: oscar.SnacFrame{
+			FoodGroup: CHAT,
+			SubGroup:  ChatUsersLeft,
 		},
 		},
-		snacOut: SNAC_0x0E_0x04_ChatUsersLeft{
-			Users: []TLVUserInfo{
+		snacOut: oscar.SNAC_0x0E_0x04_ChatUsersLeft{
+			Users: []oscar.TLVUserInfo{
 				{
 				{
 					ScreenName:   sess.ScreenName,
 					ScreenName:   sess.ScreenName,
 					WarningLevel: sess.GetWarning(),
 					WarningLevel: sess.GetWarning(),
-					TLVBlock: TLVBlock{
+					TLVBlock: oscar.TLVBlock{
 						TLVList: sess.GetUserInfo(),
 						TLVList: sess.GetUserInfo(),
 					},
 					},
 				},
 				},
@@ -244,18 +249,18 @@ func AlertUserLeft(sess *Session, sm *SessionManager) {
 }
 }
 
 
 func SendChatRoomInfoUpdate(room ChatRoom, w io.Writer, sequence *uint32) error {
 func SendChatRoomInfoUpdate(room ChatRoom, w io.Writer, sequence *uint32) error {
-	snacFrameOut := snacFrame{
-		foodGroup: CHAT,
-		subGroup:  ChatRoomInfoUpdate,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: CHAT,
+		SubGroup:  ChatRoomInfoUpdate,
 	}
 	}
-	snacPayloadOut := SNAC_0x0E_0x02_ChatRoomInfoUpdate{
+	snacPayloadOut := oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 		Exchange:       4,
 		Exchange:       4,
 		Cookie:         room.ID,
 		Cookie:         room.ID,
 		InstanceNumber: 100,
 		InstanceNumber: 100,
 		DetailLevel:    2,
 		DetailLevel:    2,
-		TLVBlock: TLVBlock{
+		TLVBlock: oscar.TLVBlock{
 			TLVList: room.TLVList(),
 			TLVList: room.TLVList(),
 		},
 		},
 	}
 	}
-	return writeOutSNAC(snacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
+	return writeOutSNAC(oscar.SnacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }

+ 60 - 59
oscar/chat_nav.go → server/chat_nav.go

@@ -1,10 +1,11 @@
-package oscar
+package server
 
 
 import (
 import (
 	"encoding/binary"
 	"encoding/binary"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"github.com/google/uuid"
 	"github.com/google/uuid"
+	"github.com/mkaminski/goaim/oscar"
 	"io"
 	"io"
 	"time"
 	"time"
 )
 )
@@ -21,8 +22,8 @@ const (
 	ChatNavNavInfo                    = 0x0009
 	ChatNavNavInfo                    = 0x0009
 )
 )
 
 
-func routeChatNav(sess *Session, cr *ChatRegistry, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
-	switch snac.subGroup {
+func routeChatNav(sess *Session, cr *ChatRegistry, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+	switch snac.SubGroup {
 	case ChatNavErr:
 	case ChatNavErr:
 		panic("not implemented")
 		panic("not implemented")
 	case ChatNavRequestChatRights:
 	case ChatNavRequestChatRights:
@@ -50,7 +51,7 @@ type ChatCookie struct {
 	SessID string
 	SessID string
 }
 }
 
 
-func (s *ChatCookie) read(r io.Reader) error {
+func (s *ChatCookie) Read(r io.Reader) error {
 	var l uint16
 	var l uint16
 	if err := binary.Read(r, binary.BigEndian, &l); err != nil {
 	if err := binary.Read(r, binary.BigEndian, &l); err != nil {
 		return err
 		return err
@@ -70,7 +71,7 @@ func (s *ChatCookie) read(r io.Reader) error {
 	return nil
 	return nil
 }
 }
 
 
-func (s ChatCookie) write(w io.Writer) error {
+func (s ChatCookie) Write(w io.Writer) error {
 	if err := binary.Write(w, binary.BigEndian, uint16(len(s.Cookie))); err != nil {
 	if err := binary.Write(w, binary.BigEndian, uint16(len(s.Cookie))); err != nil {
 		return err
 		return err
 	}
 	}
@@ -86,58 +87,58 @@ func (s ChatCookie) write(w io.Writer) error {
 	return nil
 	return nil
 }
 }
 
 
-func SendAndReceiveNextChatRights(snac snacFrame, w io.Writer, sequence *uint32) error {
+func SendAndReceiveNextChatRights(snac oscar.SnacFrame, w io.Writer, sequence *uint32) error {
 	fmt.Printf("sendAndReceiveNextChatRights read SNAC frame: %+v\n", snac)
 	fmt.Printf("sendAndReceiveNextChatRights read SNAC frame: %+v\n", snac)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: CHAT_NAV,
-		subGroup:  ChatNavNavInfo,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: CHAT_NAV,
+		SubGroup:  ChatNavNavInfo,
 	}
 	}
 
 
-	snacPayloadOut := SNAC_0x0D_0x09_ChatNavNavInfo{
-		TLVRestBlock{
-			TLVList: TLVList{
+	snacPayloadOut := oscar.SNAC_0x0D_0x09_ChatNavNavInfo{
+		TLVRestBlock: oscar.TLVRestBlock{
+			TLVList: oscar.TLVList{
 				{
 				{
-					tType: 0x02,
-					val:   uint8(10),
+					TType: 0x02,
+					Val:   uint8(10),
 				},
 				},
 				{
 				{
-					tType: 0x03,
-					val: SNAC_0x0D_0x09_TLVExchangeInfo{
+					TType: 0x03,
+					Val: oscar.SNAC_0x0D_0x09_TLVExchangeInfo{
 						Identifier: 4,
 						Identifier: 4,
-						TLVBlock: TLVBlock{
-							TLVList: TLVList{
+						TLVBlock: oscar.TLVBlock{
+							TLVList: oscar.TLVList{
 								{
 								{
-									tType: 0x0002,
-									val:   uint16(0x0010),
+									TType: 0x0002,
+									Val:   uint16(0x0010),
 								},
 								},
 								{
 								{
-									tType: 0x00c9,
-									val:   uint16(15),
+									TType: 0x00c9,
+									Val:   uint16(15),
 								},
 								},
 								{
 								{
-									tType: 0x00d3,
-									val:   "default Exchange",
+									TType: 0x00d3,
+									Val:   "default Exchange",
 								},
 								},
 								{
 								{
-									tType: 0x00d5,
-									val:   uint8(2),
+									TType: 0x00d5,
+									Val:   uint8(2),
 								},
 								},
 								{
 								{
-									tType: 0xd6,
-									val:   "us-ascii",
+									TType: 0xd6,
+									Val:   "us-ascii",
 								},
 								},
 								{
 								{
-									tType: 0xd7,
-									val:   "en",
+									TType: 0xd7,
+									Val:   "en",
 								},
 								},
 								{
 								{
-									tType: 0xd8,
-									val:   "us-ascii",
+									TType: 0xd8,
+									Val:   "us-ascii",
 								},
 								},
 								{
 								{
-									tType: 0xd9,
-									val:   "en",
+									TType: 0xd9,
+									Val:   "en",
 								},
 								},
 							},
 							},
 						},
 						},
@@ -150,15 +151,15 @@ func SendAndReceiveNextChatRights(snac snacFrame, w io.Writer, sequence *uint32)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func SendAndReceiveCreateRoom(sess *Session, cr *ChatRegistry, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceiveCreateRoom(sess *Session, cr *ChatRegistry, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("SendAndReceiveCreateRoom read SNAC frame: %+v\n", snac)
 	fmt.Printf("SendAndReceiveCreateRoom read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x0E_0x02_ChatRoomInfoUpdate{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	name, hasName := snacPayloadIn.getString(0x00d3)
+	name, hasName := snacPayloadIn.GetString(0x00d3)
 	if !hasName {
 	if !hasName {
 		return errors.New("unable to find chat name")
 		return errors.New("unable to find chat name")
 	}
 	}
@@ -174,21 +175,21 @@ func SendAndReceiveCreateRoom(sess *Session, cr *ChatRegistry, snac snacFrame, r
 	// add user to chat room
 	// add user to chat room
 	room.SessionManager.NewSessionWithSN(sess.ID, sess.ScreenName)
 	room.SessionManager.NewSessionWithSN(sess.ID, sess.ScreenName)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: CHAT_NAV,
-		subGroup:  ChatNavNavInfo,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: CHAT_NAV,
+		SubGroup:  ChatNavNavInfo,
 	}
 	}
-	snacPayloadOut := SNAC_0x0D_0x09_ChatNavNavInfo{
-		TLVRestBlock{
-			TLVList: TLVList{
+	snacPayloadOut := oscar.SNAC_0x0D_0x09_ChatNavNavInfo{
+		TLVRestBlock: oscar.TLVRestBlock{
+			TLVList: oscar.TLVList{
 				{
 				{
-					tType: 0x04,
-					val: SNAC_0x0E_0x02_ChatRoomInfoUpdate{
+					TType: 0x04,
+					Val: oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 						Exchange:       4,
 						Exchange:       4,
 						Cookie:         room.ID,
 						Cookie:         room.ID,
 						InstanceNumber: 100,
 						InstanceNumber: 100,
 						DetailLevel:    2,
 						DetailLevel:    2,
-						TLVBlock: TLVBlock{
+						TLVBlock: oscar.TLVBlock{
 							TLVList: room.TLVList(),
 							TLVList: room.TLVList(),
 						},
 						},
 					},
 					},
@@ -200,11 +201,11 @@ func SendAndReceiveCreateRoom(sess *Session, cr *ChatRegistry, snac snacFrame, r
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func SendAndReceiveRequestRoomInfo(cr *ChatRegistry, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceiveRequestRoomInfo(cr *ChatRegistry, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("SendAndReceiveRequestRoomInfo read SNAC frame: %+v\n", snac)
 	fmt.Printf("SendAndReceiveRequestRoomInfo read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x0D_0x04_ChatNavRequestRoomInfo{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x0D_0x04_ChatNavRequestRoomInfo{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -213,22 +214,22 @@ func SendAndReceiveRequestRoomInfo(cr *ChatRegistry, snac snacFrame, r io.Reader
 		return err
 		return err
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: CHAT_NAV,
-		subGroup:  ChatNavNavInfo,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: CHAT_NAV,
+		SubGroup:  ChatNavNavInfo,
 	}
 	}
 
 
-	snacPayloadOut := SNAC_0x0D_0x09_ChatNavNavInfo{
-		TLVRestBlock{
-			TLVList: TLVList{
+	snacPayloadOut := oscar.SNAC_0x0D_0x09_ChatNavNavInfo{
+		TLVRestBlock: oscar.TLVRestBlock{
+			TLVList: oscar.TLVList{
 				{
 				{
-					tType: 0x04,
-					val: SNAC_0x0E_0x02_ChatRoomInfoUpdate{
+					TType: 0x04,
+					Val: oscar.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 						Exchange:       4,
 						Exchange:       4,
 						Cookie:         room.ID,
 						Cookie:         room.ID,
 						InstanceNumber: 100,
 						InstanceNumber: 100,
 						DetailLevel:    2,
 						DetailLevel:    2,
-						TLVBlock: TLVBlock{
+						TLVBlock: oscar.TLVBlock{
 							TLVList: room.TLVList(),
 							TLVList: room.TLVList(),
 						},
 						},
 					},
 					},

+ 89 - 88
oscar/feedbag.go → server/feedbag.go

@@ -1,8 +1,9 @@
-package oscar
+package server
 
 
 import (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"github.com/mkaminski/goaim/oscar"
 	"io"
 	"io"
 )
 )
 
 
@@ -143,8 +144,8 @@ const (
 	FeedbagClassIdMin                     = 0x0400
 	FeedbagClassIdMin                     = 0x0400
 )
 )
 
 
-func routeFeedbag(sm *SessionManager, sess *Session, fm *FeedbagStore, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
-	switch snac.subGroup {
+func routeFeedbag(sm *SessionManager, sess *Session, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+	switch snac.SubGroup {
 	case FeedbagErr:
 	case FeedbagErr:
 		panic("not implemented")
 		panic("not implemented")
 	case FeedbagRightsQuery:
 	case FeedbagRightsQuery:
@@ -218,30 +219,30 @@ func routeFeedbag(sm *SessionManager, sess *Session, fm *FeedbagStore, snac snac
 	return nil
 	return nil
 }
 }
 
 
-func SendAndReceiveFeedbagRightsQuery(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceiveFeedbagRightsQuery(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("sendAndReceiveFeedbagRightsQuery read SNAC frame: %+v\n", snac)
 	fmt.Printf("sendAndReceiveFeedbagRightsQuery read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x13_0x02_FeedbagRightsQuery{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x13_0x02_FeedbagRightsQuery{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	fmt.Printf("sendAndReceiveFeedbagRightsQuery read SNAC payload: %+v\n", snacPayloadIn)
 	fmt.Printf("sendAndReceiveFeedbagRightsQuery read SNAC payload: %+v\n", snacPayloadIn)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: 0x13,
-		subGroup:  0x03,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: 0x13,
+		SubGroup:  0x03,
 	}
 	}
-	snacPayloadOut := SNAC_0x13_0x03_FeedbagRightsReply{
-		TLVRestBlock{
-			TLVList: TLVList{
+	snacPayloadOut := oscar.SNAC_0x13_0x03_FeedbagRightsReply{
+		TLVRestBlock: oscar.TLVRestBlock{
+			TLVList: oscar.TLVList{
 				{
 				{
-					tType: 0x03,
-					val:   uint16(200),
+					TType: 0x03,
+					Val:   uint16(200),
 				},
 				},
 				{
 				{
-					tType: 0x04,
-					val: []uint16{
+					TType: 0x04,
+					Val: []uint16{
 						0x3D,
 						0x3D,
 						0x3D,
 						0x3D,
 						0x64,
 						0x64,
@@ -266,40 +267,40 @@ func SendAndReceiveFeedbagRightsQuery(snac snacFrame, r io.Reader, w io.Writer,
 					},
 					},
 				},
 				},
 				{
 				{
-					tType: 0x05,
-					val:   uint16(200),
+					TType: 0x05,
+					Val:   uint16(200),
 				},
 				},
 				{
 				{
-					tType: 0x06,
-					val:   uint16(200),
+					TType: 0x06,
+					Val:   uint16(200),
 				},
 				},
 				{
 				{
-					tType: 0x07,
-					val:   uint16(200),
+					TType: 0x07,
+					Val:   uint16(200),
 				},
 				},
 				{
 				{
-					tType: 0x08,
-					val:   uint16(200),
+					TType: 0x08,
+					Val:   uint16(200),
 				},
 				},
 				{
 				{
-					tType: 0x09,
-					val:   uint16(200),
+					TType: 0x09,
+					Val:   uint16(200),
 				},
 				},
 				{
 				{
-					tType: 0x0A,
-					val:   uint16(200),
+					TType: 0x0A,
+					Val:   uint16(200),
 				},
 				},
 				{
 				{
-					tType: 0x0C,
-					val:   uint16(200),
+					TType: 0x0C,
+					Val:   uint16(200),
 				},
 				},
 				{
 				{
-					tType: 0x0D,
-					val:   uint16(200),
+					TType: 0x0D,
+					Val:   uint16(200),
 				},
 				},
 				{
 				{
-					tType: 0x0E,
-					val:   uint16(100),
+					TType: 0x0E,
+					Val:   uint16(100),
 				},
 				},
 			},
 			},
 		},
 		},
@@ -308,7 +309,7 @@ func SendAndReceiveFeedbagRightsQuery(snac snacFrame, r io.Reader, w io.Writer,
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func ReceiveAndSendFeedbagQuery(sess *Session, fm *FeedbagStore, snac snacFrame, w io.Writer, sequence *uint32) error {
+func ReceiveAndSendFeedbagQuery(sess *Session, fm *FeedbagStore, snac oscar.SnacFrame, w io.Writer, sequence *uint32) error {
 	fmt.Printf("receiveAndSendFeedbagQuery read SNAC frame: %+v\n", snac)
 	fmt.Printf("receiveAndSendFeedbagQuery read SNAC frame: %+v\n", snac)
 
 
 	fb, err := fm.Retrieve(sess.ScreenName)
 	fb, err := fm.Retrieve(sess.ScreenName)
@@ -325,11 +326,11 @@ func ReceiveAndSendFeedbagQuery(sess *Session, fm *FeedbagStore, snac snacFrame,
 		lastModified = uint32(lm.Unix())
 		lastModified = uint32(lm.Unix())
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: 0x13,
-		subGroup:  0x06,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: 0x13,
+		SubGroup:  0x06,
 	}
 	}
-	snacPayloadOut := SNAC_0x13_0x06_FeedbagReply{
+	snacPayloadOut := oscar.SNAC_0x13_0x06_FeedbagReply{
 		Version:    0,
 		Version:    0,
 		Items:      fb,
 		Items:      fb,
 		LastUpdate: lastModified,
 		LastUpdate: lastModified,
@@ -338,11 +339,11 @@ func ReceiveAndSendFeedbagQuery(sess *Session, fm *FeedbagStore, snac snacFrame,
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func ReceiveAndSendFeedbagQueryIfModified(sess *Session, fm *FeedbagStore, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveAndSendFeedbagQueryIfModified(sess *Session, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("ReceiveAndSendFeedbagQueryIfModified read SNAC frame: %+v\n", snac)
 	fmt.Printf("ReceiveAndSendFeedbagQueryIfModified read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x13_0x05_FeedbagQueryIfModified{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x13_0x05_FeedbagQueryIfModified{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -362,7 +363,7 @@ func ReceiveAndSendFeedbagQueryIfModified(sess *Session, fm *FeedbagStore, snac
 	//todo not sure this works right now
 	//todo not sure this works right now
 	//	snacFrameOut := snacFrame{
 	//	snacFrameOut := snacFrame{
 	//		FoodGroup: 0x13,
 	//		FoodGroup: 0x13,
-	//		subGroup:  0x0F,
+	//		SubGroup:  0x0F,
 	//	}
 	//	}
 	//	lm, err := fm.LastModified(sess.ScreenName)
 	//	lm, err := fm.LastModified(sess.ScreenName)
 	//	if err != nil {
 	//	if err != nil {
@@ -375,11 +376,11 @@ func ReceiveAndSendFeedbagQueryIfModified(sess *Session, fm *FeedbagStore, snac
 	//	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	//	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	//}
 	//}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: 0x13,
-		subGroup:  0x06,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: 0x13,
+		SubGroup:  0x06,
 	}
 	}
-	snacPayloadOut := SNAC_0x13_0x06_FeedbagReply{
+	snacPayloadOut := oscar.SNAC_0x13_0x06_FeedbagReply{
 		Version:    0,
 		Version:    0,
 		Items:      fb,
 		Items:      fb,
 		LastUpdate: uint32(lm.Unix()),
 		LastUpdate: uint32(lm.Unix()),
@@ -388,24 +389,24 @@ func ReceiveAndSendFeedbagQueryIfModified(sess *Session, fm *FeedbagStore, snac
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func ReceiveInsertItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveInsertItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("ReceiveInsertItem read SNAC frame: %+v\n", snac)
 	fmt.Printf("ReceiveInsertItem read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x13_0x08_FeedbagInsertItem{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x13_0x08_FeedbagInsertItem{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	snacPayloadOut := SNAC_0x13_0x0E_FeedbagStatus{}
+	snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
 	for _, item := range snacPayloadIn.Items {
 	for _, item := range snacPayloadIn.Items {
 		// don't let users block themselves, it causes the AIM client to go
 		// don't let users block themselves, it causes the AIM client to go
 		// into a weird state.
 		// into a weird state.
 		if item.ClassID == 3 && item.Name == sess.ScreenName {
 		if item.ClassID == 3 && item.Name == sess.ScreenName {
-			snacFrameOut := snacFrame{
-				foodGroup: FEEDBAG,
-				subGroup:  FeedbagErr,
+			snacFrameOut := oscar.SnacFrame{
+				FoodGroup: FEEDBAG,
+				SubGroup:  FeedbagErr,
 			}
 			}
-			snacPayloadOut := snacError{
+			snacPayloadOut := oscar.SnacError{
 				Code: ErrorCodeNotSupportedByHost,
 				Code: ErrorCodeNotSupportedByHost,
 			}
 			}
 			return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 			return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
@@ -418,9 +419,9 @@ func ReceiveInsertItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac
 		return err
 		return err
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: FEEDBAG,
-		subGroup:  FeedbagStatus,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: FEEDBAG,
+		SubGroup:  FeedbagStatus,
 	}
 	}
 
 
 	if err := writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w); err != nil {
 	if err := writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w); err != nil {
@@ -443,12 +444,12 @@ func ReceiveInsertItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac
 func blockBuddy(sm *SessionManager, sess *Session, screenName string, sequence *uint32, w io.Writer) error {
 func blockBuddy(sm *SessionManager, sess *Session, screenName string, sequence *uint32, w io.Writer) error {
 	// tell the blocked buddy you've signed off
 	// tell the blocked buddy you've signed off
 	sm.SendToScreenName(screenName, XMessage{
 	sm.SendToScreenName(screenName, XMessage{
-		snacFrame: snacFrame{
-			foodGroup: BUDDY,
-			subGroup:  BuddyDeparted,
+		snacFrame: oscar.SnacFrame{
+			FoodGroup: BUDDY,
+			SubGroup:  BuddyDeparted,
 		},
 		},
-		snacOut: SNAC_0x03_0x0A_BuddyArrived{
-			TLVUserInfo: TLVUserInfo{
+		snacOut: oscar.SNAC_0x03_0x0A_BuddyArrived{
+			TLVUserInfo: oscar.TLVUserInfo{
 				ScreenName:   sess.ScreenName,
 				ScreenName:   sess.ScreenName,
 				WarningLevel: sess.GetWarning(),
 				WarningLevel: sess.GetWarning(),
 			},
 			},
@@ -465,25 +466,25 @@ func blockBuddy(sm *SessionManager, sess *Session, screenName string, sequence *
 		return err
 		return err
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: BUDDY,
-		subGroup:  BuddyDeparted,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: BUDDY,
+		SubGroup:  BuddyDeparted,
 	}
 	}
-	snacPayloadOut := SNAC_0x03_0x0A_BuddyArrived{
-		TLVUserInfo: TLVUserInfo{
+	snacPayloadOut := oscar.SNAC_0x03_0x0A_BuddyArrived{
+		TLVUserInfo: oscar.TLVUserInfo{
 			ScreenName:   buddySess.ScreenName,
 			ScreenName:   buddySess.ScreenName,
 			WarningLevel: buddySess.GetWarning(),
 			WarningLevel: buddySess.GetWarning(),
 		},
 		},
 	}
 	}
 
 
-	return writeOutSNAC(snacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
+	return writeOutSNAC(oscar.SnacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func ReceiveUpdateItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveUpdateItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("ReceiveUpdateItem read SNAC frame: %+v\n", snac)
 	fmt.Printf("ReceiveUpdateItem read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x13_0x09_FeedbagUpdateItem{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x13_0x09_FeedbagUpdateItem{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -491,16 +492,16 @@ func ReceiveUpdateItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac
 		return err
 		return err
 	}
 	}
 
 
-	snacPayloadOut := SNAC_0x13_0x0E_FeedbagStatus{}
+	snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
 
 
 	for _, item := range snacPayloadIn.Items {
 	for _, item := range snacPayloadIn.Items {
 		snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000) // success by default
 		snacPayloadOut.Results = append(snacPayloadOut.Results, 0x0000) // success by default
 		fmt.Printf("ReceiveUpdateItem read SNAC feedbag item: %+v\n", item)
 		fmt.Printf("ReceiveUpdateItem read SNAC feedbag item: %+v\n", item)
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: FEEDBAG,
-		subGroup:  FeedbagStatus,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: FEEDBAG,
+		SubGroup:  FeedbagStatus,
 	}
 	}
 
 
 	if err := writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w); err != nil {
 	if err := writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w); err != nil {
@@ -510,11 +511,11 @@ func ReceiveUpdateItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac
 	return GetOnlineBuddies(w, sess, sm, fm, sequence)
 	return GetOnlineBuddies(w, sess, sm, fm, sequence)
 }
 }
 
 
-func ReceiveDeleteItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveDeleteItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("ReceiveUpdateItem read SNAC frame: %+v\n", snac)
 	fmt.Printf("ReceiveUpdateItem read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x13_0x0A_FeedbagDeleteItem{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x13_0x0A_FeedbagDeleteItem{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -522,7 +523,7 @@ func ReceiveDeleteItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac
 		return err
 		return err
 	}
 	}
 
 
-	snacPayloadOut := SNAC_0x13_0x0E_FeedbagStatus{}
+	snacPayloadOut := oscar.SNAC_0x13_0x0E_FeedbagStatus{}
 
 
 	hasUnblock := false
 	hasUnblock := false
 	for _, item := range snacPayloadIn.Items {
 	for _, item := range snacPayloadIn.Items {
@@ -540,26 +541,26 @@ func ReceiveDeleteItem(sm *SessionManager, sess *Session, fm *FeedbagStore, snac
 		}
 		}
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: FEEDBAG,
-		subGroup:  FeedbagStatus,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: FEEDBAG,
+		SubGroup:  FeedbagStatus,
 	}
 	}
 
 
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func ReceiveFeedbagStartCluster(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveFeedbagStartCluster(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("ReceiveFeedbagStartCluster read SNAC frame: %+v\n", snac)
 	fmt.Printf("ReceiveFeedbagStartCluster read SNAC frame: %+v\n", snac)
-	tlv := TLVRestBlock{}
-	return tlv.read(r)
+	tlv := oscar.TLVRestBlock{}
+	return tlv.Read(r)
 }
 }
 
 
-func ReceiveFeedbagEndCluster(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveFeedbagEndCluster(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("receiveFeedbagEndCluster read SNAC frame: %+v\n", snac)
 	fmt.Printf("receiveFeedbagEndCluster read SNAC frame: %+v\n", snac)
 	return nil
 	return nil
 }
 }
 
 
-func ReceiveUse(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveUse(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("ReceiveUse read SNAC frame: %+v\n", snac)
 	fmt.Printf("ReceiveUse read SNAC frame: %+v\n", snac)
 	return nil
 	return nil
 }
 }

+ 13 - 14
oscar/feedbag_store.go → server/feedbag_store.go

@@ -1,15 +1,14 @@
-package oscar
+package server
 
 
 import (
 import (
 	"bytes"
 	"bytes"
 	"database/sql"
 	"database/sql"
 	"errors"
 	"errors"
 	_ "github.com/mattn/go-sqlite3"
 	_ "github.com/mattn/go-sqlite3"
+	"github.com/mkaminski/goaim/oscar"
 	"time"
 	"time"
 )
 )
 
 
-const file string = "/Users/mike/dev/goaim/aim.db"
-
 var errUserNotExist = errors.New("user does not exist")
 var errUserNotExist = errors.New("user does not exist")
 
 
 var feedbagDDL = `
 var feedbagDDL = `
@@ -60,7 +59,7 @@ func (f *FeedbagStore) UpsertUser(screenName string) error {
 	return err
 	return err
 }
 }
 
 
-func (f *FeedbagStore) Delete(screenName string, items []FeedbagItem) error {
+func (f *FeedbagStore) Delete(screenName string, items []oscar.FeedbagItem) error {
 	// todo add transaction
 	// todo add transaction
 	q := `DELETE FROM feedbag WHERE ScreenName = ? AND itemID = ?`
 	q := `DELETE FROM feedbag WHERE ScreenName = ? AND itemID = ?`
 
 
@@ -73,7 +72,7 @@ func (f *FeedbagStore) Delete(screenName string, items []FeedbagItem) error {
 	return nil
 	return nil
 }
 }
 
 
-func (f *FeedbagStore) Retrieve(screenName string) ([]FeedbagItem, error) {
+func (f *FeedbagStore) Retrieve(screenName string) ([]oscar.FeedbagItem, error) {
 	q := `
 	q := `
 		SELECT 
 		SELECT 
 			groupID,
 			groupID,
@@ -91,14 +90,14 @@ func (f *FeedbagStore) Retrieve(screenName string) ([]FeedbagItem, error) {
 	}
 	}
 	defer rows.Close()
 	defer rows.Close()
 
 
-	var items []FeedbagItem
+	var items []oscar.FeedbagItem
 	for rows.Next() {
 	for rows.Next() {
-		var item FeedbagItem
+		var item oscar.FeedbagItem
 		var attrs []byte
 		var attrs []byte
 		if err := rows.Scan(&item.GroupID, &item.ItemID, &item.ClassID, &item.Name, &attrs); err != nil {
 		if err := rows.Scan(&item.GroupID, &item.ItemID, &item.ClassID, &item.Name, &attrs); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		err = item.TLVLBlock.read(bytes.NewBuffer(attrs))
+		err = item.TLVLBlock.Read(bytes.NewBuffer(attrs))
 		if err != nil {
 		if err != nil {
 			return items, err
 			return items, err
 		}
 		}
@@ -110,12 +109,12 @@ func (f *FeedbagStore) Retrieve(screenName string) ([]FeedbagItem, error) {
 
 
 func (f *FeedbagStore) LastModified(screenName string) (time.Time, error) {
 func (f *FeedbagStore) LastModified(screenName string) (time.Time, error) {
 	var lastModified sql.NullInt64
 	var lastModified sql.NullInt64
-	sql := `SELECT MAX(lastModified) FROM feedbag WHERE ScreenName = ?`
-	err := f.db.QueryRow(sql, screenName).Scan(&lastModified)
+	q := `SELECT MAX(lastModified) FROM feedbag WHERE ScreenName = ?`
+	err := f.db.QueryRow(q, screenName).Scan(&lastModified)
 	return time.Unix(lastModified.Int64, 0), err
 	return time.Unix(lastModified.Int64, 0), err
 }
 }
 
 
-func (f *FeedbagStore) Upsert(screenName string, items []FeedbagItem) error {
+func (f *FeedbagStore) Upsert(screenName string, items []oscar.FeedbagItem) error {
 
 
 	q := `
 	q := `
 		INSERT INTO feedbag (ScreenName, groupID, itemID, classID, name, attributes, lastModified)
 		INSERT INTO feedbag (ScreenName, groupID, itemID, classID, name, attributes, lastModified)
@@ -130,7 +129,7 @@ func (f *FeedbagStore) Upsert(screenName string, items []FeedbagItem) error {
 	for _, item := range items {
 	for _, item := range items {
 
 
 		buf := &bytes.Buffer{}
 		buf := &bytes.Buffer{}
-		if err := item.TLVLBlock.writeTLV(buf); err != nil {
+		if err := item.TLVLBlock.WriteTLV(buf); err != nil {
 			return err
 			return err
 		}
 		}
 
 
@@ -286,12 +285,12 @@ func (f *FeedbagStore) RetrieveProfile(screenName string) (string, error) {
 }
 }
 
 
 func (f *FeedbagStore) UpsertProfile(screenName string, body string) error {
 func (f *FeedbagStore) UpsertProfile(screenName string, body string) error {
-	sql := `
+	q := `
 		INSERT INTO profile (ScreenName, body)
 		INSERT INTO profile (ScreenName, body)
 		VALUES (?, ?)
 		VALUES (?, ?)
 		ON CONFLICT (ScreenName)
 		ON CONFLICT (ScreenName)
 			DO UPDATE SET body = excluded.body
 			DO UPDATE SET body = excluded.body
 	`
 	`
-	_, err := f.db.Exec(sql, screenName, body)
+	_, err := f.db.Exec(q, screenName, body)
 	return err
 	return err
 }
 }

+ 11 - 10
oscar/feedbag_store_test.go → server/feedbag_store_test.go

@@ -1,6 +1,7 @@
-package oscar
+package server
 
 
 import (
 import (
+	"github.com/mkaminski/goaim/oscar"
 	"os"
 	"os"
 	"reflect"
 	"reflect"
 	"testing"
 	"testing"
@@ -23,13 +24,13 @@ func TestFeedbagStore(t *testing.T) {
 		t.Fatalf("failed to create new feedbag store: %s", err.Error())
 		t.Fatalf("failed to create new feedbag store: %s", err.Error())
 	}
 	}
 
 
-	itemsIn := []FeedbagItem{
+	itemsIn := []oscar.FeedbagItem{
 		{
 		{
 			GroupID:   0,
 			GroupID:   0,
 			ItemID:    1805,
 			ItemID:    1805,
 			ClassID:   3,
 			ClassID:   3,
 			Name:      "spimmer1234",
 			Name:      "spimmer1234",
-			TLVLBlock: TLVLBlock{},
+			TLVLBlock: oscar.TLVLBlock{},
 		},
 		},
 		{
 		{
 			GroupID: 0x0A,
 			GroupID: 0x0A,
@@ -69,17 +70,17 @@ func TestFeedbagDelete(t *testing.T) {
 		t.Fatalf("failed to create new feedbag store: %s", err.Error())
 		t.Fatalf("failed to create new feedbag store: %s", err.Error())
 	}
 	}
 
 
-	itemsIn := []FeedbagItem{
+	itemsIn := []oscar.FeedbagItem{
 		{
 		{
 			GroupID: 0,
 			GroupID: 0,
 			ItemID:  1805,
 			ItemID:  1805,
 			ClassID: 3,
 			ClassID: 3,
 			Name:    "spimmer1234",
 			Name:    "spimmer1234",
-			TLVLBlock: TLVLBlock{
-				TLVList: TLVList{
+			TLVLBlock: oscar.TLVLBlock{
+				TLVList: oscar.TLVList{
 					{
 					{
-						tType: 0x01,
-						val:   uint16(1000),
+						TType: 0x01,
+						Val:   uint16(1000),
 					},
 					},
 				},
 				},
 			},
 			},
@@ -102,7 +103,7 @@ func TestFeedbagDelete(t *testing.T) {
 		t.Fatalf("failed to upsert: %s", err.Error())
 		t.Fatalf("failed to upsert: %s", err.Error())
 	}
 	}
 
 
-	if err := f.Delete(screenName, []FeedbagItem{itemsIn[0]}); err != nil {
+	if err := f.Delete(screenName, []oscar.FeedbagItem{itemsIn[0]}); err != nil {
 		t.Fatalf("failed to delete: %s", err.Error())
 		t.Fatalf("failed to delete: %s", err.Error())
 	}
 	}
 
 
@@ -159,7 +160,7 @@ func TestLastModifiedNotEmpty(t *testing.T) {
 		t.Fatalf("failed to create new feedbag store: %s", err.Error())
 		t.Fatalf("failed to create new feedbag store: %s", err.Error())
 	}
 	}
 
 
-	itemsIn := []FeedbagItem{
+	itemsIn := []oscar.FeedbagItem{
 		{
 		{
 			GroupID: 0x0A,
 			GroupID: 0x0A,
 			ItemID:  0,
 			ItemID:  0,

+ 78 - 77
oscar/icbm.go → server/icbm.go

@@ -1,8 +1,9 @@
-package oscar
+package server
 
 
 import (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"github.com/mkaminski/goaim/oscar"
 	"io"
 	"io"
 )
 )
 
 
@@ -30,8 +31,8 @@ const (
 	ICBMSinReply                  = 0x0017
 	ICBMSinReply                  = 0x0017
 )
 )
 
 
-func routeICBM(sm *SessionManager, fm *FeedbagStore, sess *Session, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
-	switch snac.subGroup {
+func routeICBM(sm *SessionManager, fm *FeedbagStore, sess *Session, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+	switch snac.SubGroup {
 	case ICBMErr:
 	case ICBMErr:
 		panic("not implemented")
 		panic("not implemented")
 	case ICBMAddParameters:
 	case ICBMAddParameters:
@@ -75,14 +76,14 @@ func routeICBM(sm *SessionManager, fm *FeedbagStore, sess *Session, snac snacFra
 	return nil
 	return nil
 }
 }
 
 
-func SendAndReceiveICBMParameterReply(snac snacFrame, _ io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceiveICBMParameterReply(snac oscar.SnacFrame, _ io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("sendAndReceiveICBMParameterReply read SNAC frame: %+v\n", snac)
 	fmt.Printf("sendAndReceiveICBMParameterReply read SNAC frame: %+v\n", snac)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: ICBM,
-		subGroup:  ICBMParameterReply,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: ICBM,
+		SubGroup:  ICBMParameterReply,
 	}
 	}
-	snacPayloadOut := SNAC_0x04_0x05_ICBMParameterReply{
+	snacPayloadOut := oscar.SNAC_0x04_0x05_ICBMParameterReply{
 		MaxSlots:             100,
 		MaxSlots:             100,
 		ICBMFlags:            3,
 		ICBMFlags:            3,
 		MaxIncomingICBMLen:   512,
 		MaxIncomingICBMLen:   512,
@@ -94,11 +95,11 @@ func SendAndReceiveICBMParameterReply(snac snacFrame, _ io.Reader, w io.Writer,
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func SendAndReceiveChannelMsgTohost(sm *SessionManager, fm *FeedbagStore, sess *Session, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceiveChannelMsgTohost(sm *SessionManager, fm *FeedbagStore, sess *Session, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("SendAndReceiveChannelMsgTohost read SNAC frame: %+v\n", snac)
 	fmt.Printf("SendAndReceiveChannelMsgTohost read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x04_0x06_ICBMChannelMsgToHost{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x04_0x06_ICBMChannelMsgToHost{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -107,11 +108,11 @@ func SendAndReceiveChannelMsgTohost(sm *SessionManager, fm *FeedbagStore, sess *
 		return err
 		return err
 	}
 	}
 	if blocked != BlockedNo {
 	if blocked != BlockedNo {
-		snacFrameOut := snacFrame{
-			foodGroup: ICBM,
-			subGroup:  ICBMErr,
+		snacFrameOut := oscar.SnacFrame{
+			FoodGroup: ICBM,
+			SubGroup:  ICBMErr,
 		}
 		}
-		snacPayloadOut := snacError{
+		snacPayloadOut := oscar.SnacError{
 			Code: ErrorCodeNotLoggedOn,
 			Code: ErrorCodeNotLoggedOn,
 		}
 		}
 		if blocked == BlockedA {
 		if blocked == BlockedA {
@@ -123,11 +124,11 @@ func SendAndReceiveChannelMsgTohost(sm *SessionManager, fm *FeedbagStore, sess *
 	recipSess, err := sm.RetrieveByScreenName(snacPayloadIn.ScreenName)
 	recipSess, err := sm.RetrieveByScreenName(snacPayloadIn.ScreenName)
 	if err != nil {
 	if err != nil {
 		if errors.Is(err, errSessNotFound) {
 		if errors.Is(err, errSessNotFound) {
-			snacFrameOut := snacFrame{
-				foodGroup: ICBM,
-				subGroup:  ICBMErr,
+			snacFrameOut := oscar.SnacFrame{
+				FoodGroup: ICBM,
+				SubGroup:  ICBMErr,
 			}
 			}
-			snacPayloadOut := snacError{
+			snacPayloadOut := oscar.SnacError{
 				Code: ErrorCodeNotLoggedOn,
 				Code: ErrorCodeNotLoggedOn,
 			}
 			}
 			return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 			return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
@@ -135,57 +136,57 @@ func SendAndReceiveChannelMsgTohost(sm *SessionManager, fm *FeedbagStore, sess *
 		return err
 		return err
 	}
 	}
 
 
-	clientIM := SNAC_0x04_0x07_ICBMChannelMsgToClient{
+	clientIM := oscar.SNAC_0x04_0x07_ICBMChannelMsgToClient{
 		Cookie:    snacPayloadIn.Cookie,
 		Cookie:    snacPayloadIn.Cookie,
 		ChannelID: snacPayloadIn.ChannelID,
 		ChannelID: snacPayloadIn.ChannelID,
-		TLVUserInfo: TLVUserInfo{
+		TLVUserInfo: oscar.TLVUserInfo{
 			ScreenName:   sess.ScreenName,
 			ScreenName:   sess.ScreenName,
 			WarningLevel: sess.GetWarning(),
 			WarningLevel: sess.GetWarning(),
 		},
 		},
-		TLVRestBlock: TLVRestBlock{
-			TLVList: TLVList{
+		TLVRestBlock: oscar.TLVRestBlock{
+			TLVList: oscar.TLVList{
 				{
 				{
-					tType: 0x0B,
-					val:   []byte{},
+					TType: 0x0B,
+					Val:   []byte{},
 				},
 				},
 			},
 			},
 		},
 		},
 	}
 	}
-	if messagePayload, found := snacPayloadIn.TLVRestBlock.getSlice(0x02); found {
-		clientIM.TLVRestBlock.addTLV(TLV{
-			tType: 0x02,
-			val:   messagePayload,
+	if messagePayload, found := snacPayloadIn.TLVRestBlock.GetSlice(0x02); found {
+		clientIM.TLVRestBlock.AddTLV(oscar.TLV{
+			TType: 0x02,
+			Val:   messagePayload,
 		})
 		})
 	}
 	}
-	if messagePayload, found := snacPayloadIn.TLVRestBlock.getSlice(0x05); found {
-		clientIM.TLVRestBlock.addTLV(TLV{
-			tType: 0x05,
-			val:   messagePayload,
+	if messagePayload, found := snacPayloadIn.TLVRestBlock.GetSlice(0x05); found {
+		clientIM.TLVRestBlock.AddTLV(oscar.TLV{
+			TType: 0x05,
+			Val:   messagePayload,
 		})
 		})
 	}
 	}
-	if t, hasAutoResp := snacPayloadIn.getTLV(0x04); hasAutoResp {
-		clientIM.TLVRestBlock.addTLV(t)
+	if t, hasAutoResp := snacPayloadIn.GetTLV(0x04); hasAutoResp {
+		clientIM.TLVRestBlock.AddTLV(t)
 	}
 	}
 
 
 	sm.SendToScreenName(recipSess.ScreenName, XMessage{
 	sm.SendToScreenName(recipSess.ScreenName, XMessage{
-		snacFrame: snacFrame{
-			foodGroup: ICBM,
-			subGroup:  ICBMChannelMsgToclient,
+		snacFrame: oscar.SnacFrame{
+			FoodGroup: ICBM,
+			SubGroup:  ICBMChannelMsgToclient,
 		},
 		},
 		snacOut: clientIM,
 		snacOut: clientIM,
 	})
 	})
 
 
-	if _, requestedConfirmation := snacPayloadIn.TLVRestBlock.getSlice(0x03); !requestedConfirmation {
+	if _, requestedConfirmation := snacPayloadIn.TLVRestBlock.GetSlice(0x03); !requestedConfirmation {
 		// don't ack message
 		// don't ack message
 		return nil
 		return nil
 	}
 	}
 
 
 	// ack message back to sender
 	// ack message back to sender
-	snacFrameOut := snacFrame{
-		foodGroup: ICBM,
-		subGroup:  ICBMHostAck,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: ICBM,
+		SubGroup:  ICBMHostAck,
 	}
 	}
-	snacPayloadOut := SNAC_0x04_0x0C_ICBMHostAck{
+	snacPayloadOut := oscar.SNAC_0x04_0x0C_ICBMHostAck{
 		Cookie:     snacPayloadIn.Cookie,
 		Cookie:     snacPayloadIn.Cookie,
 		ChannelID:  snacPayloadIn.ChannelID,
 		ChannelID:  snacPayloadIn.ChannelID,
 		ScreenName: snacPayloadIn.ScreenName,
 		ScreenName: snacPayloadIn.ScreenName,
@@ -194,11 +195,11 @@ func SendAndReceiveChannelMsgTohost(sm *SessionManager, fm *FeedbagStore, sess *
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func ReceiveAddParameters(snac snacFrame, r io.Reader) error {
+func ReceiveAddParameters(snac oscar.SnacFrame, r io.Reader) error {
 	fmt.Printf("ReceiveAddParameters read SNAC frame: %+v\n", snac)
 	fmt.Printf("ReceiveAddParameters read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x04_0x02_ICBMAddParameters{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x04_0x02_ICBMAddParameters{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -206,11 +207,11 @@ func ReceiveAddParameters(snac snacFrame, r io.Reader) error {
 	return nil
 	return nil
 }
 }
 
 
-func ReceiveClientErr(snac snacFrame, r io.Reader) error {
+func ReceiveClientErr(snac oscar.SnacFrame, r io.Reader) error {
 	fmt.Printf("ReceiveClientErr read SNAC frame: %+v\n", snac)
 	fmt.Printf("ReceiveClientErr read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x04_0x0B_ICBMClientErr{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x04_0x0B_ICBMClientErr{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -218,11 +219,11 @@ func ReceiveClientErr(snac snacFrame, r io.Reader) error {
 	return nil
 	return nil
 }
 }
 
 
-func SendAndReceiveClientEvent(sm *SessionManager, fm *FeedbagStore, sess *Session, snac snacFrame, r io.Reader) error {
+func SendAndReceiveClientEvent(sm *SessionManager, fm *FeedbagStore, sess *Session, snac oscar.SnacFrame, r io.Reader) error {
 	fmt.Printf("SendAndReceiveClientEvent read SNAC frame: %+v\n", snac)
 	fmt.Printf("SendAndReceiveClientEvent read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x04_0x14_ICBMClientEvent{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x04_0x14_ICBMClientEvent{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -235,11 +236,11 @@ func SendAndReceiveClientEvent(sm *SessionManager, fm *FeedbagStore, sess *Sessi
 	}
 	}
 
 
 	sm.SendToScreenName(snacPayloadIn.ScreenName, XMessage{
 	sm.SendToScreenName(snacPayloadIn.ScreenName, XMessage{
-		snacFrame: snacFrame{
-			foodGroup: ICBM,
-			subGroup:  ICBMClientEvent,
+		snacFrame: oscar.SnacFrame{
+			FoodGroup: ICBM,
+			SubGroup:  ICBMClientEvent,
 		},
 		},
-		snacOut: SNAC_0x04_0x14_ICBMClientEvent{
+		snacOut: oscar.SNAC_0x04_0x14_ICBMClientEvent{
 			Cookie:     snacPayloadIn.Cookie,
 			Cookie:     snacPayloadIn.Cookie,
 			ChannelID:  snacPayloadIn.ChannelID,
 			ChannelID:  snacPayloadIn.ChannelID,
 			ScreenName: sess.ScreenName,
 			ScreenName: sess.ScreenName,
@@ -255,22 +256,22 @@ const (
 	evilDeltaAnon = uint16(30)
 	evilDeltaAnon = uint16(30)
 )
 )
 
 
-func SendAndReceiveEvilRequest(sm *SessionManager, fm *FeedbagStore, sess *Session, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceiveEvilRequest(sm *SessionManager, fm *FeedbagStore, sess *Session, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("SendAndReceiveEvilRequest read SNAC frame: %+v\n", snac)
 	fmt.Printf("SendAndReceiveEvilRequest read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x04_0x08_ICBMEvilRequest{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x04_0x08_ICBMEvilRequest{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	// don't let users warn themselves, it causes the AIM client to go into a
 	// don't let users warn themselves, it causes the AIM client to go into a
 	// weird state.
 	// weird state.
 	if snacPayloadIn.ScreenName == sess.ScreenName {
 	if snacPayloadIn.ScreenName == sess.ScreenName {
-		snacFrameOut := snacFrame{
-			foodGroup: ICBM,
-			subGroup:  ICBMErr,
+		snacFrameOut := oscar.SnacFrame{
+			FoodGroup: ICBM,
+			SubGroup:  ICBMErr,
 		}
 		}
-		snacPayloadOut := snacError{
+		snacPayloadOut := oscar.SnacError{
 			Code: ErrorCodeNotSupportedByHost,
 			Code: ErrorCodeNotSupportedByHost,
 		}
 		}
 		return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 		return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
@@ -281,11 +282,11 @@ func SendAndReceiveEvilRequest(sm *SessionManager, fm *FeedbagStore, sess *Sessi
 		return err
 		return err
 	}
 	}
 	if blocked != BlockedNo {
 	if blocked != BlockedNo {
-		snacFrameOut := snacFrame{
-			foodGroup: ICBM,
-			subGroup:  ICBMErr,
+		snacFrameOut := oscar.SnacFrame{
+			FoodGroup: ICBM,
+			SubGroup:  ICBMErr,
 		}
 		}
-		snacPayloadOut := snacError{
+		snacPayloadOut := oscar.SnacError{
 			Code: ErrorCodeNotLoggedOn,
 			Code: ErrorCodeNotLoggedOn,
 		}
 		}
 		return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 		return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
@@ -302,11 +303,11 @@ func SendAndReceiveEvilRequest(sm *SessionManager, fm *FeedbagStore, sess *Sessi
 	}
 	}
 	recipSess.IncreaseWarning(increase)
 	recipSess.IncreaseWarning(increase)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: ICBM,
-		subGroup:  ICBMEvilReply,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: ICBM,
+		SubGroup:  ICBMEvilReply,
 	}
 	}
-	snacPayloadOut := SNAC_0x04_0x09_ICBMEvilReply{
+	snacPayloadOut := oscar.SNAC_0x04_0x09_ICBMEvilReply{
 		EvilDeltaApplied: increase,
 		EvilDeltaApplied: increase,
 		UpdatedEvilValue: recipSess.GetWarning(),
 		UpdatedEvilValue: recipSess.GetWarning(),
 	}
 	}
@@ -317,23 +318,23 @@ func SendAndReceiveEvilRequest(sm *SessionManager, fm *FeedbagStore, sess *Sessi
 
 
 	var notif any
 	var notif any
 	if snacPayloadIn.SendAs == 0 {
 	if snacPayloadIn.SendAs == 0 {
-		notif = SNAC_0x01_0x10_OServiceEvilNotification{
+		notif = oscar.SNAC_0x01_0x10_OServiceEvilNotification{
 			NewEvil: recipSess.GetWarning(),
 			NewEvil: recipSess.GetWarning(),
-			TLVUserInfo: TLVUserInfo{
+			TLVUserInfo: oscar.TLVUserInfo{
 				ScreenName:   sess.ScreenName,
 				ScreenName:   sess.ScreenName,
 				WarningLevel: recipSess.GetWarning(),
 				WarningLevel: recipSess.GetWarning(),
 			},
 			},
 		}
 		}
 	} else {
 	} else {
-		notif = SNAC_0x01_0x10_OServiceEvilNotificationAnon{
+		notif = oscar.SNAC_0x01_0x10_OServiceEvilNotificationAnon{
 			NewEvil: recipSess.GetWarning(),
 			NewEvil: recipSess.GetWarning(),
 		}
 		}
 	}
 	}
 
 
 	sm.SendToScreenName(recipSess.ScreenName, XMessage{
 	sm.SendToScreenName(recipSess.ScreenName, XMessage{
-		snacFrame: snacFrame{
-			foodGroup: OSERVICE,
-			subGroup:  OServiceEvilNotification,
+		snacFrame: oscar.SnacFrame{
+			FoodGroup: OSERVICE,
+			SubGroup:  OServiceEvilNotification,
 		},
 		},
 		snacOut: notif,
 		snacOut: notif,
 	})
 	})

+ 76 - 75
oscar/locate.go → server/locate.go

@@ -1,8 +1,9 @@
-package oscar
+package server
 
 
 import (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"github.com/mkaminski/goaim/oscar"
 	"io"
 	"io"
 )
 )
 
 
@@ -30,8 +31,8 @@ const (
 	LocateUserInfoQuery2              = 0x0015
 	LocateUserInfoQuery2              = 0x0015
 )
 )
 
 
-func routeLocate(sess *Session, sm *SessionManager, fm *FeedbagStore, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
-	switch snac.subGroup {
+func routeLocate(sess *Session, sm *SessionManager, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+	switch snac.SubGroup {
 	case LocateErr:
 	case LocateErr:
 		panic("not implemented")
 		panic("not implemented")
 	case LocateRightsQuery:
 	case LocateRightsQuery:
@@ -73,35 +74,35 @@ func routeLocate(sess *Session, sm *SessionManager, fm *FeedbagStore, snac snacF
 	return nil
 	return nil
 }
 }
 
 
-func SendAndReceiveLocateRights(snac snacFrame, w io.Writer, sequence *uint32) error {
+func SendAndReceiveLocateRights(snac oscar.SnacFrame, w io.Writer, sequence *uint32) error {
 	fmt.Printf("sendAndReceiveLocateRights read SNAC frame: %+v\n", snac)
 	fmt.Printf("sendAndReceiveLocateRights read SNAC frame: %+v\n", snac)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: LOCATE,
-		subGroup:  LocateRightsReply,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: LOCATE,
+		SubGroup:  LocateRightsReply,
 	}
 	}
-	snacPayloadOut := SNAC_0x02_0x03_LocateRightsReply{
-		TLVRestBlock: TLVRestBlock{
-			TLVList: TLVList{
+	snacPayloadOut := oscar.SNAC_0x02_0x03_LocateRightsReply{
+		TLVRestBlock: oscar.TLVRestBlock{
+			TLVList: oscar.TLVList{
 				{
 				{
-					tType: 0x01,
-					val:   uint16(1000),
+					TType: 0x01,
+					Val:   uint16(1000),
 				},
 				},
 				{
 				{
-					tType: 0x02,
-					val:   uint16(1000),
+					TType: 0x02,
+					Val:   uint16(1000),
 				},
 				},
 				{
 				{
-					tType: 0x03,
-					val:   uint16(1000),
+					TType: 0x03,
+					Val:   uint16(1000),
 				},
 				},
 				{
 				{
-					tType: 0x04,
-					val:   uint16(1000),
+					TType: 0x04,
+					Val:   uint16(1000),
 				},
 				},
 				{
 				{
-					tType: 0x05,
-					val:   uint16(1000),
+					TType: 0x05,
+					Val:   uint16(1000),
 				},
 				},
 			},
 			},
 		},
 		},
@@ -124,23 +125,23 @@ var (
 	LocateTlvTagsInfoHtmlInfoType    = uint16(0x0D)
 	LocateTlvTagsInfoHtmlInfoType    = uint16(0x0D)
 )
 )
 
 
-func ReceiveSetInfo(sess *Session, sm *SessionManager, fm *FeedbagStore, snac snacFrame, r io.Reader) error {
+func ReceiveSetInfo(sess *Session, sm *SessionManager, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader) error {
 	fmt.Printf("ReceiveSetInfo read SNAC frame: %+v\n", snac)
 	fmt.Printf("ReceiveSetInfo read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x02_0x04_LocateSetInfo{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x02_0x04_LocateSetInfo{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	// update profile
 	// update profile
-	if profile, hasProfile := snacPayloadIn.getString(LocateTlvTagsInfoSigData); hasProfile {
+	if profile, hasProfile := snacPayloadIn.GetString(LocateTlvTagsInfoSigData); hasProfile {
 		if err := fm.UpsertProfile(sess.ScreenName, profile); err != nil {
 		if err := fm.UpsertProfile(sess.ScreenName, profile); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
 
 
 	// broadcast away message change to buddies
 	// broadcast away message change to buddies
-	if awayMsg, hasAwayMsg := snacPayloadIn.getString(LocateTlvTagsInfoUnavailableData); hasAwayMsg {
+	if awayMsg, hasAwayMsg := snacPayloadIn.GetString(LocateTlvTagsInfoUnavailableData); hasAwayMsg {
 		sess.SetAwayMessage(awayMsg)
 		sess.SetAwayMessage(awayMsg)
 		if err := NotifyArrival(sess, sm, fm); err != nil {
 		if err := NotifyArrival(sess, sm, fm); err != nil {
 			return err
 			return err
@@ -152,11 +153,11 @@ func ReceiveSetInfo(sess *Session, sm *SessionManager, fm *FeedbagStore, snac sn
 	return nil
 	return nil
 }
 }
 
 
-func ReceiveLocateGetDirInfo(snac snacFrame, r io.Reader) error {
+func ReceiveLocateGetDirInfo(snac oscar.SnacFrame, r io.Reader) error {
 	fmt.Printf("ReceiveLocateGetDirInfo read SNAC frame: %+v\n", snac)
 	fmt.Printf("ReceiveLocateGetDirInfo read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x02_0x0B_LocateGetDirInfo{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x02_0x0B_LocateGetDirInfo{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -165,11 +166,11 @@ func ReceiveLocateGetDirInfo(snac snacFrame, r io.Reader) error {
 	return nil
 	return nil
 }
 }
 
 
-func SendAndReceiveUserInfoQuery2(sess *Session, sm *SessionManager, fm *FeedbagStore, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceiveUserInfoQuery2(sess *Session, sm *SessionManager, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("SendAndReceiveUserInfoQuery2 read SNAC frame: %+v\n", snac)
 	fmt.Printf("SendAndReceiveUserInfoQuery2 read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x02_0x15_LocateUserInfoQuery2{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x02_0x15_LocateUserInfoQuery2{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -178,11 +179,11 @@ func SendAndReceiveUserInfoQuery2(sess *Session, sm *SessionManager, fm *Feedbag
 		return err
 		return err
 	}
 	}
 	if blocked != BlockedNo {
 	if blocked != BlockedNo {
-		snacFrameOut := snacFrame{
-			foodGroup: LOCATE,
-			subGroup:  LocateErr,
+		snacFrameOut := oscar.SnacFrame{
+			FoodGroup: LOCATE,
+			SubGroup:  LocateErr,
 		}
 		}
-		snacPayloadOut := snacError{
+		snacPayloadOut := oscar.SnacError{
 			Code: ErrorCodeNotLoggedOn,
 			Code: ErrorCodeNotLoggedOn,
 		}
 		}
 		return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 		return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
@@ -191,31 +192,31 @@ func SendAndReceiveUserInfoQuery2(sess *Session, sm *SessionManager, fm *Feedbag
 	buddySess, err := sm.RetrieveByScreenName(snacPayloadIn.ScreenName)
 	buddySess, err := sm.RetrieveByScreenName(snacPayloadIn.ScreenName)
 	if err != nil {
 	if err != nil {
 		if errors.Is(err, errSessNotFound) {
 		if errors.Is(err, errSessNotFound) {
-			snacFrameOut := snacFrame{
-				foodGroup: LOCATE,
-				subGroup:  LocateErr,
+			snacFrameOut := oscar.SnacFrame{
+				FoodGroup: LOCATE,
+				SubGroup:  LocateErr,
 			}
 			}
-			snacPayloadOut := snacError{
+			snacPayloadOut := oscar.SnacError{
 				Code: ErrorCodeNotLoggedOn,
 				Code: ErrorCodeNotLoggedOn,
 			}
 			}
 			return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 			return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 		}
 		}
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: LOCATE,
-		subGroup:  LocateUserInfoReply,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: LOCATE,
+		SubGroup:  LocateUserInfoReply,
 	}
 	}
-	snacPayloadOut := SNAC_0x02_0x06_LocateUserInfoReply{
-		TLVUserInfo: TLVUserInfo{
+	snacPayloadOut := oscar.SNAC_0x02_0x06_LocateUserInfoReply{
+		TLVUserInfo: oscar.TLVUserInfo{
 			ScreenName:   snacPayloadIn.ScreenName,
 			ScreenName:   snacPayloadIn.ScreenName,
 			WarningLevel: buddySess.GetWarning(),
 			WarningLevel: buddySess.GetWarning(),
-			TLVBlock: TLVBlock{
+			TLVBlock: oscar.TLVBlock{
 				TLVList: buddySess.GetUserInfo(),
 				TLVList: buddySess.GetUserInfo(),
 			},
 			},
 		},
 		},
-		ClientProfile: TLVRestBlock{},
-		AwayMessage:   TLVRestBlock{},
+		ClientProfile: oscar.TLVRestBlock{},
+		AwayMessage:   oscar.TLVRestBlock{},
 	}
 	}
 
 
 	// profile
 	// profile
@@ -223,39 +224,39 @@ func SendAndReceiveUserInfoQuery2(sess *Session, sm *SessionManager, fm *Feedbag
 		profile, err := fm.RetrieveProfile(snacPayloadIn.ScreenName)
 		profile, err := fm.RetrieveProfile(snacPayloadIn.ScreenName)
 		if err != nil {
 		if err != nil {
 			if err == errUserNotExist {
 			if err == errUserNotExist {
-				snacFrameOut := snacFrame{
-					foodGroup: LOCATE,
-					subGroup:  LocateErr,
+				snacFrameOut := oscar.SnacFrame{
+					FoodGroup: LOCATE,
+					SubGroup:  LocateErr,
 				}
 				}
-				snacPayloadOut := snacError{
+				snacPayloadOut := oscar.SnacError{
 					Code: ErrorCodeNotLoggedOn,
 					Code: ErrorCodeNotLoggedOn,
 				}
 				}
 				return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 				return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 			}
 			}
 			return err
 			return err
 		}
 		}
-		snacPayloadOut.ClientProfile.TLVList = append(snacPayloadOut.ClientProfile.TLVList, []TLV{
+		snacPayloadOut.ClientProfile.TLVList = append(snacPayloadOut.ClientProfile.TLVList, []oscar.TLV{
 			{
 			{
-				tType: 0x01,
-				val:   `text/aolrtf; charset="us-ascii"`,
+				TType: 0x01,
+				Val:   `text/aolrtf; charset="us-ascii"`,
 			},
 			},
 			{
 			{
-				tType: 0x02,
-				val:   profile,
+				TType: 0x02,
+				Val:   profile,
 			},
 			},
 		}...)
 		}...)
 	}
 	}
 
 
 	// away message
 	// away message
 	if snacPayloadIn.Type2&2 == 2 {
 	if snacPayloadIn.Type2&2 == 2 {
-		snacPayloadOut.ClientProfile.TLVList = append(snacPayloadOut.ClientProfile.TLVList, []TLV{
+		snacPayloadOut.ClientProfile.TLVList = append(snacPayloadOut.ClientProfile.TLVList, []oscar.TLV{
 			{
 			{
-				tType: 0x03,
-				val:   `text/aolrtf; charset="us-ascii"`,
+				TType: 0x03,
+				Val:   `text/aolrtf; charset="us-ascii"`,
 			},
 			},
 			{
 			{
-				tType: 0x04,
-				val:   buddySess.GetAwayMessage(),
+				TType: 0x04,
+				Val:   buddySess.GetAwayMessage(),
 			},
 			},
 		}...)
 		}...)
 	}
 	}
@@ -263,38 +264,38 @@ func SendAndReceiveUserInfoQuery2(sess *Session, sm *SessionManager, fm *Feedbag
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func SendAndReceiveSetDirInfo(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceiveSetDirInfo(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("SendAndReceiveSetDirInfo read SNAC frame: %+v\n", snac)
 	fmt.Printf("SendAndReceiveSetDirInfo read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x02_0x09_LocateSetDirInfo{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x02_0x09_LocateSetDirInfo{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: LOCATE,
-		subGroup:  LocateSetDirReply,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: LOCATE,
+		SubGroup:  LocateSetDirReply,
 	}
 	}
-	snacPayloadOut := SNAC_0x02_0x0A_LocateSetDirReply{
+	snacPayloadOut := oscar.SNAC_0x02_0x0A_LocateSetDirReply{
 		Result: 1,
 		Result: 1,
 	}
 	}
 
 
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func SendAndReceiveSetKeywordInfo(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceiveSetKeywordInfo(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("SendAndReceiveSetKeywordInfo read SNAC frame: %+v\n", snac)
 	fmt.Printf("SendAndReceiveSetKeywordInfo read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x02_0x0F_LocateSetKeywordInfo{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x02_0x0F_LocateSetKeywordInfo{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: LOCATE,
-		subGroup:  LocateSetKeywordReply,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: LOCATE,
+		SubGroup:  LocateSetKeywordReply,
 	}
 	}
-	snacPayloadOut := SNAC_0x02_0x10_LocateSetKeywordReply{
+	snacPayloadOut := oscar.SNAC_0x02_0x10_LocateSetKeywordReply{
 		Unknown: 1,
 		Unknown: 1,
 	}
 	}
 
 

+ 83 - 82
oscar/oservice.go → server/oservice.go

@@ -1,9 +1,10 @@
-package oscar
+package server
 
 
 import (
 import (
 	"bytes"
 	"bytes"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
+	"github.com/mkaminski/goaim/oscar"
 	"io"
 	"io"
 	"time"
 	"time"
 )
 )
@@ -46,8 +47,8 @@ const (
 	OServiceBartReply2               = 0x0023
 	OServiceBartReply2               = 0x0023
 )
 )
 
 
-func routeOService(cfg Config, ready OnReadyCB, cr *ChatRegistry, sm *SessionManager, fm *FeedbagStore, sess *Session, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
-	switch snac.subGroup {
+func routeOService(cfg Config, ready OnReadyCB, cr *ChatRegistry, sm *SessionManager, fm *FeedbagStore, sess *Session, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+	switch snac.SubGroup {
 	case OServiceErr:
 	case OServiceErr:
 		panic("not implemented")
 		panic("not implemented")
 	case OServiceClientOnline:
 	case OServiceClientOnline:
@@ -119,46 +120,46 @@ func routeOService(cfg Config, ready OnReadyCB, cr *ChatRegistry, sm *SessionMan
 
 
 func WriteOServiceHostOnline(foodGroups []uint16, w io.Writer, sequence *uint32) error {
 func WriteOServiceHostOnline(foodGroups []uint16, w io.Writer, sequence *uint32) error {
 	fmt.Println("writeOServiceHostOnline...")
 	fmt.Println("writeOServiceHostOnline...")
-	snacFrameOut := snacFrame{
-		foodGroup: OSERVICE,
-		subGroup:  OServiceHostOnline,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: OSERVICE,
+		SubGroup:  OServiceHostOnline,
 	}
 	}
-	snacPayloadOut := SNAC_0x01_0x03_OServiceHostOnline{
+	snacPayloadOut := oscar.SNAC_0x01_0x03_OServiceHostOnline{
 		FoodGroups: foodGroups,
 		FoodGroups: foodGroups,
 	}
 	}
-	return writeOutSNAC(snacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
+	return writeOutSNAC(oscar.SnacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func ReceiveAndSendHostVersions(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveAndSendHostVersions(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("receiveAndSendHostVersions read SNAC frame: %+v\n", snac)
 	fmt.Printf("receiveAndSendHostVersions read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x01_0x17_OServiceClientVersions{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x01_0x17_OServiceClientVersions{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	fmt.Printf("receiveAndSendHostVersions read SNAC: %+v\n", snacPayloadIn)
 	fmt.Printf("receiveAndSendHostVersions read SNAC: %+v\n", snacPayloadIn)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: OSERVICE,
-		subGroup:  OServiceHostVersions,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: OSERVICE,
+		SubGroup:  OServiceHostVersions,
 	}
 	}
-	snacPayloadOut := SNAC_0x01_0x18_OServiceHostVersions{
+	snacPayloadOut := oscar.SNAC_0x01_0x18_OServiceHostVersions{
 		Versions: snacPayloadIn.Versions,
 		Versions: snacPayloadIn.Versions,
 	}
 	}
 
 
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func ReceiveAndSendServiceRateParams(snac snacFrame, _ io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveAndSendServiceRateParams(snac oscar.SnacFrame, _ io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("receiveAndSendServiceRateParams read SNAC frame: %+v\n", snac)
 	fmt.Printf("receiveAndSendServiceRateParams read SNAC frame: %+v\n", snac)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: OSERVICE,
-		subGroup:  OServiceRateParamsReply,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: OSERVICE,
+		SubGroup:  OServiceRateParamsReply,
 	}
 	}
 
 
-	snacPayloadOut := SNAC_0x01_0x07_OServiceRateParamsReply{
+	snacPayloadOut := oscar.SNAC_0x01_0x07_OServiceRateParamsReply{
 		RateClasses: []struct {
 		RateClasses: []struct {
 			ID              uint16
 			ID              uint16
 			WindowSize      uint32
 			WindowSize      uint32
@@ -216,18 +217,18 @@ func ReceiveAndSendServiceRateParams(snac snacFrame, _ io.Reader, w io.Writer, s
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func ReceiveAndSendServiceRequestSelfInfo(sess *Session, snac snacFrame, _ io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveAndSendServiceRequestSelfInfo(sess *Session, snac oscar.SnacFrame, _ io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("receiveAndSendServiceRequestSelfInfo read SNAC frame: %+v\n", snac)
 	fmt.Printf("receiveAndSendServiceRequestSelfInfo read SNAC frame: %+v\n", snac)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: OSERVICE,
-		subGroup:  OServiceUserInfoUpdate,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: OSERVICE,
+		SubGroup:  OServiceUserInfoUpdate,
 	}
 	}
-	snacPayloadOut := SNAC_0x01_0x0F_OServiceUserInfoUpdate{
-		TLVUserInfo: TLVUserInfo{
+	snacPayloadOut := oscar.SNAC_0x01_0x0F_OServiceUserInfoUpdate{
+		TLVUserInfo: oscar.TLVUserInfo{
 			ScreenName:   sess.ScreenName,
 			ScreenName:   sess.ScreenName,
 			WarningLevel: sess.GetWarning(),
 			WarningLevel: sess.GetWarning(),
-			TLVBlock: TLVBlock{
+			TLVBlock: oscar.TLVBlock{
 				TLVList: sess.GetUserInfo(),
 				TLVList: sess.GetUserInfo(),
 			},
 			},
 		},
 		},
@@ -236,11 +237,11 @@ func ReceiveAndSendServiceRequestSelfInfo(sess *Session, snac snacFrame, _ io.Re
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func ReceiveRateParamsSubAdd(snac snacFrame, r io.Reader) error {
+func ReceiveRateParamsSubAdd(snac oscar.SnacFrame, r io.Reader) error {
 	fmt.Printf("receiveRateParamsSubAdd read SNAC frame: %+v\n", snac)
 	fmt.Printf("receiveRateParamsSubAdd read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x01_0x08_OServiceRateParamsSubAdd{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x01_0x08_OServiceRateParamsSubAdd{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -251,11 +252,11 @@ func ReceiveRateParamsSubAdd(snac snacFrame, r io.Reader) error {
 
 
 type OnReadyCB func(sess *Session, sm *SessionManager, r io.Reader, w io.Writer, sequence *uint32) error
 type OnReadyCB func(sess *Session, sm *SessionManager, r io.Reader, w io.Writer, sequence *uint32) error
 
 
-func ReceiveClientOnline(onReadyCB OnReadyCB, sess *Session, sm *SessionManager, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveClientOnline(onReadyCB OnReadyCB, sess *Session, sm *SessionManager, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("receiveClientOnline read SNAC frame: %+v\n", snac)
 	fmt.Printf("receiveClientOnline read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x01_0x02_OServiceClientOnline{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x01_0x02_OServiceClientOnline{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -285,36 +286,36 @@ func GetOnlineBuddies(w io.Writer, sess *Session, sm *SessionManager, fm *Feedba
 			continue
 			continue
 		}
 		}
 
 
-		snacFrameOut := snacFrame{
-			foodGroup: BUDDY,
-			subGroup:  BuddyArrived,
+		snacFrameOut := oscar.SnacFrame{
+			FoodGroup: BUDDY,
+			SubGroup:  BuddyArrived,
 		}
 		}
-		snacPayloadOut := SNAC_0x03_0x0A_BuddyArrived{
-			TLVUserInfo: TLVUserInfo{
+		snacPayloadOut := oscar.SNAC_0x03_0x0A_BuddyArrived{
+			TLVUserInfo: oscar.TLVUserInfo{
 				ScreenName:   buddies,
 				ScreenName:   buddies,
 				WarningLevel: sess.GetWarning(),
 				WarningLevel: sess.GetWarning(),
-				TLVBlock: TLVBlock{
+				TLVBlock: oscar.TLVBlock{
 					TLVList: sess.GetUserInfo(),
 					TLVList: sess.GetUserInfo(),
 				},
 				},
 			},
 			},
 		}
 		}
 
 
-		if err := writeOutSNAC(snacFrame{}, snacFrameOut, snacPayloadOut, sequence, w); err != nil {
+		if err := writeOutSNAC(oscar.SnacFrame{}, snacFrameOut, snacPayloadOut, sequence, w); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-func ReceiveSetUserInfoFields(sess *Session, sm *SessionManager, fm *FeedbagStore, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveSetUserInfoFields(sess *Session, sm *SessionManager, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("receiveSetUserInfoFields read SNAC frame: %+v\n", snac)
 	fmt.Printf("receiveSetUserInfoFields read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x01_0x1E_OServiceSetUserInfoFields{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x01_0x1E_OServiceSetUserInfoFields{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	if status, hasStatus := snacPayloadIn.getUint32(0x06); hasStatus {
+	if status, hasStatus := snacPayloadIn.GetUint32(0x06); hasStatus {
 		switch status {
 		switch status {
 		case 0x000:
 		case 0x000:
 			sess.SetInvisible(false)
 			sess.SetInvisible(false)
@@ -331,15 +332,15 @@ func ReceiveSetUserInfoFields(sess *Session, sm *SessionManager, fm *FeedbagStor
 		}
 		}
 	}
 	}
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: OSERVICE,
-		subGroup:  OServiceUserInfoUpdate,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: OSERVICE,
+		SubGroup:  OServiceUserInfoUpdate,
 	}
 	}
-	snacPayloadOut := SNAC_0x01_0x0F_OServiceUserInfoUpdate{
-		TLVUserInfo: TLVUserInfo{
+	snacPayloadOut := oscar.SNAC_0x01_0x0F_OServiceUserInfoUpdate{
+		TLVUserInfo: oscar.TLVUserInfo{
 			ScreenName:   sess.ScreenName,
 			ScreenName:   sess.ScreenName,
 			WarningLevel: sess.GetWarning(),
 			WarningLevel: sess.GetWarning(),
-			TLVBlock: TLVBlock{
+			TLVBlock: oscar.TLVBlock{
 				TLVList: sess.GetUserInfo(),
 				TLVList: sess.GetUserInfo(),
 			},
 			},
 		},
 		},
@@ -348,11 +349,11 @@ func ReceiveSetUserInfoFields(sess *Session, sm *SessionManager, fm *FeedbagStor
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 	return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 }
 }
 
 
-func ReceiveIdleNotification(sess *Session, sm *SessionManager, fm *FeedbagStore, snac snacFrame, r io.Reader) error {
+func ReceiveIdleNotification(sess *Session, sm *SessionManager, fm *FeedbagStore, snac oscar.SnacFrame, r io.Reader) error {
 	fmt.Printf("receiveIdleNotification read SNAC frame: %+v\n", snac)
 	fmt.Printf("receiveIdleNotification read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x01_0x11_OServiceIdleNotification{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x01_0x11_OServiceIdleNotification{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -373,22 +374,22 @@ const (
 	OserviceTlvTagsSslState             = 0x8E
 	OserviceTlvTagsSslState             = 0x8E
 )
 )
 
 
-func ReceiveAndSendServiceRequest(cfg Config, cr *ChatRegistry, sess *Session, snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+func ReceiveAndSendServiceRequest(cfg Config, cr *ChatRegistry, sess *Session, snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("receiveAndSendServiceRequest read SNAC frame: %+v\n", snac)
 	fmt.Printf("receiveAndSendServiceRequest read SNAC frame: %+v\n", snac)
 
 
-	snacPayloadIn := SNAC_0x01_0x04_OServiceServiceRequest{}
-	if err := Unmarshal(&snacPayloadIn, r); err != nil {
+	snacPayloadIn := oscar.SNAC_0x01_0x04_OServiceServiceRequest{}
+	if err := oscar.Unmarshal(&snacPayloadIn, r); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	// this prevents AIM client from crashing when using the
 	// this prevents AIM client from crashing when using the
 	// store/edit email address feature.
 	// store/edit email address feature.
-	if _, hasEditBuddy := snacPayloadIn.getTLV(0x28); hasEditBuddy {
-		snacFrameOut := snacFrame{
-			foodGroup: OSERVICE,
-			subGroup:  OServiceErr,
+	if _, hasEditBuddy := snacPayloadIn.GetTLV(0x28); hasEditBuddy {
+		snacFrameOut := oscar.SnacFrame{
+			FoodGroup: OSERVICE,
+			SubGroup:  OServiceErr,
 		}
 		}
-		snacPayloadOut := SnacOServiceErr{
+		snacPayloadOut := oscar.SnacOServiceErr{
 			Code: ErrorCodeNotSupportedByHost,
 			Code: ErrorCodeNotSupportedByHost,
 		}
 		}
 		return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
 		return writeOutSNAC(snac, snacFrameOut, snacPayloadOut, sequence, w)
@@ -397,22 +398,22 @@ func ReceiveAndSendServiceRequest(cfg Config, cr *ChatRegistry, sess *Session, s
 	fmt.Printf("receiveAndSendServiceRequest read SNAC body: %+v\n", snacPayloadIn)
 	fmt.Printf("receiveAndSendServiceRequest read SNAC body: %+v\n", snacPayloadIn)
 
 
 	// just say that all the services are offline
 	// just say that all the services are offline
-	snacFrameOut := snacFrame{
-		foodGroup: OSERVICE,
-		subGroup:  OServiceServiceResponse,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: OSERVICE,
+		SubGroup:  OServiceServiceResponse,
 	}
 	}
-	snacPayloadOut := SnacOServiceErr{
+	snacPayloadOut := oscar.SnacOServiceErr{
 		Code: 0x06,
 		Code: 0x06,
 	}
 	}
 
 
 	if snacPayloadIn.FoodGroup == CHAT {
 	if snacPayloadIn.FoodGroup == CHAT {
-		roomMeta, ok := snacPayloadIn.getSlice(0x01)
+		roomMeta, ok := snacPayloadIn.GetSlice(0x01)
 		if !ok {
 		if !ok {
 			return errors.New("missing room info")
 			return errors.New("missing room info")
 		}
 		}
 
 
-		roomSnac := SNAC_0x01_0x04_TLVRoomInfo{}
-		if err := Unmarshal(&roomSnac, bytes.NewBuffer(roomMeta)); err != nil {
+		roomSnac := oscar.SNAC_0x01_0x04_TLVRoomInfo{}
+		if err := oscar.Unmarshal(&roomSnac, bytes.NewBuffer(roomMeta)); err != nil {
 			return err
 			return err
 		}
 		}
 
 
@@ -421,7 +422,7 @@ func ReceiveAndSendServiceRequest(cfg Config, cr *ChatRegistry, sess *Session, s
 			SessID: sess.ID,
 			SessID: sess.ID,
 		}
 		}
 		buf := &bytes.Buffer{}
 		buf := &bytes.Buffer{}
-		if err := cookie.write(buf); err != nil {
+		if err := cookie.Write(buf); err != nil {
 			return err
 			return err
 		}
 		}
 
 
@@ -432,28 +433,28 @@ func ReceiveAndSendServiceRequest(cfg Config, cr *ChatRegistry, sess *Session, s
 
 
 		room.SessionManager.NewSessionWithSN(sess.ID, sess.ScreenName)
 		room.SessionManager.NewSessionWithSN(sess.ID, sess.ScreenName)
 
 
-		snacPayloadOut := SNAC_0x01_0x05_OServiceServiceResponse{
-			TLVRestBlock{
-				TLVList: TLVList{
+		snacPayloadOut := oscar.SNAC_0x01_0x05_OServiceServiceResponse{
+			TLVRestBlock: oscar.TLVRestBlock{
+				TLVList: oscar.TLVList{
 					{
 					{
-						tType: OserviceTlvTagsReconnectHere,
-						val:   Address(cfg.OSCARHost, cfg.ChatPort),
+						TType: OserviceTlvTagsReconnectHere,
+						Val:   Address(cfg.OSCARHost, cfg.ChatPort),
 					},
 					},
 					{
 					{
-						tType: OserviceTlvTagsLoginCookie,
-						val:   buf.Bytes(),
+						TType: OserviceTlvTagsLoginCookie,
+						Val:   buf.Bytes(),
 					},
 					},
 					{
 					{
-						tType: OserviceTlvTagsGroupId,
-						val:   snacPayloadIn.FoodGroup,
+						TType: OserviceTlvTagsGroupId,
+						Val:   snacPayloadIn.FoodGroup,
 					},
 					},
 					{
 					{
-						tType: OserviceTlvTagsSslCertname,
-						val:   "",
+						TType: OserviceTlvTagsSslCertname,
+						Val:   "",
 					},
 					},
 					{
 					{
-						tType: OserviceTlvTagsSslState,
-						val:   uint8(0x00),
+						TType: OserviceTlvTagsSslState,
+						Val:   uint8(0x00),
 					},
 					},
 				},
 				},
 			},
 			},

+ 17 - 16
oscar/pd.go → server/pd.go

@@ -1,7 +1,8 @@
-package oscar
+package server
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"github.com/mkaminski/goaim/oscar"
 	"io"
 	"io"
 )
 )
 
 
@@ -19,8 +20,8 @@ const (
 	PDDelTempPermitListEntries        = 0x000B
 	PDDelTempPermitListEntries        = 0x000B
 )
 )
 
 
-func routePD(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
-	switch snac.subGroup {
+func routePD(snac oscar.SnacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
+	switch snac.SubGroup {
 	case PDErr:
 	case PDErr:
 		panic("not implemented")
 		panic("not implemented")
 	case PDRightsQuery:
 	case PDRightsQuery:
@@ -46,27 +47,27 @@ func routePD(snac snacFrame, r io.Reader, w io.Writer, sequence *uint32) error {
 	return nil
 	return nil
 }
 }
 
 
-func SendAndReceivePDRightsQuery(snac snacFrame, _ io.Reader, w io.Writer, sequence *uint32) error {
+func SendAndReceivePDRightsQuery(snac oscar.SnacFrame, _ io.Reader, w io.Writer, sequence *uint32) error {
 	fmt.Printf("sendAndReceivePDRightsQuery read SNAC frame: %+v\n", snac)
 	fmt.Printf("sendAndReceivePDRightsQuery read SNAC frame: %+v\n", snac)
 
 
-	snacFrameOut := snacFrame{
-		foodGroup: PD,
-		subGroup:  PDRightsReply,
+	snacFrameOut := oscar.SnacFrame{
+		FoodGroup: PD,
+		SubGroup:  PDRightsReply,
 	}
 	}
-	snacPayloadOut := SNAC_0x09_0x03_PDRightsReply{
-		TLVRestBlock{
-			TLVList: TLVList{
+	snacPayloadOut := oscar.SNAC_0x09_0x03_PDRightsReply{
+		TLVRestBlock: oscar.TLVRestBlock{
+			TLVList: oscar.TLVList{
 				{
 				{
-					tType: 0x01,
-					val:   uint16(100),
+					TType: 0x01,
+					Val:   uint16(100),
 				},
 				},
 				{
 				{
-					tType: 0x02,
-					val:   uint16(100),
+					TType: 0x02,
+					Val:   uint16(100),
 				},
 				},
 				{
 				{
-					tType: 0x03,
-					val:   uint16(100),
+					TType: 0x03,
+					Val:   uint16(100),
 				},
 				},
 			},
 			},
 		},
 		},

+ 358 - 0
server/protocol.go

@@ -0,0 +1,358 @@
+package server
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"github.com/google/uuid"
+	"github.com/mkaminski/goaim/oscar"
+	"io"
+)
+
+const (
+	ErrorCodeInvalidSnac          uint16 = 0x01
+	ErrorCodeRateToHost           uint16 = 0x02
+	ErrorCodeRateToClient         uint16 = 0x03
+	ErrorCodeNotLoggedOn          uint16 = 0x04
+	ErrorCodeServiceUnavailable   uint16 = 0x05
+	ErrorCodeServiceNotDefined    uint16 = 0x06
+	ErrorCodeObsoleteSnac         uint16 = 0x07
+	ErrorCodeNotSupportedByHost   uint16 = 0x08
+	ErrorCodeNotSupportedByClient uint16 = 0x09
+	ErrorCodeRefusedByClient      uint16 = 0x0A
+	ErrorCodeReplyTooBig          uint16 = 0x0B
+	ErrorCodeResponsesLost        uint16 = 0x0C
+	ErrorCodeRequestDenied        uint16 = 0x0D
+	ErrorCodeBustedSnacPayload    uint16 = 0x0E
+	ErrorCodeInsufficientRights   uint16 = 0x0F
+	ErrorCodeInLocalPermitDeny    uint16 = 0x10
+	ErrorCodeTooEvilSender        uint16 = 0x11
+	ErrorCodeTooEvilReceiver      uint16 = 0x12
+	ErrorCodeUserTempUnavail      uint16 = 0x13
+	ErrorCodeNoMatch              uint16 = 0x14
+	ErrorCodeListOverflow         uint16 = 0x15
+	ErrorCodeRequestAmbigous      uint16 = 0x16
+	ErrorCodeQueueFull            uint16 = 0x17
+	ErrorCodeNotWhileOnAol        uint16 = 0x18
+	ErrorCodeQueryFail            uint16 = 0x19
+	ErrorCodeTimeout              uint16 = 0x1A
+	ErrorCodeErrorText            uint16 = 0x1B
+	ErrorCodeGeneralFailure       uint16 = 0x1C
+	ErrorCodeProgress             uint16 = 0x1D
+	ErrorCodeInFreeArea           uint16 = 0x1E
+	ErrorCodeRestrictedByPc       uint16 = 0x1F
+	ErrorCodeRemoteRestrictedByPc uint16 = 0x20
+)
+
+const (
+	ErrorTagsFailUrl        = 0x04
+	ErrorTagsErrorSubcode   = 0x08
+	ErrorTagsErrorText      = 0x1B
+	ErrorTagsErrorInfoClsid = 0x29
+	ErrorTagsErrorInfoData  = 0x2A
+)
+
+var (
+	CapChat, _ = uuid.MustParse("748F2420-6287-11D1-8222-444553540000").MarshalBinary()
+)
+
+type Config struct {
+	OSCARHost string `envconfig:"OSCAR_HOST" required:"true"`
+	OSCARPort int    `envconfig:"OSCAR_PORT" default:"5190"`
+	BOSPort   int    `envconfig:"BOS_PORT" default:"5191"`
+	ChatPort  int    `envconfig:"CHAT_PORT" default:"5192"`
+	DBPath    string `envconfig:"DB_PATH" required:"true"`
+}
+
+func Address(host string, port int) string {
+	return fmt.Sprintf("%s:%d", host, port)
+}
+
+func SendAndReceiveSignonFrame(rw io.ReadWriter, sequence *uint32) (*oscar.FlapSignonFrame, error) {
+	flapOut := oscar.FlapSignonFrame{
+		FlapFrame: oscar.FlapFrame{
+			StartMarker:   42,
+			FrameType:     1,
+			Sequence:      uint16(*sequence),
+			PayloadLength: 4, // size of flapVersion
+		},
+		FlapVersion: 1,
+	}
+
+	if err := flapOut.Write(rw); err != nil {
+		return nil, err
+	}
+
+	fmt.Printf("SendAndReceiveSignonFrame read FLAP: %+v\n", flapOut)
+
+	// receive
+	flapIn := oscar.FlapSignonFrame{}
+	if err := flapIn.Read(rw); err != nil {
+		return nil, err
+	}
+
+	fmt.Printf("SendAndReceiveSignonFrame write FLAP: %+v\n", flapIn)
+
+	*sequence++
+
+	return &flapIn, nil
+}
+
+func VerifyLogin(sm *SessionManager, rw io.ReadWriter) (*Session, uint32, error) {
+	seq := uint32(100)
+	fmt.Println("VerifyLogin...")
+
+	flap, err := SendAndReceiveSignonFrame(rw, &seq)
+	if err != nil {
+		return nil, 0, err
+	}
+
+	var ok bool
+	ID, ok := flap.GetSlice(OserviceTlvTagsLoginCookie)
+	if !ok {
+		return nil, 0, errors.New("unable to get session ID from payload")
+	}
+
+	sess, ok := sm.Retrieve(string(ID))
+	if !ok {
+		return nil, 0, fmt.Errorf("unable to find session by ID %s", ID)
+	}
+
+	return sess, seq, nil
+}
+
+func VerifyChatLogin(rw io.ReadWriter) (*ChatCookie, uint32, error) {
+	seq := uint32(100)
+	fmt.Println("VerifyChatLogin...")
+
+	flap, err := SendAndReceiveSignonFrame(rw, &seq)
+	if err != nil {
+		return nil, 0, err
+	}
+
+	var ok bool
+	buf, ok := flap.GetSlice(OserviceTlvTagsLoginCookie)
+	if !ok {
+		return nil, 0, errors.New("unable to get session ID from payload")
+	}
+
+	cookie := ChatCookie{}
+	err = cookie.Read(bytes.NewBuffer(buf))
+
+	return &cookie, seq, err
+}
+
+const (
+	OSERVICE      uint16 = 0x0001
+	LOCATE               = 0x0002
+	BUDDY                = 0x0003
+	ICBM                 = 0x0004
+	ADVERT               = 0x0005
+	INVITE               = 0x0006
+	ADMIN                = 0x0007
+	POPUP                = 0x0008
+	PD                   = 0x0009
+	USER_LOOKUP          = 0x000A
+	STATS                = 0x000B
+	TRANSLATE            = 0x000C
+	CHAT_NAV             = 0x000D
+	CHAT                 = 0x000E
+	ODIR                 = 0x000F
+	BART                 = 0x0010
+	FEEDBAG              = 0x0013
+	ICQ                  = 0x0015
+	BUCP                 = 0x0017
+	ALERT                = 0x0018
+	PLUGIN               = 0x0022
+	UNNAMED_FG_24        = 0x0024
+	MDIR                 = 0x0025
+	ARS                  = 0x044A
+)
+
+type IncomingMessage struct {
+	flap oscar.FlapFrame
+	snac oscar.SnacFrame
+	buf  io.Reader
+}
+
+type XMessage struct {
+	snacFrame oscar.SnacFrame
+	snacOut   any
+}
+
+const (
+	FlapFrameSignon    uint8 = 0x01
+	FlapFrameData            = 0x02
+	FlapFrameError           = 0x03
+	FlapFrameSignoff         = 0x04
+	FlapFrameKeepAlive       = 0x05
+)
+
+func readIncomingRequests(rw io.Reader, msCh chan IncomingMessage, errCh chan error) {
+	defer close(msCh)
+	defer close(errCh)
+
+	for {
+		flap := oscar.FlapFrame{}
+		if err := flap.Read(rw); err != nil {
+			errCh <- err
+			return
+		}
+
+		switch flap.FrameType {
+		case FlapFrameSignon:
+			errCh <- errors.New("shouldn't get FlapFrameSignon")
+			return
+		case FlapFrameData:
+			b := make([]byte, flap.PayloadLength)
+			if _, err := rw.Read(b); err != nil {
+				errCh <- err
+				return
+			}
+
+			buf := bytes.NewBuffer(b)
+
+			snac := oscar.SnacFrame{}
+			if err := snac.Read(buf); err != nil {
+				errCh <- err
+				return
+			}
+
+			msCh <- IncomingMessage{
+				flap: flap,
+				snac: snac,
+				buf:  buf,
+			}
+		case FlapFrameError:
+			errCh <- fmt.Errorf("got FlapFrameError: %v", flap)
+			return
+		case FlapFrameSignoff:
+			errCh <- fmt.Errorf("got signoff: %v", flap)
+			return
+		case FlapFrameKeepAlive:
+			fmt.Println("keepalive heartbeat")
+		default:
+			errCh <- fmt.Errorf("unknown frame type: %v", flap)
+			return
+		}
+	}
+}
+
+func Signout(sess *Session, sm *SessionManager, fm *FeedbagStore) {
+	if err := NotifyDeparture(sess, sm, fm); err != nil {
+		fmt.Printf("error notifying departure: %s", err.Error())
+	}
+	sm.Remove(sess)
+}
+
+func ReadBos(cfg Config, ready OnReadyCB, sess *Session, seq uint32, sm *SessionManager, fm *FeedbagStore, cr *ChatRegistry, rwc io.ReadWriter, foodGroups []uint16) error {
+	if err := WriteOServiceHostOnline(foodGroups, rwc, &seq); err != nil {
+		return err
+	}
+
+	// buffered so that the go routine has room to exit
+	msgCh := make(chan IncomingMessage, 1)
+	errCh := make(chan error, 1)
+	go readIncomingRequests(rwc, msgCh, errCh)
+
+	for {
+		select {
+		case m := <-msgCh:
+			if err := routeIncomingRequests(cfg, ready, sm, sess, fm, cr, rwc, &seq, m.snac, m.buf); err != nil {
+				return err
+			}
+		case m := <-sess.RecvMessage():
+			if err := writeOutSNAC(oscar.SnacFrame{}, m.snacFrame, m.snacOut, &seq, rwc); err != nil {
+				return err
+			}
+		case err := <-errCh:
+			return err
+		}
+	}
+}
+
+func routeIncomingRequests(cfg Config, ready OnReadyCB, sm *SessionManager, sess *Session, fm *FeedbagStore, cr *ChatRegistry, rw io.ReadWriter, sequence *uint32, snac oscar.SnacFrame, buf io.Reader) error {
+	switch snac.FoodGroup {
+	case OSERVICE:
+		if err := routeOService(cfg, ready, cr, sm, fm, sess, snac, buf, rw, sequence); err != nil {
+			return err
+		}
+	case LOCATE:
+		if err := routeLocate(sess, sm, fm, snac, buf, rw, sequence); err != nil {
+			return err
+		}
+	case BUDDY:
+		if err := routeBuddy(snac, buf, rw, sequence); err != nil {
+			return err
+		}
+	case ICBM:
+		if err := routeICBM(sm, fm, sess, snac, buf, rw, sequence); err != nil {
+			return err
+		}
+	case PD:
+		if err := routePD(snac, buf, rw, sequence); err != nil {
+			return err
+		}
+	case CHAT_NAV:
+		if err := routeChatNav(sess, cr, snac, buf, rw, sequence); err != nil {
+			return err
+		}
+	case FEEDBAG:
+		if err := routeFeedbag(sm, sess, fm, snac, buf, rw, sequence); err != nil {
+			return err
+		}
+	case BUCP:
+		if err := routeBUCP(snac); err != nil {
+			return err
+		}
+	case CHAT:
+		if err := routeChat(sess, sm, snac, buf, rw, sequence); err != nil {
+			return err
+		}
+	default:
+		panic(fmt.Sprintf("unsupported food group: %d", snac.FoodGroup))
+	}
+
+	return nil
+}
+
+func writeOutSNAC(originsnac oscar.SnacFrame, snacFrame oscar.SnacFrame, snacOut any, sequence *uint32, w io.Writer) error {
+	if originsnac.RequestID != 0 {
+		snacFrame.RequestID = originsnac.RequestID
+	}
+
+	snacBuf := &bytes.Buffer{}
+	if err := snacFrame.Write(snacBuf); err != nil {
+		return err
+	}
+	if err := oscar.Marshal(snacOut, snacBuf); err != nil {
+		return err
+	}
+
+	flap := oscar.FlapFrame{
+		StartMarker:   42,
+		FrameType:     2,
+		Sequence:      uint16(*sequence),
+		PayloadLength: uint16(snacBuf.Len()),
+	}
+
+	fmt.Printf(" write FLAP: %+v\n", flap)
+
+	if err := flap.Write(w); err != nil {
+		return err
+	}
+
+	fmt.Printf(" write SNAC: %+v\n", snacOut)
+
+	expectLen := snacBuf.Len()
+	c, err := w.Write(snacBuf.Bytes())
+	if err != nil {
+		return err
+	}
+	if c != expectLen {
+		panic("did not write the expected # of bytes")
+	}
+
+	*sequence++
+	return nil
+}

+ 38 - 37
oscar/session.go → server/session.go

@@ -1,9 +1,10 @@
-package oscar
+package server
 
 
 import (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"github.com/google/uuid"
 	"github.com/google/uuid"
+	"github.com/mkaminski/goaim/oscar"
 	"sync"
 	"sync"
 	"time"
 	"time"
 )
 )
@@ -89,55 +90,55 @@ func (s *Session) GetAwayMessage() string {
 	return s.AwayMessage
 	return s.AwayMessage
 }
 }
 
 
-func (s *Session) GetUserInfo() []TLV {
+func (s *Session) GetUserInfo() []oscar.TLV {
 	s.Mutex.RLock()
 	s.Mutex.RLock()
 	defer s.Mutex.RUnlock()
 	defer s.Mutex.RUnlock()
 
 
 	// sign-in timestamp
 	// sign-in timestamp
-	tlvs := []TLV{
+	tlvs := []oscar.TLV{
 		{
 		{
-			tType: 0x03,
-			val:   uint32(s.SignonTime.Unix()),
+			TType: 0x03,
+			Val:   uint32(s.SignonTime.Unix()),
 		},
 		},
 	}
 	}
 
 
 	// away message status
 	// away message status
-	userFlags := TLV{
-		tType: 0x01,
-		val:   uint16(0x0010), // AIM client
+	userFlags := oscar.TLV{
+		TType: 0x01,
+		Val:   uint16(0x0010), // AIM client
 	}
 	}
 	if s.AwayMessage != "" {
 	if s.AwayMessage != "" {
-		userFlags.val = userFlags.val.(uint16) | uint16(0x0020)
+		userFlags.Val = userFlags.Val.(uint16) | uint16(0x0020)
 	}
 	}
 	tlvs = append(tlvs, userFlags)
 	tlvs = append(tlvs, userFlags)
 
 
 	// invisibility status
 	// invisibility status
-	status := TLV{
-		tType: 0x06,
-		val:   uint16(0x0000),
+	status := oscar.TLV{
+		TType: 0x06,
+		Val:   uint16(0x0000),
 	}
 	}
 	if s.invisible {
 	if s.invisible {
-		status.val = status.val.(uint16) | uint16(0x0100)
+		status.Val = status.Val.(uint16) | uint16(0x0100)
 	}
 	}
 	tlvs = append(tlvs, status)
 	tlvs = append(tlvs, status)
 
 
 	// idle status
 	// idle status
-	idle := TLV{
-		tType: 0x04,
-		val:   uint16(0),
+	idle := oscar.TLV{
+		TType: 0x04,
+		Val:   uint16(0),
 	}
 	}
 	if s.idle {
 	if s.idle {
-		idle.val = uint16(time.Now().Sub(s.idleTime).Seconds())
+		idle.Val = uint16(time.Now().Sub(s.idleTime).Seconds())
 	}
 	}
 	tlvs = append(tlvs, idle)
 	tlvs = append(tlvs, idle)
 
 
 	// capabilities
 	// capabilities
-	caps := TLV{
-		tType: 0x0D,
-		val:   []byte{},
+	caps := oscar.TLV{
+		TType: 0x0D,
+		Val:   []byte{},
 	}
 	}
 	// chat capability
 	// chat capability
-	caps.val = append(caps.val.([]byte), CapChat...)
+	caps.Val = append(caps.Val.([]byte), CapChat...)
 	tlvs = append(tlvs, caps)
 	tlvs = append(tlvs, caps)
 
 
 	return tlvs
 	return tlvs
@@ -328,35 +329,35 @@ type ChatRoom struct {
 	Name           string
 	Name           string
 }
 }
 
 
-func (c ChatRoom) TLVList() []TLV {
-	return []TLV{
+func (c ChatRoom) TLVList() []oscar.TLV {
+	return []oscar.TLV{
 		{
 		{
-			tType: 0x00c9,
-			val:   uint16(15),
+			TType: 0x00c9,
+			Val:   uint16(15),
 		},
 		},
 		{
 		{
-			tType: 0x00ca,
-			val:   uint32(c.CreateTime.Unix()),
+			TType: 0x00ca,
+			Val:   uint32(c.CreateTime.Unix()),
 		},
 		},
 		{
 		{
-			tType: 0x00d1,
-			val:   uint16(1024),
+			TType: 0x00d1,
+			Val:   uint16(1024),
 		},
 		},
 		{
 		{
-			tType: 0x00d2,
-			val:   uint16(100),
+			TType: 0x00d2,
+			Val:   uint16(100),
 		},
 		},
 		{
 		{
-			tType: 0x00d5,
-			val:   uint8(2),
+			TType: 0x00d5,
+			Val:   uint8(2),
 		},
 		},
 		{
 		{
-			tType: 0x006a,
-			val:   c.Name,
+			TType: 0x006a,
+			Val:   c.Name,
 		},
 		},
 		{
 		{
-			tType: 0x00d3,
-			val:   c.Name,
+			TType: 0x00d3,
+			Val:   c.Name,
 		},
 		},
 	}
 	}
 }
 }

+ 1 - 1
oscar/session_test.go → server/session_test.go

@@ -1,4 +1,4 @@
-package oscar
+package server
 
 
 import (
 import (
 	"testing"
 	"testing"