Browse Source

support user search and icq2000->icq2001 user auth

- Implement SNACs required for search to work in ICQ 20001.
- Authorization from ICQ 2000 to ICQ 2001 works, but not vice-versa.
- Add new methods for retrieving values from TLVs for ICQ. Rename
  existing methods with "-BE" big endian suffix.
Mike 1 year ago
parent
commit
5cc3a60e0a
56 changed files with 2494 additions and 712 deletions
  1. 19 19
      foodgroup/admin.go
  2. 36 35
      foodgroup/admin_test.go
  3. 8 8
      foodgroup/auth.go
  4. 76 76
      foodgroup/auth_test.go
  5. 7 7
      foodgroup/buddy.go
  6. 6 6
      foodgroup/buddy_test.go
  7. 5 5
      foodgroup/chat.go
  8. 16 16
      foodgroup/chat_nav.go
  9. 55 55
      foodgroup/chat_nav_test.go
  10. 12 12
      foodgroup/chat_test.go
  11. 53 12
      foodgroup/feedbag.go
  12. 115 17
      foodgroup/feedbag_test.go
  13. 2 2
      foodgroup/icbm.go
  14. 4 4
      foodgroup/icbm_test.go
  15. 151 8
      foodgroup/icq.go
  16. 670 64
      foodgroup/icq_test.go
  17. 10 10
      foodgroup/locate.go
  18. 14 14
      foodgroup/locate_test.go
  19. 58 0
      foodgroup/mock_icq_user_finder_test.go
  20. 27 27
      foodgroup/oservice.go
  21. 24 24
      foodgroup/oservice_test.go
  22. 3 3
      foodgroup/permit_deny.go
  23. 3 3
      foodgroup/permit_deny_test.go
  24. 12 4
      foodgroup/test_helpers.go
  25. 9 3
      foodgroup/types.go
  26. 1 1
      server/http/mgmt_api.go
  27. 1 1
      server/http/mgmt_api_test.go
  28. 1 1
      server/oscar/admin.go
  29. 1 1
      server/oscar/auth.go
  30. 1 1
      server/oscar/bos.go
  31. 1 1
      server/oscar/bos_test.go
  32. 1 1
      server/oscar/chat.go
  33. 1 1
      server/oscar/chat_test.go
  34. 4 4
      server/oscar/handler/admin_test.go
  35. 2 2
      server/oscar/handler/buddy_test.go
  36. 13 0
      server/oscar/handler/feedbag.go
  37. 25 0
      server/oscar/handler/feedbag_test.go
  38. 28 1
      server/oscar/handler/icq.go
  39. 173 38
      server/oscar/handler/icq_test.go
  40. 1 1
      server/oscar/handler/locate_test.go
  41. 49 0
      server/oscar/handler/mock_feedbag_test.go
  42. 147 0
      server/oscar/handler/mock_icq_service_test.go
  43. 3 3
      server/oscar/handler/oservice_test.go
  44. 1 1
      server/oscar/handler/permit_deny_test.go
  45. 1 0
      server/oscar/handler/routes.go
  46. 8 8
      state/chat.go
  47. 8 8
      state/chat_test.go
  48. 6 6
      state/session.go
  49. 27 27
      state/session_test.go
  50. 73 58
      state/user_store.go
  51. 79 3
      state/user_store_test.go
  52. 6 6
      wire/decode_test.go
  53. 6 6
      wire/encode_test.go
  54. 133 28
      wire/snacs.go
  55. 116 19
      wire/tlv.go
  56. 182 51
      wire/tlv_test.go

+ 19 - 19
foodgroup/admin.go

@@ -95,29 +95,29 @@ func (s AdminService) InfoQuery(_ context.Context, sess *state.Session, frame wi
 
 	tlvList := wire.TLVList{}
 
-	if _, hasRegStatus := body.TLVRestBlock.Slice(wire.AdminTLVRegistrationStatus); hasRegStatus {
+	if _, hasRegStatus := body.TLVRestBlock.Bytes(wire.AdminTLVRegistrationStatus); hasRegStatus {
 		regStatus, err := s.accountManager.RegStatusByName(sess.IdentScreenName())
 		if err != nil {
 			return wire.SNACMessage{}, err
 		}
-		tlvList.Append(wire.NewTLV(wire.AdminTLVRegistrationStatus, regStatus))
+		tlvList.Append(wire.NewTLVBE(wire.AdminTLVRegistrationStatus, regStatus))
 		return getAdminInfoReply(tlvList), nil
 	}
 
-	if _, hasEmail := body.TLVRestBlock.Slice(wire.AdminTLVEmailAddress); hasEmail {
+	if _, hasEmail := body.TLVRestBlock.Bytes(wire.AdminTLVEmailAddress); hasEmail {
 		e, err := s.accountManager.EmailAddressByName(sess.IdentScreenName())
 		if errors.Is(err, state.ErrNoEmailAddress) {
-			tlvList.Append(wire.NewTLV(wire.AdminTLVEmailAddress, ""))
+			tlvList.Append(wire.NewTLVBE(wire.AdminTLVEmailAddress, ""))
 		} else if err != nil {
 			return wire.SNACMessage{}, err
 		} else {
-			tlvList.Append(wire.NewTLV(wire.AdminTLVEmailAddress, e.Address))
+			tlvList.Append(wire.NewTLVBE(wire.AdminTLVEmailAddress, e.Address))
 		}
 		return getAdminInfoReply(tlvList), nil
 	}
 
-	if _, hasNickName := body.TLVRestBlock.Slice(wire.AdminTLVScreenNameFormatted); hasNickName {
-		tlvList.Append(wire.NewTLV(wire.AdminTLVScreenNameFormatted, sess.DisplayScreenName().String()))
+	if _, hasNickName := body.TLVRestBlock.Bytes(wire.AdminTLVScreenNameFormatted); hasNickName {
+		tlvList.Append(wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, sess.DisplayScreenName().String()))
 		return getAdminInfoReply(tlvList), nil
 	}
 
@@ -196,11 +196,11 @@ func (s AdminService) InfoChangeRequest(ctx context.Context, sess *state.Session
 
 	tlvList := wire.TLVList{}
 
-	if sn, hasScreenNameFormatted := body.TLVRestBlock.Slice(wire.AdminTLVScreenNameFormatted); hasScreenNameFormatted {
+	if sn, hasScreenNameFormatted := body.TLVRestBlock.Bytes(wire.AdminTLVScreenNameFormatted); hasScreenNameFormatted {
 		proposedName := state.DisplayScreenName(sn)
 		if ok, errorCode := validateProposedName(proposedName); !ok {
-			tlvList.Append(wire.NewTLV(wire.AdminTLVErrorCode, errorCode))
-			tlvList.Append(wire.NewTLV(wire.AdminTLVUrl, ""))
+			tlvList.Append(wire.NewTLVBE(wire.AdminTLVErrorCode, errorCode))
+			tlvList.Append(wire.NewTLVBE(wire.AdminTLVUrl, ""))
 			return getAdminChangeReply(tlvList), nil
 		}
 		if err := s.accountManager.UpdateDisplayScreenName(proposedName); err != nil {
@@ -219,26 +219,26 @@ func (s AdminService) InfoChangeRequest(ctx context.Context, sess *state.Session
 				TLVUserInfo: sess.TLVUserInfo(),
 			},
 		})
-		tlvList.Append(wire.NewTLV(wire.AdminTLVScreenNameFormatted, proposedName.String()))
+		tlvList.Append(wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, proposedName.String()))
 		return getAdminChangeReply(tlvList), nil
 	}
 
-	if emailAddress, hasEmailAddress := body.TLVRestBlock.Slice(wire.AdminTLVEmailAddress); hasEmailAddress {
+	if emailAddress, hasEmailAddress := body.TLVRestBlock.Bytes(wire.AdminTLVEmailAddress); hasEmailAddress {
 		e, errorCode := validateProposedEmailAddress(emailAddress)
 		if errorCode != 0 {
-			tlvList.Append(wire.NewTLV(wire.AdminTLVErrorCode, errorCode))
-			tlvList.Append(wire.NewTLV(wire.AdminTLVUrl, ""))
+			tlvList.Append(wire.NewTLVBE(wire.AdminTLVErrorCode, errorCode))
+			tlvList.Append(wire.NewTLVBE(wire.AdminTLVUrl, ""))
 			return getAdminChangeReply(tlvList), nil
 
 		}
 		if err := s.accountManager.UpdateEmailAddress(e, sess.IdentScreenName()); err != nil {
 			return wire.SNACMessage{}, err
 		}
-		tlvList.Append(wire.NewTLV(wire.AdminTLVEmailAddress, e.Address))
+		tlvList.Append(wire.NewTLVBE(wire.AdminTLVEmailAddress, e.Address))
 		return getAdminChangeReply(tlvList), nil
 	}
 
-	if regStatus, hasRegStatus := body.TLVRestBlock.Uint16(wire.AdminTLVRegistrationStatus); hasRegStatus {
+	if regStatus, hasRegStatus := body.TLVRestBlock.Uint16BE(wire.AdminTLVRegistrationStatus); hasRegStatus {
 		switch regStatus {
 		case
 			wire.AdminInfoRegStatusFullDisclosure,
@@ -247,11 +247,11 @@ func (s AdminService) InfoChangeRequest(ctx context.Context, sess *state.Session
 			if err := s.accountManager.UpdateRegStatus(regStatus, sess.IdentScreenName()); err != nil {
 				return wire.SNACMessage{}, err
 			}
-			tlvList.Append(wire.NewTLV(wire.AdminTLVRegistrationStatus, regStatus))
+			tlvList.Append(wire.NewTLVBE(wire.AdminTLVRegistrationStatus, regStatus))
 			return getAdminChangeReply(tlvList), nil
 		}
-		tlvList.Append(wire.NewTLV(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidRegistrationPreference))
-		tlvList.Append(wire.NewTLV(wire.AdminTLVUrl, ""))
+		tlvList.Append(wire.NewTLVBE(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidRegistrationPreference))
+		tlvList.Append(wire.NewTLVBE(wire.AdminTLVUrl, ""))
 		return getAdminChangeReply(tlvList), nil
 	}
 

+ 36 - 35
foodgroup/admin_test.go

@@ -4,11 +4,12 @@ import (
 	"net/mail"
 	"testing"
 
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+
 	"github.com/mk6i/retro-aim-server/config"
 	"github.com/mk6i/retro-aim-server/state"
 	"github.com/mk6i/retro-aim-server/wire"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/mock"
 )
 
 func TestAdminService_ConfirmRequest(t *testing.T) {
@@ -236,7 +237,7 @@ func TestAdminService_InfoQuery(t *testing.T) {
 				Body: wire.SNAC_0x07_0x02_AdminInfoQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVRegistrationStatus, uint16(0x00))},
+							wire.NewTLVBE(wire.AdminTLVRegistrationStatus, uint16(0x00))},
 					},
 				},
 			},
@@ -250,7 +251,7 @@ func TestAdminService_InfoQuery(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVRegistrationStatus, wire.AdminInfoRegStatusLimitDisclosure),
+							wire.NewTLVBE(wire.AdminTLVRegistrationStatus, wire.AdminInfoRegStatusLimitDisclosure),
 						},
 					},
 				},
@@ -279,7 +280,7 @@ func TestAdminService_InfoQuery(t *testing.T) {
 				Body: wire.SNAC_0x07_0x02_AdminInfoQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVEmailAddress, uint16(0x00))},
+							wire.NewTLVBE(wire.AdminTLVEmailAddress, uint16(0x00))},
 					},
 				},
 			},
@@ -293,7 +294,7 @@ func TestAdminService_InfoQuery(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVEmailAddress, "chattingchuck@aol.com"),
+							wire.NewTLVBE(wire.AdminTLVEmailAddress, "chattingchuck@aol.com"),
 						},
 					},
 				},
@@ -324,7 +325,7 @@ func TestAdminService_InfoQuery(t *testing.T) {
 				Body: wire.SNAC_0x07_0x02_AdminInfoQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVEmailAddress, uint16(0x00))},
+							wire.NewTLVBE(wire.AdminTLVEmailAddress, uint16(0x00))},
 					},
 				},
 			},
@@ -338,7 +339,7 @@ func TestAdminService_InfoQuery(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVEmailAddress, ""),
+							wire.NewTLVBE(wire.AdminTLVEmailAddress, ""),
 						},
 					},
 				},
@@ -367,7 +368,7 @@ func TestAdminService_InfoQuery(t *testing.T) {
 				Body: wire.SNAC_0x07_0x02_AdminInfoQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVScreenNameFormatted, uint16(0x00))},
+							wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, uint16(0x00))},
 					},
 				},
 			},
@@ -381,7 +382,7 @@ func TestAdminService_InfoQuery(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVScreenNameFormatted, "ChattingChuck"),
+							wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "ChattingChuck"),
 						},
 					},
 				},
@@ -399,7 +400,7 @@ func TestAdminService_InfoQuery(t *testing.T) {
 				Body: wire.SNAC_0x07_0x02_AdminInfoQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(uint16(0x99), uint16(0x00))},
+							wire.NewTLVBE(uint16(0x99), uint16(0x00))},
 					},
 				},
 			},
@@ -511,7 +512,7 @@ func TestAdminService_InfoChangeRequest_ScreenName(t *testing.T) {
 				Body: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVScreenNameFormatted, "Chatting Chuck"),
+							wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "Chatting Chuck"),
 						},
 					},
 				},
@@ -526,7 +527,7 @@ func TestAdminService_InfoChangeRequest_ScreenName(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVScreenNameFormatted, "Chatting Chuck"),
+							wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "Chatting Chuck"),
 						},
 					},
 				},
@@ -544,7 +545,7 @@ func TestAdminService_InfoChangeRequest_ScreenName(t *testing.T) {
 				Body: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVScreenNameFormatted, "c  h  a  t  t  i  n  g  c  h  u  c  k")},
+							wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "c  h  a  t  t  i  n  g  c  h  u  c  k")},
 					},
 				},
 			},
@@ -558,8 +559,8 @@ func TestAdminService_InfoChangeRequest_ScreenName(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidNickNameLength),
-							wire.NewTLV(wire.AdminTLVUrl, ""),
+							wire.NewTLVBE(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidNickNameLength),
+							wire.NewTLVBE(wire.AdminTLVUrl, ""),
 						},
 					},
 				},
@@ -577,7 +578,7 @@ func TestAdminService_InfoChangeRequest_ScreenName(t *testing.T) {
 				Body: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVScreenNameFormatted, "QuietQuinton")},
+							wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "QuietQuinton")},
 					},
 				},
 			},
@@ -591,8 +592,8 @@ func TestAdminService_InfoChangeRequest_ScreenName(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVErrorCode, wire.AdminInfoErrorValidateNickName),
-							wire.NewTLV(wire.AdminTLVUrl, ""),
+							wire.NewTLVBE(wire.AdminTLVErrorCode, wire.AdminInfoErrorValidateNickName),
+							wire.NewTLVBE(wire.AdminTLVUrl, ""),
 						},
 					},
 				},
@@ -610,7 +611,7 @@ func TestAdminService_InfoChangeRequest_ScreenName(t *testing.T) {
 				Body: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVScreenNameFormatted, "ChattingChuck ")},
+							wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "ChattingChuck ")},
 					},
 				},
 			},
@@ -624,8 +625,8 @@ func TestAdminService_InfoChangeRequest_ScreenName(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidNickName),
-							wire.NewTLV(wire.AdminTLVUrl, ""),
+							wire.NewTLVBE(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidNickName),
+							wire.NewTLVBE(wire.AdminTLVUrl, ""),
 						},
 					},
 				},
@@ -725,7 +726,7 @@ func TestAdminService_InfoChangeRequest_EmailAddress(t *testing.T) {
 				Body: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVEmailAddress, "chattingchuck@aol.com"),
+							wire.NewTLVBE(wire.AdminTLVEmailAddress, "chattingchuck@aol.com"),
 						},
 					},
 				},
@@ -740,7 +741,7 @@ func TestAdminService_InfoChangeRequest_EmailAddress(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVEmailAddress, "chattingchuck@aol.com"),
+							wire.NewTLVBE(wire.AdminTLVEmailAddress, "chattingchuck@aol.com"),
 						},
 					},
 				},
@@ -758,7 +759,7 @@ func TestAdminService_InfoChangeRequest_EmailAddress(t *testing.T) {
 				Body: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVEmailAddress, "chattingchuck@@@@@@@aol.com"),
+							wire.NewTLVBE(wire.AdminTLVEmailAddress, "chattingchuck@@@@@@@aol.com"),
 						},
 					},
 				},
@@ -773,8 +774,8 @@ func TestAdminService_InfoChangeRequest_EmailAddress(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidEmail),
-							wire.NewTLV(wire.AdminTLVUrl, ""),
+							wire.NewTLVBE(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidEmail),
+							wire.NewTLVBE(wire.AdminTLVUrl, ""),
 						},
 					},
 				},
@@ -792,7 +793,7 @@ func TestAdminService_InfoChangeRequest_EmailAddress(t *testing.T) {
 				Body: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVEmailAddress, longEmailAddress),
+							wire.NewTLVBE(wire.AdminTLVEmailAddress, longEmailAddress),
 						},
 					},
 				},
@@ -807,8 +808,8 @@ func TestAdminService_InfoChangeRequest_EmailAddress(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidEmailLength),
-							wire.NewTLV(wire.AdminTLVUrl, ""),
+							wire.NewTLVBE(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidEmailLength),
+							wire.NewTLVBE(wire.AdminTLVUrl, ""),
 						},
 					},
 				},
@@ -885,7 +886,7 @@ func TestAdminService_InfoChangeRequest_RegStatus(t *testing.T) {
 				Body: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVRegistrationStatus, wire.AdminInfoRegStatusNoDisclosure),
+							wire.NewTLVBE(wire.AdminTLVRegistrationStatus, wire.AdminInfoRegStatusNoDisclosure),
 						},
 					},
 				},
@@ -900,7 +901,7 @@ func TestAdminService_InfoChangeRequest_RegStatus(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVRegistrationStatus, wire.AdminInfoRegStatusNoDisclosure),
+							wire.NewTLVBE(wire.AdminTLVRegistrationStatus, wire.AdminInfoRegStatusNoDisclosure),
 						},
 					},
 				},
@@ -918,7 +919,7 @@ func TestAdminService_InfoChangeRequest_RegStatus(t *testing.T) {
 				Body: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVRegistrationStatus, uint16(0x1337)),
+							wire.NewTLVBE(wire.AdminTLVRegistrationStatus, uint16(0x1337)),
 						},
 					},
 				},
@@ -933,8 +934,8 @@ func TestAdminService_InfoChangeRequest_RegStatus(t *testing.T) {
 					Permissions: wire.AdminInfoPermissionsReadWrite,
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidRegistrationPreference),
-							wire.NewTLV(wire.AdminTLVUrl, ""),
+							wire.NewTLVBE(wire.AdminTLVErrorCode, wire.AdminInfoErrorInvalidRegistrationPreference),
+							wire.NewTLVBE(wire.AdminTLVUrl, ""),
 						},
 					},
 				},

+ 8 - 8
foodgroup/auth.go

@@ -201,7 +201,7 @@ func (s AuthService) BUCPChallenge(
 			Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: []wire.TLV{
-						wire.NewTLV(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
+						wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
 					},
 				},
 			},
@@ -318,9 +318,9 @@ func (s AuthService) login(
 	// get the password from the appropriate TLV. older clients have a
 	// roasted password, newer clients have a hashed password. ICQ may omit
 	// the password TLV when logging in without saved password.
-	if md5Hash, hasMD5 := TLVList.Slice(wire.LoginTLVTagsPasswordHash); hasMD5 {
+	if md5Hash, hasMD5 := TLVList.Bytes(wire.LoginTLVTagsPasswordHash); hasMD5 {
 		loginOK = user.ValidateHash(md5Hash)
-	} else if roastedPass, hasRoasted := TLVList.Slice(wire.LoginTLVTagsRoastedPassword); hasRoasted {
+	} else if roastedPass, hasRoasted := TLVList.Bytes(wire.LoginTLVTagsRoastedPassword); hasRoasted {
 		loginOK = user.ValidateRoastedPass(roastedPass)
 	}
 	if !loginOK {
@@ -346,9 +346,9 @@ func (s AuthService) loginSuccessResponse(screenName state.DisplayScreenName, er
 
 	return wire.TLVRestBlock{
 		TLVList: []wire.TLV{
-			wire.NewTLV(wire.LoginTLVTagsScreenName, screenName),
-			wire.NewTLV(wire.LoginTLVTagsReconnectHere, net.JoinHostPort(s.config.OSCARHost, s.config.BOSPort)),
-			wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, cookie),
+			wire.NewTLVBE(wire.LoginTLVTagsScreenName, screenName),
+			wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, net.JoinHostPort(s.config.OSCARHost, s.config.BOSPort)),
+			wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, cookie),
 		},
 	}, nil
 }
@@ -356,8 +356,8 @@ func (s AuthService) loginSuccessResponse(screenName state.DisplayScreenName, er
 func loginFailureResponse(screenName state.DisplayScreenName, code uint16) wire.TLVRestBlock {
 	return wire.TLVRestBlock{
 		TLVList: []wire.TLV{
-			wire.NewTLV(wire.LoginTLVTagsScreenName, screenName),
-			wire.NewTLV(wire.LoginTLVTagsErrorSubcode, code),
+			wire.NewTLVBE(wire.LoginTLVTagsScreenName, screenName),
+			wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, code),
 		},
 	}
 }

+ 76 - 76
foodgroup/auth_test.go

@@ -49,8 +49,8 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-						wire.NewTLV(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
 					},
 				},
 			},
@@ -87,9 +87,9 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-							wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
-							wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
+							wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+							wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
+							wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 						},
 					},
 				},
@@ -104,9 +104,9 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-						wire.NewTLV(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
-						wire.NewTLV(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
+						wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
 					},
 				},
 			},
@@ -143,9 +143,9 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-							wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
-							wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
+							wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+							wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
+							wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 						},
 					},
 				},
@@ -160,8 +160,8 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad_password")),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("bad_password")),
 					},
 				},
 			},
@@ -183,8 +183,8 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: []wire.TLV{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-							wire.NewTLV(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
+							wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+							wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
 						},
 					},
 				},
@@ -199,8 +199,8 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("password")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, []byte("non_existent_screen_name")),
+						wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("password")),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("non_existent_screen_name")),
 					},
 				},
 			},
@@ -222,8 +222,8 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: []wire.TLV{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("non_existent_screen_name")),
-							wire.NewTLV(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
+							wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("non_existent_screen_name")),
+							wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
 						},
 					},
 				},
@@ -238,8 +238,8 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("password")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, []byte("100003")),
+						wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("password")),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("100003")),
 					},
 				},
 			},
@@ -261,8 +261,8 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: []wire.TLV{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("100003")),
-							wire.NewTLV(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
+							wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("100003")),
+							wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
 						},
 					},
 				},
@@ -278,8 +278,8 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-						wire.NewTLV(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
 					},
 				},
 			},
@@ -324,9 +324,9 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-							wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
-							wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
+							wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+							wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
+							wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 						},
 					},
 				},
@@ -342,8 +342,8 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-						wire.NewTLV(wire.LoginTLVTagsPasswordHash, []byte("bad-password-hash")),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, []byte("bad-password-hash")),
 					},
 				},
 			},
@@ -383,9 +383,9 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-							wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
-							wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
+							wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+							wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
+							wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 						},
 					},
 				},
@@ -396,8 +396,8 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x02_BUCPLoginRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsPasswordHash, user.StrongMD5Pass),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
 					},
 				},
 			},
@@ -486,8 +486,8 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			inputSNAC: wire.FLAPSignonFrame{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsRoastedPassword, roastedPassword),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, roastedPassword),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
 					},
 				},
 			},
@@ -518,9 +518,9 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-					wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
-					wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
+					wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+					wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
+					wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 				},
 			},
 		},
@@ -533,9 +533,9 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			inputSNAC: wire.FLAPSignonFrame{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
-						wire.NewTLV(wire.LoginTLVTagsRoastedPassword, roastedPassword),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
+						wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, roastedPassword),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
 					},
 				},
 			},
@@ -566,9 +566,9 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-					wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
-					wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
+					wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+					wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
+					wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 				},
 			},
 		},
@@ -581,8 +581,8 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			inputSNAC: wire.FLAPSignonFrame{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsRoastedPassword, []byte("bad_roasted_password")),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, []byte("bad_roasted_password")),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
 					},
 				},
 			},
@@ -598,8 +598,8 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: []wire.TLV{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-					wire.NewTLV(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
+					wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+					wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidPassword),
 				},
 			},
 		},
@@ -612,8 +612,8 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			inputSNAC: wire.FLAPSignonFrame{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsRoastedPassword, roastedPassword),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, []byte("non_existent_screen_name")),
+						wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, roastedPassword),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("non_existent_screen_name")),
 					},
 				},
 			},
@@ -629,8 +629,8 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: []wire.TLV{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("non_existent_screen_name")),
-					wire.NewTLV(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
+					wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("non_existent_screen_name")),
+					wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrInvalidUsernameOrPassword),
 				},
 			},
 		},
@@ -643,9 +643,9 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			inputSNAC: wire.FLAPSignonFrame{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
-						wire.NewTLV(wire.LoginTLVTagsRoastedPassword, roastedPassword),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, []byte("100003")),
+						wire.NewTLVBE(wire.LoginTLVTagsClientIdentity, "ICQ 2000b"),
+						wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, roastedPassword),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, []byte("100003")),
 					},
 				},
 			},
@@ -661,8 +661,8 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: []wire.TLV{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("100003")),
-					wire.NewTLV(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
+					wire.NewTLVBE(wire.LoginTLVTagsScreenName, state.NewIdentScreenName("100003")),
+					wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
 				},
 			},
 		},
@@ -676,8 +676,8 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			inputSNAC: wire.FLAPSignonFrame{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsRoastedPassword, roastedPassword),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, roastedPassword),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
 					},
 				},
 			},
@@ -716,9 +716,9 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-					wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
-					wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
+					wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+					wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
+					wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 				},
 			},
 		},
@@ -732,8 +732,8 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			inputSNAC: wire.FLAPSignonFrame{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsRoastedPassword, "bad-roasted-password"),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, "bad-roasted-password"),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
 					},
 				},
 			},
@@ -767,9 +767,9 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			},
 			expectOutput: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
-					wire.NewTLV(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
-					wire.NewTLV(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
+					wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+					wire.NewTLVBE(wire.LoginTLVTagsReconnectHere, "127.0.0.1:1234"),
+					wire.NewTLVBE(wire.LoginTLVTagsAuthorizationCookie, []byte("the-cookie")),
 				},
 			},
 		},
@@ -778,8 +778,8 @@ func TestAuthService_FLAPLoginResponse(t *testing.T) {
 			inputSNAC: wire.FLAPSignonFrame{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsRoastedPassword, roastedPassword),
-						wire.NewTLV(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
+						wire.NewTLVBE(wire.LoginTLVTagsRoastedPassword, roastedPassword),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, user.DisplayScreenName),
 					},
 				},
 			},
@@ -856,7 +856,7 @@ func TestAuthService_BUCPChallengeRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsScreenName, "sn_user_a"),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_a"),
 					},
 				},
 			},
@@ -893,7 +893,7 @@ func TestAuthService_BUCPChallengeRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsScreenName, "sn_user_b"),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_b"),
 					},
 				},
 			},
@@ -926,7 +926,7 @@ func TestAuthService_BUCPChallengeRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsScreenName, "sn_user_b"),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_b"),
 					},
 				},
 			},
@@ -948,7 +948,7 @@ func TestAuthService_BUCPChallengeRequest(t *testing.T) {
 				Body: wire.SNAC_0x17_0x03_BUCPLoginResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LoginTLVTagsErrorSubcode, uint16(0x01)),
+							wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, uint16(0x01)),
 						},
 					},
 				},
@@ -959,7 +959,7 @@ func TestAuthService_BUCPChallengeRequest(t *testing.T) {
 			inputSNAC: wire.SNAC_0x17_0x06_BUCPChallengeRequest{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LoginTLVTagsScreenName, "sn_user_b"),
+						wire.NewTLVBE(wire.LoginTLVTagsScreenName, "sn_user_b"),
 					},
 				},
 			},

+ 7 - 7
foodgroup/buddy.go

@@ -42,10 +42,10 @@ func (s BuddyService) RightsQuery(_ context.Context, frameIn wire.SNACFrame) wir
 		Body: wire.SNAC_0x03_0x03_BuddyRightsReply{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.BuddyTLVTagsParmMaxBuddies, uint16(100)),
-					wire.NewTLV(wire.BuddyTLVTagsParmMaxWatchers, uint16(100)),
-					wire.NewTLV(wire.BuddyTLVTagsParmMaxIcqBroad, uint16(100)),
-					wire.NewTLV(wire.BuddyTLVTagsParmMaxTempBuddies, uint16(100)),
+					wire.NewTLVBE(wire.BuddyTLVTagsParmMaxBuddies, uint16(100)),
+					wire.NewTLVBE(wire.BuddyTLVTagsParmMaxWatchers, uint16(100)),
+					wire.NewTLVBE(wire.BuddyTLVTagsParmMaxIcqBroad, uint16(100)),
+					wire.NewTLVBE(wire.BuddyTLVTagsParmMaxTempBuddies, uint16(100)),
 				},
 			},
 		},
@@ -89,7 +89,7 @@ func (s BuddyService) UnicastBuddyArrived(ctx context.Context, from *state.Sessi
 	case err != nil:
 		return err
 	case icon != nil:
-		userInfo.Append(wire.NewTLV(wire.OServiceUserInfoBARTInfo, *icon))
+		userInfo.Append(wire.NewTLVBE(wire.OServiceUserInfoBARTInfo, *icon))
 	}
 	s.messageRelayer.RelayToScreenName(ctx, to.IdentScreenName(), wire.SNACMessage{
 		Frame: wire.SNACFrame{
@@ -124,7 +124,7 @@ func (s BuddyService) BroadcastBuddyArrived(ctx context.Context, sess *state.Ses
 	case err != nil:
 		return err
 	case icon != nil:
-		userInfo.Append(wire.NewTLV(wire.OServiceUserInfoBARTInfo, *icon))
+		userInfo.Append(wire.NewTLVBE(wire.OServiceUserInfoBARTInfo, *icon))
 	}
 
 	s.messageRelayer.RelayToScreenNames(ctx, recipients, wire.SNACMessage{
@@ -164,7 +164,7 @@ func (s BuddyService) BroadcastBuddyDeparted(ctx context.Context, sess *state.Se
 					TLVList: wire.TLVList{
 						// this TLV needs to be set in order for departure
 						// events to work in ICQ
-						wire.NewTLV(wire.OServiceUserInfoUserFlags, uint16(0)),
+						wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0)),
 					},
 				},
 			},

+ 6 - 6
foodgroup/buddy_test.go

@@ -24,10 +24,10 @@ func TestBuddyService_RightsQuery(t *testing.T) {
 		Body: wire.SNAC_0x03_0x03_BuddyRightsReply{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.BuddyTLVTagsParmMaxBuddies, uint16(100)),
-					wire.NewTLV(wire.BuddyTLVTagsParmMaxWatchers, uint16(100)),
-					wire.NewTLV(wire.BuddyTLVTagsParmMaxIcqBroad, uint16(100)),
-					wire.NewTLV(wire.BuddyTLVTagsParmMaxTempBuddies, uint16(100)),
+					wire.NewTLVBE(wire.BuddyTLVTagsParmMaxBuddies, uint16(100)),
+					wire.NewTLVBE(wire.BuddyTLVTagsParmMaxWatchers, uint16(100)),
+					wire.NewTLVBE(wire.BuddyTLVTagsParmMaxIcqBroad, uint16(100)),
+					wire.NewTLVBE(wire.BuddyTLVTagsParmMaxTempBuddies, uint16(100)),
 				},
 			},
 		},
@@ -442,7 +442,7 @@ func TestBuddyService_BroadcastDeparture(t *testing.T) {
 										WarningLevel: 0,
 										TLVBlock: wire.TLVBlock{
 											TLVList: wire.TLVList{
-												wire.NewTLV(wire.OServiceUserInfoUserFlags, uint16(0)),
+												wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0)),
 											},
 										},
 									},
@@ -612,7 +612,7 @@ func TestBuddyService_UnicastBuddyArrived(t *testing.T) {
 									Name:    strconv.Itoa(int(wire.BARTTypesBuddyIcon)),
 									TLVLBlock: wire.TLVLBlock{
 										TLVList: wire.TLVList{
-											wire.NewTLV(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
+											wire.NewTLVBE(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
 												Hash: []byte{'t', 'h', 'e', 'h', 'a', 's', 'h'},
 											}),
 										},

+ 5 - 5
foodgroup/chat.go

@@ -32,7 +32,7 @@ func (s ChatService) ChannelMsgToHost(ctx context.Context, sess *state.Session,
 		SubGroup:  wire.ChatChannelMsgToClient,
 	}
 
-	msg, hasMessage := inBody.Slice(wire.ChatTLVMessageInformation)
+	msg, hasMessage := inBody.Bytes(wire.ChatTLVMessageInformation)
 	if !hasMessage {
 		return nil, errors.New("SNAC(0x0E,0x05) does not contain a message TLV")
 	}
@@ -44,9 +44,9 @@ func (s ChatService) ChannelMsgToHost(ctx context.Context, sess *state.Session,
 			TLVList: wire.TLVList{
 				// The order of these TLVs matters for AIM 2.x. if out of
 				// order, screen names do not appear with each chat message.
-				wire.NewTLV(wire.ChatTLVSenderInformation, sess.TLVUserInfo()),
-				wire.NewTLV(wire.ChatTLVPublicWhisperFlag, []byte{}),
-				wire.NewTLV(wire.ChatTLVMessageInformation, msg),
+				wire.NewTLVBE(wire.ChatTLVSenderInformation, sess.TLVUserInfo()),
+				wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
+				wire.NewTLVBE(wire.ChatTLVMessageInformation, msg),
 			},
 		},
 	}
@@ -63,7 +63,7 @@ func (s ChatService) ChannelMsgToHost(ctx context.Context, sess *state.Session,
 	})
 
 	var ret *wire.SNACMessage
-	if _, ackMsg := inBody.Slice(wire.ChatTLVEnableReflectionFlag); ackMsg {
+	if _, ackMsg := inBody.Bytes(wire.ChatTLVEnableReflectionFlag); ackMsg {
 		// reflect the message back to the sender
 		ret = &wire.SNACMessage{
 			Frame: frameOut,

+ 16 - 16
foodgroup/chat_nav.go

@@ -12,15 +12,15 @@ import (
 
 var defaultExchangeCfg = wire.TLVBlock{
 	TLVList: wire.TLVList{
-		wire.NewTLV(wire.ChatRoomTLVMaxConcurrentRooms, uint8(10)),
-		wire.NewTLV(wire.ChatRoomTLVClassPerms, uint16(0x0010)),
-		wire.NewTLV(wire.ChatRoomTLVMaxNameLen, uint16(100)),
-		wire.NewTLV(wire.ChatRoomTLVFlags, uint16(15)),
-		wire.NewTLV(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
-		wire.NewTLV(wire.ChatRoomTLVCharSet1, "us-ascii"),
-		wire.NewTLV(wire.ChatRoomTLVLang1, "en"),
-		wire.NewTLV(wire.ChatRoomTLVCharSet2, "us-ascii"),
-		wire.NewTLV(wire.ChatRoomTLVLang2, "en"),
+		wire.NewTLVBE(wire.ChatRoomTLVMaxConcurrentRooms, uint8(10)),
+		wire.NewTLVBE(wire.ChatRoomTLVClassPerms, uint16(0x0010)),
+		wire.NewTLVBE(wire.ChatRoomTLVMaxNameLen, uint16(100)),
+		wire.NewTLVBE(wire.ChatRoomTLVFlags, uint16(15)),
+		wire.NewTLVBE(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
+		wire.NewTLVBE(wire.ChatRoomTLVCharSet1, "us-ascii"),
+		wire.NewTLVBE(wire.ChatRoomTLVLang1, "en"),
+		wire.NewTLVBE(wire.ChatRoomTLVCharSet2, "us-ascii"),
+		wire.NewTLVBE(wire.ChatRoomTLVLang2, "en"),
 	},
 }
 
@@ -58,12 +58,12 @@ func (s ChatNavService) RequestChatRights(_ context.Context, inFrame wire.SNACFr
 		Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.ChatNavTLVMaxConcurrentRooms, uint8(10)),
-					wire.NewTLV(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
+					wire.NewTLVBE(wire.ChatNavTLVMaxConcurrentRooms, uint8(10)),
+					wire.NewTLVBE(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
 						Identifier: state.PrivateExchange,
 						TLVBlock:   defaultExchangeCfg,
 					}),
-					wire.NewTLV(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
+					wire.NewTLVBE(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
 						Identifier: state.PublicExchange,
 						TLVBlock:   defaultExchangeCfg,
 					}),
@@ -118,7 +118,7 @@ func (s ChatNavService) CreateRoom(_ context.Context, sess *state.Session, inFra
 		Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.ChatNavTLVRoomInfo, wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
+					wire.NewTLVBE(wire.ChatNavTLVRoomInfo, wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 						Cookie:         room.Cookie(),
 						Exchange:       room.Exchange(),
 						DetailLevel:    room.DetailLevel(),
@@ -159,7 +159,7 @@ func (s ChatNavService) RequestRoomInfo(_ context.Context, inFrame wire.SNACFram
 		Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.ChatNavTLVRoomInfo, wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
+					wire.NewTLVBE(wire.ChatNavTLVRoomInfo, wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 						Cookie:         room.Cookie(),
 						Exchange:       room.Exchange(),
 						DetailLevel:    room.DetailLevel(),
@@ -188,8 +188,8 @@ func (s ChatNavService) ExchangeInfo(_ context.Context, inFrame wire.SNACFrame,
 		Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.ChatNavTLVMaxConcurrentRooms, uint8(10)),
-					wire.NewTLV(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
+					wire.NewTLVBE(wire.ChatNavTLVMaxConcurrentRooms, uint8(10)),
+					wire.NewTLVBE(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
 						Identifier: inBody.Exchange,
 						TLVBlock:   defaultExchangeCfg,
 					}),

+ 55 - 55
foodgroup/chat_nav_test.go

@@ -41,7 +41,7 @@ func TestChatNavService_CreateRoom(t *testing.T) {
 					DetailLevel:    basicChatRoom.DetailLevel(),
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ChatRoomTLVRoomName, basicChatRoom.Name()),
+							wire.NewTLVBE(wire.ChatRoomTLVRoomName, basicChatRoom.Name()),
 						},
 					},
 				},
@@ -55,7 +55,7 @@ func TestChatNavService_CreateRoom(t *testing.T) {
 				Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(
+							wire.NewTLVBE(
 								wire.ChatNavRequestRoomInfo,
 								wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 									Exchange:       basicChatRoom.Exchange(),
@@ -98,7 +98,7 @@ func TestChatNavService_CreateRoom(t *testing.T) {
 					DetailLevel:    basicChatRoom.DetailLevel(),
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ChatRoomTLVRoomName, basicChatRoom.Name()),
+							wire.NewTLVBE(wire.ChatRoomTLVRoomName, basicChatRoom.Name()),
 						},
 					},
 				},
@@ -112,7 +112,7 @@ func TestChatNavService_CreateRoom(t *testing.T) {
 				Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(
+							wire.NewTLVBE(
 								wire.ChatNavRequestRoomInfo,
 								wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 									Exchange:       basicChatRoom.Exchange(),
@@ -163,7 +163,7 @@ func TestChatNavService_CreateRoom(t *testing.T) {
 					DetailLevel:    publicChatRoom.DetailLevel(),
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ChatRoomTLVRoomName, publicChatRoom.Name()),
+							wire.NewTLVBE(wire.ChatRoomTLVRoomName, publicChatRoom.Name()),
 						},
 					},
 				},
@@ -177,7 +177,7 @@ func TestChatNavService_CreateRoom(t *testing.T) {
 				Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(
+							wire.NewTLVBE(
 								wire.ChatNavRequestRoomInfo,
 								wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 									Exchange:       publicChatRoom.Exchange(),
@@ -220,7 +220,7 @@ func TestChatNavService_CreateRoom(t *testing.T) {
 					DetailLevel:    publicChatRoom.DetailLevel(),
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ChatRoomTLVRoomName, publicChatRoom.Name()),
+							wire.NewTLVBE(wire.ChatRoomTLVRoomName, publicChatRoom.Name()),
 						},
 					},
 				},
@@ -265,7 +265,7 @@ func TestChatNavService_CreateRoom(t *testing.T) {
 					DetailLevel:    basicChatRoom.DetailLevel(),
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ChatRoomTLVRoomName, basicChatRoom.Name()),
+							wire.NewTLVBE(wire.ChatRoomTLVRoomName, basicChatRoom.Name()),
 						},
 					},
 				},
@@ -317,7 +317,7 @@ func TestChatNavService_CreateRoom(t *testing.T) {
 					DetailLevel:    basicChatRoom.DetailLevel(),
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ChatRoomTLVRoomName, basicChatRoom.Name()),
+							wire.NewTLVBE(wire.ChatRoomTLVRoomName, basicChatRoom.Name()),
 						},
 					},
 				},
@@ -360,7 +360,7 @@ func TestChatNavService_CreateRoom(t *testing.T) {
 					DetailLevel:    basicChatRoom.DetailLevel(),
 					TLVBlock: wire.TLVBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ChatRoomTLVRoomName, basicChatRoom.Name()),
+							wire.NewTLVBE(wire.ChatRoomTLVRoomName, basicChatRoom.Name()),
 						},
 					},
 				},
@@ -437,7 +437,7 @@ func TestChatNavService_RequestRoomInfo(t *testing.T) {
 				Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(0x04, wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
+							wire.NewTLVBE(0x04, wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 								Cookie:         privateChatRoom.Cookie(),
 								DetailLevel:    privateChatRoom.DetailLevel(),
 								Exchange:       privateChatRoom.Exchange(),
@@ -481,7 +481,7 @@ func TestChatNavService_RequestRoomInfo(t *testing.T) {
 				Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(0x04, wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
+							wire.NewTLVBE(0x04, wire.SNAC_0x0E_0x02_ChatRoomInfoUpdate{
 								Cookie:         publicChatRoom.Cookie(),
 								DetailLevel:    publicChatRoom.DetailLevel(),
 								Exchange:       publicChatRoom.Exchange(),
@@ -611,36 +611,36 @@ func TestChatNavService_RequestChatRights(t *testing.T) {
 		Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.ChatNavTLVMaxConcurrentRooms, uint8(10)),
-					wire.NewTLV(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
+					wire.NewTLVBE(wire.ChatNavTLVMaxConcurrentRooms, uint8(10)),
+					wire.NewTLVBE(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
 						Identifier: 4,
 						TLVBlock: wire.TLVBlock{
 							TLVList: wire.TLVList{
-								wire.NewTLV(wire.ChatRoomTLVMaxConcurrentRooms, uint8(10)),
-								wire.NewTLV(wire.ChatRoomTLVClassPerms, uint16(0x0010)),
-								wire.NewTLV(wire.ChatRoomTLVMaxNameLen, uint16(100)),
-								wire.NewTLV(wire.ChatRoomTLVFlags, uint16(15)),
-								wire.NewTLV(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
-								wire.NewTLV(wire.ChatRoomTLVCharSet1, "us-ascii"),
-								wire.NewTLV(wire.ChatRoomTLVLang1, "en"),
-								wire.NewTLV(wire.ChatRoomTLVCharSet2, "us-ascii"),
-								wire.NewTLV(wire.ChatRoomTLVLang2, "en"),
+								wire.NewTLVBE(wire.ChatRoomTLVMaxConcurrentRooms, uint8(10)),
+								wire.NewTLVBE(wire.ChatRoomTLVClassPerms, uint16(0x0010)),
+								wire.NewTLVBE(wire.ChatRoomTLVMaxNameLen, uint16(100)),
+								wire.NewTLVBE(wire.ChatRoomTLVFlags, uint16(15)),
+								wire.NewTLVBE(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
+								wire.NewTLVBE(wire.ChatRoomTLVCharSet1, "us-ascii"),
+								wire.NewTLVBE(wire.ChatRoomTLVLang1, "en"),
+								wire.NewTLVBE(wire.ChatRoomTLVCharSet2, "us-ascii"),
+								wire.NewTLVBE(wire.ChatRoomTLVLang2, "en"),
 							},
 						},
 					}),
-					wire.NewTLV(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
+					wire.NewTLVBE(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
 						Identifier: 5,
 						TLVBlock: wire.TLVBlock{
 							TLVList: wire.TLVList{
-								wire.NewTLV(wire.ChatRoomTLVMaxConcurrentRooms, uint8(10)),
-								wire.NewTLV(wire.ChatRoomTLVClassPerms, uint16(0x0010)),
-								wire.NewTLV(wire.ChatRoomTLVMaxNameLen, uint16(100)),
-								wire.NewTLV(wire.ChatRoomTLVFlags, uint16(15)),
-								wire.NewTLV(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
-								wire.NewTLV(wire.ChatRoomTLVCharSet1, "us-ascii"),
-								wire.NewTLV(wire.ChatRoomTLVLang1, "en"),
-								wire.NewTLV(wire.ChatRoomTLVCharSet2, "us-ascii"),
-								wire.NewTLV(wire.ChatRoomTLVLang2, "en"),
+								wire.NewTLVBE(wire.ChatRoomTLVMaxConcurrentRooms, uint8(10)),
+								wire.NewTLVBE(wire.ChatRoomTLVClassPerms, uint16(0x0010)),
+								wire.NewTLVBE(wire.ChatRoomTLVMaxNameLen, uint16(100)),
+								wire.NewTLVBE(wire.ChatRoomTLVFlags, uint16(15)),
+								wire.NewTLVBE(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
+								wire.NewTLVBE(wire.ChatRoomTLVCharSet1, "us-ascii"),
+								wire.NewTLVBE(wire.ChatRoomTLVLang1, "en"),
+								wire.NewTLVBE(wire.ChatRoomTLVCharSet2, "us-ascii"),
+								wire.NewTLVBE(wire.ChatRoomTLVLang2, "en"),
 							},
 						},
 					}),
@@ -679,20 +679,20 @@ func TestChatNavService_ExchangeInfo(t *testing.T) {
 				Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ChatNavTLVMaxConcurrentRooms, uint8(10)),
-							wire.NewTLV(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
+							wire.NewTLVBE(wire.ChatNavTLVMaxConcurrentRooms, uint8(10)),
+							wire.NewTLVBE(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
 								Identifier: state.PrivateExchange,
 								TLVBlock: wire.TLVBlock{
 									TLVList: wire.TLVList{
-										wire.NewTLV(wire.ChatRoomTLVMaxConcurrentRooms, uint8(10)),
-										wire.NewTLV(wire.ChatRoomTLVClassPerms, uint16(0x0010)),
-										wire.NewTLV(wire.ChatRoomTLVMaxNameLen, uint16(100)),
-										wire.NewTLV(wire.ChatRoomTLVFlags, uint16(15)),
-										wire.NewTLV(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
-										wire.NewTLV(wire.ChatRoomTLVCharSet1, "us-ascii"),
-										wire.NewTLV(wire.ChatRoomTLVLang1, "en"),
-										wire.NewTLV(wire.ChatRoomTLVCharSet2, "us-ascii"),
-										wire.NewTLV(wire.ChatRoomTLVLang2, "en"),
+										wire.NewTLVBE(wire.ChatRoomTLVMaxConcurrentRooms, uint8(10)),
+										wire.NewTLVBE(wire.ChatRoomTLVClassPerms, uint16(0x0010)),
+										wire.NewTLVBE(wire.ChatRoomTLVMaxNameLen, uint16(100)),
+										wire.NewTLVBE(wire.ChatRoomTLVFlags, uint16(15)),
+										wire.NewTLVBE(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
+										wire.NewTLVBE(wire.ChatRoomTLVCharSet1, "us-ascii"),
+										wire.NewTLVBE(wire.ChatRoomTLVLang1, "en"),
+										wire.NewTLVBE(wire.ChatRoomTLVCharSet2, "us-ascii"),
+										wire.NewTLVBE(wire.ChatRoomTLVLang2, "en"),
 									},
 								},
 							}),
@@ -720,20 +720,20 @@ func TestChatNavService_ExchangeInfo(t *testing.T) {
 				Body: wire.SNAC_0x0D_0x09_ChatNavNavInfo{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ChatNavTLVMaxConcurrentRooms, uint8(10)),
-							wire.NewTLV(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
+							wire.NewTLVBE(wire.ChatNavTLVMaxConcurrentRooms, uint8(10)),
+							wire.NewTLVBE(wire.ChatNavTLVExchangeInfo, wire.SNAC_0x0D_0x09_TLVExchangeInfo{
 								Identifier: state.PublicExchange,
 								TLVBlock: wire.TLVBlock{
 									TLVList: wire.TLVList{
-										wire.NewTLV(wire.ChatRoomTLVMaxConcurrentRooms, uint8(10)),
-										wire.NewTLV(wire.ChatRoomTLVClassPerms, uint16(0x0010)),
-										wire.NewTLV(wire.ChatRoomTLVMaxNameLen, uint16(100)),
-										wire.NewTLV(wire.ChatRoomTLVFlags, uint16(15)),
-										wire.NewTLV(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
-										wire.NewTLV(wire.ChatRoomTLVCharSet1, "us-ascii"),
-										wire.NewTLV(wire.ChatRoomTLVLang1, "en"),
-										wire.NewTLV(wire.ChatRoomTLVCharSet2, "us-ascii"),
-										wire.NewTLV(wire.ChatRoomTLVLang2, "en"),
+										wire.NewTLVBE(wire.ChatRoomTLVMaxConcurrentRooms, uint8(10)),
+										wire.NewTLVBE(wire.ChatRoomTLVClassPerms, uint16(0x0010)),
+										wire.NewTLVBE(wire.ChatRoomTLVMaxNameLen, uint16(100)),
+										wire.NewTLVBE(wire.ChatRoomTLVFlags, uint16(15)),
+										wire.NewTLVBE(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
+										wire.NewTLVBE(wire.ChatRoomTLVCharSet1, "us-ascii"),
+										wire.NewTLVBE(wire.ChatRoomTLVLang1, "en"),
+										wire.NewTLVBE(wire.ChatRoomTLVCharSet2, "us-ascii"),
+										wire.NewTLVBE(wire.ChatRoomTLVLang2, "en"),
 									},
 								},
 							}),

+ 12 - 12
foodgroup/chat_test.go

@@ -74,10 +74,10 @@ func TestChatService_ChannelMsgToHost(t *testing.T) {
 									Channel: 14,
 									TLVRestBlock: wire.TLVRestBlock{
 										TLVList: wire.TLVList{
-											wire.NewTLV(wire.ChatTLVSenderInformation,
+											wire.NewTLVBE(wire.ChatTLVSenderInformation,
 												newTestSession("user_sending_chat_msg", sessOptCannedSignonTime).TLVUserInfo()),
-											wire.NewTLV(wire.ChatTLVPublicWhisperFlag, []byte{}),
-											wire.NewTLV(wire.ChatTLVMessageInformation, []byte{}),
+											wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
+											wire.NewTLVBE(wire.ChatTLVMessageInformation, []byte{}),
 										},
 									},
 								},
@@ -97,10 +97,10 @@ func TestChatService_ChannelMsgToHost(t *testing.T) {
 					Channel: 14,
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ChatTLVSenderInformation,
+							wire.NewTLVBE(wire.ChatTLVSenderInformation,
 								newTestSession("user_sending_chat_msg", sessOptCannedSignonTime).TLVUserInfo()),
-							wire.NewTLV(wire.ChatTLVPublicWhisperFlag, []byte{}),
-							wire.NewTLV(wire.ChatTLVMessageInformation, []byte{}),
+							wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
+							wire.NewTLVBE(wire.ChatTLVMessageInformation, []byte{}),
 						},
 					},
 				},
@@ -148,10 +148,10 @@ func TestChatService_ChannelMsgToHost(t *testing.T) {
 									Channel: wire.ICBMChannelMIME,
 									TLVRestBlock: wire.TLVRestBlock{
 										TLVList: wire.TLVList{
-											wire.NewTLV(wire.ChatTLVSenderInformation,
+											wire.NewTLVBE(wire.ChatTLVSenderInformation,
 												newTestSession("user_sending_chat_msg", sessOptCannedSignonTime).TLVUserInfo()),
-											wire.NewTLV(wire.ChatTLVPublicWhisperFlag, []byte{}),
-											wire.NewTLV(wire.ChatTLVMessageInformation, []byte{}),
+											wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
+											wire.NewTLVBE(wire.ChatTLVMessageInformation, []byte{}),
 										},
 									},
 								},
@@ -203,10 +203,10 @@ func TestChatService_ChannelMsgToHost(t *testing.T) {
 									Channel: 14,
 									TLVRestBlock: wire.TLVRestBlock{
 										TLVList: wire.TLVList{
-											wire.NewTLV(wire.ChatTLVSenderInformation,
+											wire.NewTLVBE(wire.ChatTLVSenderInformation,
 												newTestSession("user_sending_chat_msg", sessOptCannedSignonTime).TLVUserInfo()),
-											wire.NewTLV(wire.ChatTLVPublicWhisperFlag, []byte{}),
-											wire.NewTLV(wire.ChatTLVMessageInformation, []byte{}),
+											wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
+											wire.NewTLVBE(wire.ChatTLVMessageInformation, []byte{}),
 										},
 									},
 								},

+ 53 - 12
foodgroup/feedbag.go

@@ -71,17 +71,17 @@ func (s FeedbagService) RightsQuery(_ context.Context, inFrame wire.SNACFrame) w
 		Body: wire.SNAC_0x13_0x03_FeedbagRightsReply{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.FeedbagRightsMaxItemAttrs, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxItemsByClass, maxItemsByClass),
-					wire.NewTLV(wire.FeedbagRightsMaxClientItems, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxItemNameLen, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxRecentBuddies, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsInteractionBuddies, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsInteractionHalfLife, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsInteractionMaxScore, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxBuddiesPerGroup, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxMegaBots, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxSmartGroups, uint16(100)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxItemAttrs, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxItemsByClass, maxItemsByClass),
+					wire.NewTLVBE(wire.FeedbagRightsMaxClientItems, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxItemNameLen, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxRecentBuddies, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsInteractionBuddies, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsInteractionHalfLife, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsInteractionMaxScore, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxBuddiesPerGroup, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxMegaBots, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxSmartGroups, uint16(100)),
 				},
 			},
 		},
@@ -244,7 +244,7 @@ func (s FeedbagService) UpsertItem(ctx context.Context, sess *state.Session, inF
 // If the icon already exists, tell the user's buddies about the icon change.
 func (s FeedbagService) broadcastIconUpdate(ctx context.Context, sess *state.Session, item wire.FeedbagItem) error {
 	btlv := wire.BARTInfo{}
-	if b, hasBuf := item.Slice(wire.FeedbagAttributesBartInfo); hasBuf {
+	if b, hasBuf := item.Bytes(wire.FeedbagAttributesBartInfo); hasBuf {
 		if err := wire.UnmarshalBE(&btlv, bytes.NewBuffer(b)); err != nil {
 			return err
 		}
@@ -364,3 +364,44 @@ func (s FeedbagService) Use(ctx context.Context, sess *state.Session) error {
 	}
 	return nil
 }
+
+// RespondAuthorizeToHost forwards an authorization response from the user
+// whose authorization was requested to the user who made the authorization
+// request.
+// Right now we send an ICBM request so that responses can work for both ICQ
+// 2000b and ICQ 2001a. This function should eventually only send an ICBM
+// message to non-feedbag clients and SNAC(0x0013,0x001B) to feedbag clients.
+func (s FeedbagService) RespondAuthorizeToHost(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost) error {
+	response := wire.ICBMCh4Message{
+		UIN:     sess.UIN(),
+		Message: inBody.Reason,
+	}
+
+	switch inBody.Accepted {
+	case 0:
+		response.MessageType = wire.ICBMMsgTypeAuthDeny
+	case 1:
+		response.MessageType = wire.ICBMMsgTypeAuthOK
+	default:
+		return fmt.Errorf("invalid accepted flag %d", inBody.Accepted)
+	}
+
+	s.messageRelayer.RelayToScreenName(ctx, state.NewIdentScreenName(inBody.ScreenName), wire.SNACMessage{
+		Frame: wire.SNACFrame{
+			FoodGroup: wire.ICBM,
+			SubGroup:  wire.ICBMChannelMsgToClient,
+		},
+		Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
+			ChannelID:   wire.ICBMChannelICQ,
+			TLVUserInfo: sess.TLVUserInfo(),
+			TLVRestBlock: wire.TLVRestBlock{
+				TLVList: wire.TLVList{
+					wire.NewTLVLE(wire.ICBMTLVData, response),
+					wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
+				},
+			},
+		},
+	})
+
+	return nil
+}

+ 115 - 17
foodgroup/feedbag_test.go

@@ -327,8 +327,8 @@ func TestFeedbagService_RightsQuery(t *testing.T) {
 		Body: wire.SNAC_0x13_0x03_FeedbagRightsReply{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.FeedbagRightsMaxItemAttrs, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxItemsByClass, []uint16{
+					wire.NewTLVBE(wire.FeedbagRightsMaxItemAttrs, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxItemsByClass, []uint16{
 						61,
 						61,
 						100,
@@ -351,15 +351,15 @@ func TestFeedbagService_RightsQuery(t *testing.T) {
 						1,
 						200,
 					}),
-					wire.NewTLV(wire.FeedbagRightsMaxClientItems, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxItemNameLen, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxRecentBuddies, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsInteractionBuddies, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsInteractionHalfLife, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsInteractionMaxScore, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxBuddiesPerGroup, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxMegaBots, uint16(200)),
-					wire.NewTLV(wire.FeedbagRightsMaxSmartGroups, uint16(100)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxClientItems, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxItemNameLen, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxRecentBuddies, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsInteractionBuddies, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsInteractionHalfLife, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsInteractionMaxScore, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxBuddiesPerGroup, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxMegaBots, uint16(200)),
+					wire.NewTLVBE(wire.FeedbagRightsMaxSmartGroups, uint16(100)),
 				},
 			},
 		},
@@ -750,7 +750,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 							ClassID: wire.FeedbagClassIdBart,
 							TLVLBlock: wire.TLVLBlock{
 								TLVList: wire.TLVList{
-									wire.NewTLV(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
+									wire.NewTLVBE(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
 										Hash: []byte{'t', 'h', 'e', 'h', 'a', 's', 'h'},
 									}),
 								},
@@ -777,7 +777,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 									ClassID: wire.FeedbagClassIdBart,
 									TLVLBlock: wire.TLVLBlock{
 										TLVList: wire.TLVList{
-											wire.NewTLV(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
+											wire.NewTLVBE(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
 												Hash: []byte{'t', 'h', 'e', 'h', 'a', 's', 'h'},
 											}),
 										},
@@ -834,7 +834,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 							ClassID: wire.FeedbagClassIdBart,
 							TLVLBlock: wire.TLVLBlock{
 								TLVList: wire.TLVList{
-									wire.NewTLV(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
+									wire.NewTLVBE(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
 										Hash: []byte{'t', 'h', 'e', 'h', 'a', 's', 'h'},
 									}),
 								},
@@ -861,7 +861,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 									ClassID: wire.FeedbagClassIdBart,
 									TLVLBlock: wire.TLVLBlock{
 										TLVList: wire.TLVList{
-											wire.NewTLV(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
+											wire.NewTLVBE(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
 												Hash: []byte{'t', 'h', 'e', 'h', 'a', 's', 'h'},
 											}),
 										},
@@ -927,7 +927,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 							ClassID: wire.FeedbagClassIdBart,
 							TLVLBlock: wire.TLVLBlock{
 								TLVList: wire.TLVList{
-									wire.NewTLV(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
+									wire.NewTLVBE(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
 										Hash: wire.GetClearIconHash(),
 									}),
 								},
@@ -946,7 +946,7 @@ func TestFeedbagService_UpsertItem(t *testing.T) {
 									ClassID: wire.FeedbagClassIdBart,
 									TLVLBlock: wire.TLVLBlock{
 										TLVList: wire.TLVList{
-											wire.NewTLV(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
+											wire.NewTLVBE(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
 												Hash: wire.GetClearIconHash(),
 											}),
 										},
@@ -1441,3 +1441,101 @@ func TestFeedbagService_Use(t *testing.T) {
 		})
 	}
 }
+
+func TestFeedbagService_RespondAuthorizeToHost(t *testing.T) {
+	tests := []struct {
+		name       string
+		sess       *state.Session
+		bodyIn     wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost
+		mockParams mockParams
+		wantErr    error
+	}{
+		{
+			name: "authorization accepted",
+			sess: newTestSession("100001", sessOptUIN(100001)),
+			bodyIn: wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost{
+				ScreenName: "100003",
+				Accepted:   1,
+			},
+			mockParams: mockParams{
+				messageRelayerParams: messageRelayerParams{
+					relayToScreenNameParams: relayToScreenNameParams{
+						{
+							screenName: state.NewIdentScreenName("100003"),
+							message: wire.SNACMessage{
+								Frame: wire.SNACFrame{
+									FoodGroup: wire.ICBM,
+									SubGroup:  wire.ICBMChannelMsgToClient,
+								},
+								Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
+									ChannelID:   wire.ICBMChannelICQ,
+									TLVUserInfo: newTestSession("100001").TLVUserInfo(),
+									TLVRestBlock: wire.TLVRestBlock{
+										TLVList: wire.TLVList{
+											wire.NewTLVLE(wire.ICBMTLVData, wire.ICBMCh4Message{
+												UIN:         100001,
+												MessageType: wire.ICBMMsgTypeAuthOK,
+											}),
+											wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			name: "authorization denied (with reason)",
+			sess: newTestSession("100001", sessOptUIN(100001)),
+			bodyIn: wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost{
+				ScreenName: "100003",
+				Accepted:   0,
+				Reason:     "I don't know you!",
+			},
+			mockParams: mockParams{
+				messageRelayerParams: messageRelayerParams{
+					relayToScreenNameParams: relayToScreenNameParams{
+						{
+							screenName: state.NewIdentScreenName("100003"),
+							message: wire.SNACMessage{
+								Frame: wire.SNACFrame{
+									FoodGroup: wire.ICBM,
+									SubGroup:  wire.ICBMChannelMsgToClient,
+								},
+								Body: wire.SNAC_0x04_0x07_ICBMChannelMsgToClient{
+									ChannelID:   wire.ICBMChannelICQ,
+									TLVUserInfo: newTestSession("100001").TLVUserInfo(),
+									TLVRestBlock: wire.TLVRestBlock{
+										TLVList: wire.TLVList{
+											wire.NewTLVLE(wire.ICBMTLVData, wire.ICBMCh4Message{
+												UIN:         100001,
+												MessageType: wire.ICBMMsgTypeAuthDeny,
+												Message:     "I don't know you!",
+											}),
+											wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			messageRelayer := newMockMessageRelayer(t)
+			for _, params := range tt.mockParams.relayToScreenNameParams {
+				messageRelayer.EXPECT().
+					RelayToScreenName(nil, params.screenName, params.message)
+			}
+
+			svc := NewFeedbagService(slog.Default(), messageRelayer, nil, nil, nil)
+			haveErr := svc.RespondAuthorizeToHost(nil, tt.sess, wire.SNACFrame{}, tt.bodyIn)
+			assert.ErrorIs(t, tt.wantErr, haveErr)
+		})
+	}
+}

+ 2 - 2
foodgroup/icbm.go

@@ -91,7 +91,7 @@ func (s ICBMService) ChannelMsgToHost(ctx context.Context, sess *state.Session,
 	recipSess := s.messageRelayer.RetrieveByScreenName(recip)
 	if recipSess == nil {
 		// todo: verify user exists, otherwise this could save a bunch of garbage records
-		if _, saveOffline := inBody.Slice(wire.ICBMTLVStore); saveOffline {
+		if _, saveOffline := inBody.Bytes(wire.ICBMTLVStore); saveOffline {
 			offlineMsg := state.OfflineMessage{
 				Message:   inBody,
 				Recipient: recip,
@@ -146,7 +146,7 @@ func (s ICBMService) ChannelMsgToHost(ctx context.Context, sess *state.Session,
 		Body: clientIM,
 	})
 
-	if _, requestedConfirmation := inBody.TLVRestBlock.Slice(wire.ICBMTLVRequestHostAck); !requestedConfirmation {
+	if _, requestedConfirmation := inBody.TLVRestBlock.Bytes(wire.ICBMTLVRequestHostAck); !requestedConfirmation {
 		// don't ack message
 		return nil, nil
 	}

+ 4 - 4
foodgroup/icbm_test.go

@@ -243,8 +243,8 @@ func TestICBMService_ChannelMsgToHost(t *testing.T) {
 					ScreenName: "22222222",
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICBMTLVRequestHostAck, []byte{}),
-							wire.NewTLV(wire.ICBMTLVStore, []byte{}),
+							wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
+							wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
 						},
 					},
 				},
@@ -271,8 +271,8 @@ func TestICBMService_ChannelMsgToHost(t *testing.T) {
 									ScreenName: "22222222",
 									TLVRestBlock: wire.TLVRestBlock{
 										TLVList: wire.TLVList{
-											wire.NewTLV(wire.ICBMTLVRequestHostAck, []byte{}),
-											wire.NewTLV(wire.ICBMTLVStore, []byte{}),
+											wire.NewTLVBE(wire.ICBMTLVRequestHostAck, []byte{}),
+											wire.NewTLVBE(wire.ICBMTLVStore, []byte{}),
 										},
 									},
 								},

+ 151 - 8
foodgroup/icq.go

@@ -128,6 +128,46 @@ func (s ICQService) FindByEmail(ctx context.Context, sess *state.Session, req wi
 	})
 }
 
+func (s ICQService) FindByEmail3(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3, seq uint16) error {
+	b, hasEmail := req.Bytes(wire.ICQTLVTagsEmail)
+	if !hasEmail {
+		return errors.New("unable to get email from request")
+	}
+
+	email := wire.ICQEmail{}
+	if err := wire.UnmarshalLE(&email, bytes.NewReader(b)); err != nil {
+		return fmt.Errorf("unmarshal email: %w", err)
+	}
+
+	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
+		ICQMetadata: wire.ICQMetadata{
+			UIN:     sess.UIN(),
+			ReqType: wire.ICQDBQueryMetaReply,
+			Seq:     seq,
+		},
+		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
+		Success:    wire.ICQStatusCodeOK,
+	}
+	resp.LastResult()
+
+	res, err := s.userFinder.FindByEmail(email.Email)
+
+	switch {
+	case errors.Is(err, state.ErrNoUser):
+		resp.Success = wire.ICQStatusCodeFail
+	case err != nil:
+		s.logger.Error("FindByEmail failed", "err", err.Error())
+		resp.Success = wire.ICQStatusCodeErr
+	default:
+		resp.Success = wire.ICQStatusCodeOK
+		resp.Details = s.createResult(res)
+	}
+
+	return s.reply(ctx, sess, wire.ICQMessageReplyEnvelope{
+		Message: resp,
+	})
+}
+
 func (s ICQService) FindByInterests(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages, seq uint16) error {
 	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
 		ICQMetadata: wire.ICQMetadata{
@@ -173,6 +213,74 @@ func (s ICQService) FindByInterests(ctx context.Context, sess *state.Session, re
 	return nil
 }
 
+func (s ICQService) FindByWhitePages2(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2, seq uint16) error {
+
+	users, err := func() ([]state.User, error) {
+		if keyword, hasKeyword := req.ICQString(wire.ICQTLVTagsWhitepagesSearchKeywords); hasKeyword {
+			res, err := s.userFinder.FindByKeyword(keyword)
+			if err != nil {
+				return nil, fmt.Errorf("FindByKeyword failed: %w", err)
+			}
+			return res, nil
+		}
+
+		bNick, hasNick := req.ICQString(wire.ICQTLVTagsNickname)
+		bFirst, hasFirst := req.ICQString(wire.ICQTLVTagsFirstName)
+		bLast, hastLast := req.ICQString(wire.ICQTLVTagsLastName)
+
+		if hasNick || hasFirst || hastLast {
+			res, err := s.userFinder.FindByDetails(bFirst, bLast, bNick)
+			if err != nil {
+				return nil, fmt.Errorf("FindByDetails failed: %w", err)
+			}
+			return res, nil
+		}
+
+		return nil, nil
+	}()
+
+	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
+		ICQMetadata: wire.ICQMetadata{
+			UIN:     sess.UIN(),
+			ReqType: wire.ICQDBQueryMetaReply,
+			Seq:     seq,
+		},
+		Success:    wire.ICQStatusCodeOK,
+		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
+	}
+
+	if err != nil {
+		s.logger.Error("FindByWhitePages2 failed", "err", err.Error())
+		resp.Success = wire.ICQStatusCodeErr
+		return s.reply(ctx, sess, wire.ICQMessageReplyEnvelope{
+			Message: resp,
+		})
+	}
+
+	if len(users) == 0 {
+		resp.Success = wire.ICQStatusCodeFail
+		return s.reply(ctx, sess, wire.ICQMessageReplyEnvelope{
+			Message: resp,
+		})
+	}
+
+	for i := 0; i < len(users); i++ {
+		if i == len(users)-1 {
+			resp.LastResult()
+		} else {
+			resp.ReqSubType = wire.ICQDBQueryMetaReplyUserFound
+		}
+		resp.Details = s.createResult(users[i])
+		if err := s.reply(ctx, sess, wire.ICQMessageReplyEnvelope{
+			Message: resp,
+		}); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 func (s ICQService) FindByUIN(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error {
 	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
 		ICQMetadata: wire.ICQMetadata{
@@ -203,6 +311,41 @@ func (s ICQService) FindByUIN(ctx context.Context, sess *state.Session, req wire
 	})
 }
 
+func (s ICQService) FindByUIN2(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2, seq uint16) error {
+	UIN, hasUIN := req.Uint32LE(wire.ICQTLVTagsUIN)
+	if !hasUIN {
+		return errors.New("unable to get UIN from request")
+	}
+
+	resp := wire.ICQ_0x07DA_0x01AE_DBQueryMetaReplyLastUserFound{
+		ICQMetadata: wire.ICQMetadata{
+			UIN:     sess.UIN(),
+			ReqType: wire.ICQDBQueryMetaReply,
+			Seq:     seq,
+		},
+		ReqSubType: wire.ICQDBQueryMetaReplyLastUserFound,
+		Success:    wire.ICQStatusCodeOK,
+	}
+	resp.LastResult()
+
+	res, err := s.userFinder.FindByUIN(UIN)
+
+	switch {
+	case errors.Is(err, state.ErrNoUser):
+		resp.Success = wire.ICQStatusCodeFail
+	case err != nil:
+		s.logger.Error("FindByUIN failed", "err", err.Error())
+		resp.Success = wire.ICQStatusCodeErr
+	default:
+		resp.Success = wire.ICQStatusCodeOK
+		resp.Details = s.createResult(res)
+	}
+
+	return s.reply(ctx, sess, wire.ICQMessageReplyEnvelope{
+		Message: resp,
+	})
+}
+
 func (s ICQService) FullUserInfo(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error {
 
 	user, err := s.userFinder.FindByUIN(req.UIN)
@@ -267,7 +410,7 @@ func (s ICQService) OfflineMsgReq(ctx context.Context, sess *state.Session, seq
 
 		switch msgIn.Message.ChannelID {
 		case wire.ICBMChannelIM:
-			if payload, hasIM := msgIn.Message.Slice(wire.ICBMTLVAOLIMData); hasIM {
+			if payload, hasIM := msgIn.Message.Bytes(wire.ICBMTLVAOLIMData); hasIM {
 				// send regular IM
 				msgText, err := wire.UnmarshalICBMMessageText(payload)
 				if err != nil {
@@ -277,16 +420,16 @@ func (s ICQService) OfflineMsgReq(ctx context.Context, sess *state.Session, seq
 				reply.Message = msgText
 			}
 		case wire.ICBMChannelICQ:
-			if b, hasAuthReq := msgIn.Message.Slice(wire.ICBMTLVData); hasAuthReq {
+			if b, hasAuthReq := msgIn.Message.Bytes(wire.ICBMTLVData); hasAuthReq {
 				// send authorization request
-				rdv := wire.ICBMCh4Message{}
+				msg := wire.ICBMCh4Message{}
 				buf := bytes.NewBuffer(b)
-				if err := wire.UnmarshalLE(&rdv, buf); err != nil {
+				if err := wire.UnmarshalLE(&msg, buf); err != nil {
 					return err
 				}
-				reply.MsgType = rdv.MessageType
-				reply.Flags = rdv.Flags
-				reply.Message = rdv.Message
+				reply.MsgType = msg.MessageType
+				reply.Flags = msg.Flags
+				reply.Message = msg.Message
 			}
 		}
 
@@ -705,7 +848,7 @@ func (s ICQService) reply(ctx context.Context, sess *state.Session, message wire
 		Body: wire.SNAC_0x15_0x02_DBReply{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.ICQTLVTagsMetadata, message),
+					wire.NewTLVBE(wire.ICQTLVTagsMetadata, message),
 				},
 			},
 		},

File diff suppressed because it is too large
+ 670 - 64
foodgroup/icq_test.go


+ 10 - 10
foodgroup/locate.go

@@ -58,11 +58,11 @@ func (s LocateService) RightsQuery(_ context.Context, inFrame wire.SNACFrame) wi
 				TLVList: wire.TLVList{
 					// these are arbitrary values--AIM clients seem to perform
 					// OK with them
-					wire.NewTLV(wire.LocateTLVTagsRightsMaxSigLen, uint16(1000)),
-					wire.NewTLV(wire.LocateTLVTagsRightsMaxCapabilitiesLen, uint16(1000)),
-					wire.NewTLV(wire.LocateTLVTagsRightsMaxFindByEmailList, uint16(1000)),
-					wire.NewTLV(wire.LocateTLVTagsRightsMaxCertsLen, uint16(1000)),
-					wire.NewTLV(wire.LocateTLVTagsRightsMaxMaxShortCapabilities, uint16(1000)),
+					wire.NewTLVBE(wire.LocateTLVTagsRightsMaxSigLen, uint16(1000)),
+					wire.NewTLVBE(wire.LocateTLVTagsRightsMaxCapabilitiesLen, uint16(1000)),
+					wire.NewTLVBE(wire.LocateTLVTagsRightsMaxFindByEmailList, uint16(1000)),
+					wire.NewTLVBE(wire.LocateTLVTagsRightsMaxCertsLen, uint16(1000)),
+					wire.NewTLVBE(wire.LocateTLVTagsRightsMaxMaxShortCapabilities, uint16(1000)),
 				},
 			},
 		},
@@ -87,7 +87,7 @@ func (s LocateService) SetInfo(ctx context.Context, sess *state.Session, inBody
 	}
 
 	// update client capabilities (buddy icon, chat, etc...)
-	if b, hasCaps := inBody.Slice(wire.LocateTLVTagsInfoCapabilities); hasCaps {
+	if b, hasCaps := inBody.Bytes(wire.LocateTLVTagsInfoCapabilities); hasCaps {
 		if len(b)%16 != 0 {
 			return errors.New("capability list must be array of 16-byte values")
 		}
@@ -152,15 +152,15 @@ func (s LocateService) UserInfoQuery(_ context.Context, sess *state.Session, inF
 			return wire.SNACMessage{}, err
 		}
 		list.AppendList([]wire.TLV{
-			wire.NewTLV(wire.LocateTLVTagsInfoSigMime, `text/aolrtf; charset="us-ascii"`),
-			wire.NewTLV(wire.LocateTLVTagsInfoSigData, profile),
+			wire.NewTLVBE(wire.LocateTLVTagsInfoSigMime, `text/aolrtf; charset="us-ascii"`),
+			wire.NewTLVBE(wire.LocateTLVTagsInfoSigData, profile),
 		})
 	}
 
 	if inBody.RequestAwayMessage() {
 		list.AppendList([]wire.TLV{
-			wire.NewTLV(wire.LocateTLVTagsInfoUnavailableMime, `text/aolrtf; charset="us-ascii"`),
-			wire.NewTLV(wire.LocateTLVTagsInfoUnavailableData, buddySess.AwayMessage()),
+			wire.NewTLVBE(wire.LocateTLVTagsInfoUnavailableMime, `text/aolrtf; charset="us-ascii"`),
+			wire.NewTLVBE(wire.LocateTLVTagsInfoUnavailableData, buddySess.AwayMessage()),
 		})
 	}
 

+ 14 - 14
foodgroup/locate_test.go

@@ -128,8 +128,8 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 						TLVUserInfo(),
 					LocateInfo: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LocateTLVTagsInfoSigMime, `text/aolrtf; charset="us-ascii"`),
-							wire.NewTLV(wire.LocateTLVTagsInfoSigData, "this is my profile!"),
+							wire.NewTLVBE(wire.LocateTLVTagsInfoSigMime, `text/aolrtf; charset="us-ascii"`),
+							wire.NewTLVBE(wire.LocateTLVTagsInfoSigData, "this is my profile!"),
 						},
 					},
 				},
@@ -190,8 +190,8 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 						TLVUserInfo(),
 					LocateInfo: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LocateTLVTagsInfoSigMime, `text/aolrtf; charset="us-ascii"`),
-							wire.NewTLV(wire.LocateTLVTagsInfoSigData, "this is my profile!"),
+							wire.NewTLVBE(wire.LocateTLVTagsInfoSigMime, `text/aolrtf; charset="us-ascii"`),
+							wire.NewTLVBE(wire.LocateTLVTagsInfoSigData, "this is my profile!"),
 						},
 					},
 				},
@@ -244,8 +244,8 @@ func TestLocateService_UserInfoQuery(t *testing.T) {
 						TLVUserInfo(),
 					LocateInfo: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.LocateTLVTagsInfoUnavailableMime, `text/aolrtf; charset="us-ascii"`),
-							wire.NewTLV(wire.LocateTLVTagsInfoUnavailableData, "this is my away message!"),
+							wire.NewTLVBE(wire.LocateTLVTagsInfoUnavailableMime, `text/aolrtf; charset="us-ascii"`),
+							wire.NewTLVBE(wire.LocateTLVTagsInfoUnavailableData, "this is my away message!"),
 						},
 					},
 				},
@@ -417,7 +417,7 @@ func TestLocateService_SetInfo(t *testing.T) {
 			inBody: wire.SNAC_0x02_0x04_LocateSetInfo{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LocateTLVTagsInfoSigData, "profile-result"),
+						wire.NewTLVBE(wire.LocateTLVTagsInfoSigData, "profile-result"),
 					},
 				},
 			},
@@ -438,7 +438,7 @@ func TestLocateService_SetInfo(t *testing.T) {
 			inBody: wire.SNAC_0x02_0x04_LocateSetInfo{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.LocateTLVTagsInfoUnavailableData, "this is my away message!"),
+						wire.NewTLVBE(wire.LocateTLVTagsInfoUnavailableData, "this is my away message!"),
 					},
 				},
 			},
@@ -484,7 +484,7 @@ func TestLocateService_SetInfo_SetCaps(t *testing.T) {
 	inBody := wire.SNAC_0x02_0x04_LocateSetInfo{
 		TLVRestBlock: wire.TLVRestBlock{
 			TLVList: wire.TLVList{
-				wire.NewTLV(wire.LocateTLVTagsInfoCapabilities, []byte{
+				wire.NewTLVBE(wire.LocateTLVTagsInfoCapabilities, []byte{
 					// chat: "748F2420-6287-11D1-8222-444553540000"
 					0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00,
 					// avatar: "09461346-4c7f-11d1-8222-444553540000"
@@ -523,11 +523,11 @@ func TestLocateService_RightsQuery(t *testing.T) {
 		Body: wire.SNAC_0x02_0x03_LocateRightsReply{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.LocateTLVTagsRightsMaxSigLen, uint16(1000)),
-					wire.NewTLV(wire.LocateTLVTagsRightsMaxCapabilitiesLen, uint16(1000)),
-					wire.NewTLV(wire.LocateTLVTagsRightsMaxFindByEmailList, uint16(1000)),
-					wire.NewTLV(wire.LocateTLVTagsRightsMaxCertsLen, uint16(1000)),
-					wire.NewTLV(wire.LocateTLVTagsRightsMaxMaxShortCapabilities, uint16(1000)),
+					wire.NewTLVBE(wire.LocateTLVTagsRightsMaxSigLen, uint16(1000)),
+					wire.NewTLVBE(wire.LocateTLVTagsRightsMaxCapabilitiesLen, uint16(1000)),
+					wire.NewTLVBE(wire.LocateTLVTagsRightsMaxFindByEmailList, uint16(1000)),
+					wire.NewTLVBE(wire.LocateTLVTagsRightsMaxCertsLen, uint16(1000)),
+					wire.NewTLVBE(wire.LocateTLVTagsRightsMaxMaxShortCapabilities, uint16(1000)),
 				},
 			},
 		},

+ 58 - 0
foodgroup/mock_icq_user_finder_test.go

@@ -195,6 +195,64 @@ func (_c *mockICQUserFinder_FindByInterests_Call) RunAndReturn(run func(uint16,
 	return _c
 }
 
+// FindByKeyword provides a mock function with given fields: keyword
+func (_m *mockICQUserFinder) FindByKeyword(keyword string) ([]state.User, error) {
+	ret := _m.Called(keyword)
+
+	if len(ret) == 0 {
+		panic("no return value specified for FindByKeyword")
+	}
+
+	var r0 []state.User
+	var r1 error
+	if rf, ok := ret.Get(0).(func(string) ([]state.User, error)); ok {
+		return rf(keyword)
+	}
+	if rf, ok := ret.Get(0).(func(string) []state.User); ok {
+		r0 = rf(keyword)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]state.User)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(string) error); ok {
+		r1 = rf(keyword)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mockICQUserFinder_FindByKeyword_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindByKeyword'
+type mockICQUserFinder_FindByKeyword_Call struct {
+	*mock.Call
+}
+
+// FindByKeyword is a helper method to define mock.On call
+//   - keyword string
+func (_e *mockICQUserFinder_Expecter) FindByKeyword(keyword interface{}) *mockICQUserFinder_FindByKeyword_Call {
+	return &mockICQUserFinder_FindByKeyword_Call{Call: _e.mock.On("FindByKeyword", keyword)}
+}
+
+func (_c *mockICQUserFinder_FindByKeyword_Call) Run(run func(keyword string)) *mockICQUserFinder_FindByKeyword_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(string))
+	})
+	return _c
+}
+
+func (_c *mockICQUserFinder_FindByKeyword_Call) Return(_a0 []state.User, _a1 error) *mockICQUserFinder_FindByKeyword_Call {
+	_c.Call.Return(_a0, _a1)
+	return _c
+}
+
+func (_c *mockICQUserFinder_FindByKeyword_Call) RunAndReturn(run func(string) ([]state.User, error)) *mockICQUserFinder_FindByKeyword_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // FindByUIN provides a mock function with given fields: UIN
 func (_m *mockICQUserFinder) FindByUIN(UIN uint32) (state.User, error) {
 	ret := _m.Called(UIN)

+ 27 - 27
foodgroup/oservice.go

@@ -424,7 +424,7 @@ func (s OServiceService) UserInfoQuery(_ context.Context, sess *state.Session, i
 // to 0x0100, set invisible. Else, return an error for any other value.
 // It returns SNAC wire.OServiceUserInfoUpdate containing the user's info.
 func (s OServiceService) SetUserInfoFields(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x01_0x1E_OServiceSetUserInfoFields) (wire.SNACMessage, error) {
-	if status, hasStatus := inBody.Uint32(wire.OServiceUserInfoStatus); hasStatus {
+	if status, hasStatus := inBody.Uint32BE(wire.OServiceUserInfoStatus); hasStatus {
 		sess.SetUserStatusBitmask(status)
 		if sess.Invisible() {
 			if err := s.buddyUpdateBroadcaster.BroadcastBuddyDeparted(ctx, sess); err != nil {
@@ -598,11 +598,11 @@ func (s OServiceServiceForBOS) ServiceRequest(ctx context.Context, sess *state.S
 			Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceTLVTagsReconnectHere, net.JoinHostPort(s.cfg.OSCARHost, s.cfg.AdminPort)),
-						wire.NewTLV(wire.OServiceTLVTagsLoginCookie, cookie),
-						wire.NewTLV(wire.OServiceTLVTagsGroupID, wire.Admin),
-						wire.NewTLV(wire.OServiceTLVTagsSSLCertName, ""),
-						wire.NewTLV(wire.OServiceTLVTagsSSLState, uint8(0x00)),
+						wire.NewTLVBE(wire.OServiceTLVTagsReconnectHere, net.JoinHostPort(s.cfg.OSCARHost, s.cfg.AdminPort)),
+						wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, cookie),
+						wire.NewTLVBE(wire.OServiceTLVTagsGroupID, wire.Admin),
+						wire.NewTLVBE(wire.OServiceTLVTagsSSLCertName, ""),
+						wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
 					},
 				},
 			},
@@ -623,11 +623,11 @@ func (s OServiceServiceForBOS) ServiceRequest(ctx context.Context, sess *state.S
 			Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceTLVTagsReconnectHere, net.JoinHostPort(s.cfg.OSCARHost, s.cfg.AlertPort)),
-						wire.NewTLV(wire.OServiceTLVTagsLoginCookie, cookie),
-						wire.NewTLV(wire.OServiceTLVTagsGroupID, wire.Alert),
-						wire.NewTLV(wire.OServiceTLVTagsSSLCertName, ""),
-						wire.NewTLV(wire.OServiceTLVTagsSSLState, uint8(0x00)),
+						wire.NewTLVBE(wire.OServiceTLVTagsReconnectHere, net.JoinHostPort(s.cfg.OSCARHost, s.cfg.AlertPort)),
+						wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, cookie),
+						wire.NewTLVBE(wire.OServiceTLVTagsGroupID, wire.Alert),
+						wire.NewTLVBE(wire.OServiceTLVTagsSSLCertName, ""),
+						wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
 					},
 				},
 			},
@@ -648,11 +648,11 @@ func (s OServiceServiceForBOS) ServiceRequest(ctx context.Context, sess *state.S
 			Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceTLVTagsReconnectHere, net.JoinHostPort(s.cfg.OSCARHost, s.cfg.BARTPort)),
-						wire.NewTLV(wire.OServiceTLVTagsLoginCookie, cookie),
-						wire.NewTLV(wire.OServiceTLVTagsGroupID, wire.BART),
-						wire.NewTLV(wire.OServiceTLVTagsSSLCertName, ""),
-						wire.NewTLV(wire.OServiceTLVTagsSSLState, uint8(0x00)),
+						wire.NewTLVBE(wire.OServiceTLVTagsReconnectHere, net.JoinHostPort(s.cfg.OSCARHost, s.cfg.BARTPort)),
+						wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, cookie),
+						wire.NewTLVBE(wire.OServiceTLVTagsGroupID, wire.BART),
+						wire.NewTLVBE(wire.OServiceTLVTagsSSLCertName, ""),
+						wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
 					},
 				},
 			},
@@ -673,17 +673,17 @@ func (s OServiceServiceForBOS) ServiceRequest(ctx context.Context, sess *state.S
 			Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceTLVTagsReconnectHere, net.JoinHostPort(s.cfg.OSCARHost, s.cfg.ChatNavPort)),
-						wire.NewTLV(wire.OServiceTLVTagsLoginCookie, cookie),
-						wire.NewTLV(wire.OServiceTLVTagsGroupID, wire.ChatNav),
-						wire.NewTLV(wire.OServiceTLVTagsSSLCertName, ""),
-						wire.NewTLV(wire.OServiceTLVTagsSSLState, uint8(0x00)),
+						wire.NewTLVBE(wire.OServiceTLVTagsReconnectHere, net.JoinHostPort(s.cfg.OSCARHost, s.cfg.ChatNavPort)),
+						wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, cookie),
+						wire.NewTLVBE(wire.OServiceTLVTagsGroupID, wire.ChatNav),
+						wire.NewTLVBE(wire.OServiceTLVTagsSSLCertName, ""),
+						wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
 					},
 				},
 			},
 		}, nil
 	case wire.Chat:
-		roomMeta, ok := inBody.Slice(0x01)
+		roomMeta, ok := inBody.Bytes(0x01)
 		if !ok {
 			return wire.SNACMessage{}, errors.New("missing room info")
 		}
@@ -715,11 +715,11 @@ func (s OServiceServiceForBOS) ServiceRequest(ctx context.Context, sess *state.S
 			Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
 				TLVRestBlock: wire.TLVRestBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceTLVTagsReconnectHere, net.JoinHostPort(s.cfg.OSCARHost, s.cfg.ChatPort)),
-						wire.NewTLV(wire.OServiceTLVTagsLoginCookie, cookie),
-						wire.NewTLV(wire.OServiceTLVTagsGroupID, wire.Chat),
-						wire.NewTLV(wire.OServiceTLVTagsSSLCertName, ""),
-						wire.NewTLV(wire.OServiceTLVTagsSSLState, uint8(0x00)),
+						wire.NewTLVBE(wire.OServiceTLVTagsReconnectHere, net.JoinHostPort(s.cfg.OSCARHost, s.cfg.ChatPort)),
+						wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, cookie),
+						wire.NewTLVBE(wire.OServiceTLVTagsGroupID, wire.Chat),
+						wire.NewTLVBE(wire.OServiceTLVTagsSSLCertName, ""),
+						wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
 					},
 				},
 			},

+ 24 - 24
foodgroup/oservice_test.go

@@ -84,11 +84,11 @@ func TestOServiceServiceForBOS_ServiceRequest(t *testing.T) {
 				Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.OServiceTLVTagsReconnectHere, "127.0.0.1:1234"),
-							wire.NewTLV(wire.OServiceTLVTagsLoginCookie, []byte("the-cookie")),
-							wire.NewTLV(wire.OServiceTLVTagsGroupID, wire.ChatNav),
-							wire.NewTLV(wire.OServiceTLVTagsSSLCertName, ""),
-							wire.NewTLV(wire.OServiceTLVTagsSSLState, uint8(0x00)),
+							wire.NewTLVBE(wire.OServiceTLVTagsReconnectHere, "127.0.0.1:1234"),
+							wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, []byte("the-cookie")),
+							wire.NewTLVBE(wire.OServiceTLVTagsGroupID, wire.ChatNav),
+							wire.NewTLVBE(wire.OServiceTLVTagsSSLCertName, ""),
+							wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
 						},
 					},
 				},
@@ -130,11 +130,11 @@ func TestOServiceServiceForBOS_ServiceRequest(t *testing.T) {
 				Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.OServiceTLVTagsReconnectHere, "127.0.0.1:1234"),
-							wire.NewTLV(wire.OServiceTLVTagsLoginCookie, []byte("the-cookie")),
-							wire.NewTLV(wire.OServiceTLVTagsGroupID, wire.Alert),
-							wire.NewTLV(wire.OServiceTLVTagsSSLCertName, ""),
-							wire.NewTLV(wire.OServiceTLVTagsSSLState, uint8(0x00)),
+							wire.NewTLVBE(wire.OServiceTLVTagsReconnectHere, "127.0.0.1:1234"),
+							wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, []byte("the-cookie")),
+							wire.NewTLVBE(wire.OServiceTLVTagsGroupID, wire.Alert),
+							wire.NewTLVBE(wire.OServiceTLVTagsSSLCertName, ""),
+							wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
 						},
 					},
 				},
@@ -176,11 +176,11 @@ func TestOServiceServiceForBOS_ServiceRequest(t *testing.T) {
 				Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.OServiceTLVTagsReconnectHere, "127.0.0.1:1234"),
-							wire.NewTLV(wire.OServiceTLVTagsLoginCookie, []byte("the-cookie")),
-							wire.NewTLV(wire.OServiceTLVTagsGroupID, wire.Admin),
-							wire.NewTLV(wire.OServiceTLVTagsSSLCertName, ""),
-							wire.NewTLV(wire.OServiceTLVTagsSSLState, uint8(0x00)),
+							wire.NewTLVBE(wire.OServiceTLVTagsReconnectHere, "127.0.0.1:1234"),
+							wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, []byte("the-cookie")),
+							wire.NewTLVBE(wire.OServiceTLVTagsGroupID, wire.Admin),
+							wire.NewTLVBE(wire.OServiceTLVTagsSSLCertName, ""),
+							wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
 						},
 					},
 				},
@@ -213,7 +213,7 @@ func TestOServiceServiceForBOS_ServiceRequest(t *testing.T) {
 					FoodGroup: wire.Chat,
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(0x01, wire.SNAC_0x01_0x04_TLVRoomInfo{
+							wire.NewTLVBE(0x01, wire.SNAC_0x01_0x04_TLVRoomInfo{
 								Exchange:       chatRoom.Exchange(),
 								Cookie:         chatRoom.Cookie(),
 								InstanceNumber: chatRoom.InstanceNumber(),
@@ -231,11 +231,11 @@ func TestOServiceServiceForBOS_ServiceRequest(t *testing.T) {
 				Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.OServiceTLVTagsReconnectHere, "127.0.0.1:1234"),
-							wire.NewTLV(wire.OServiceTLVTagsLoginCookie, []byte("the-auth-cookie")),
-							wire.NewTLV(wire.OServiceTLVTagsGroupID, wire.Chat),
-							wire.NewTLV(wire.OServiceTLVTagsSSLCertName, ""),
-							wire.NewTLV(wire.OServiceTLVTagsSSLState, uint8(0x00)),
+							wire.NewTLVBE(wire.OServiceTLVTagsReconnectHere, "127.0.0.1:1234"),
+							wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, []byte("the-auth-cookie")),
+							wire.NewTLVBE(wire.OServiceTLVTagsGroupID, wire.Chat),
+							wire.NewTLVBE(wire.OServiceTLVTagsSSLCertName, ""),
+							wire.NewTLVBE(wire.OServiceTLVTagsSSLState, uint8(0x00)),
 						},
 					},
 				},
@@ -279,7 +279,7 @@ func TestOServiceServiceForBOS_ServiceRequest(t *testing.T) {
 					FoodGroup: wire.Chat,
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(0x01, wire.SNAC_0x01_0x04_TLVRoomInfo{
+							wire.NewTLVBE(0x01, wire.SNAC_0x01_0x04_TLVRoomInfo{
 								Exchange:       8,
 								Cookie:         "the-chat-cookie",
 								InstanceNumber: 16,
@@ -373,7 +373,7 @@ func TestSetUserInfoFields(t *testing.T) {
 				Body: wire.SNAC_0x01_0x1E_OServiceSetUserInfoFields{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.OServiceUserInfoStatus, uint32(0x0000)),
+							wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
 						},
 					},
 				},
@@ -408,7 +408,7 @@ func TestSetUserInfoFields(t *testing.T) {
 				Body: wire.SNAC_0x01_0x1E_OServiceSetUserInfoFields{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.OServiceUserInfoStatus, uint32(0x0100)),
+							wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0100)),
 						},
 					},
 				},

+ 3 - 3
foodgroup/permit_deny.go

@@ -32,9 +32,9 @@ func (s PermitDenyService) RightsQuery(_ context.Context, frame wire.SNACFrame)
 		Body: wire.SNAC_0x09_0x03_PermitDenyRightsReply{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.PermitDenyTLVMaxDenies, uint16(100)),
-					wire.NewTLV(wire.PermitDenyTLVMaxPermits, uint16(100)),
-					wire.NewTLV(wire.PermitDenyTLVMaxTempPermits, uint16(100)),
+					wire.NewTLVBE(wire.PermitDenyTLVMaxDenies, uint16(100)),
+					wire.NewTLVBE(wire.PermitDenyTLVMaxPermits, uint16(100)),
+					wire.NewTLVBE(wire.PermitDenyTLVMaxTempPermits, uint16(100)),
 				},
 			},
 		},

+ 3 - 3
foodgroup/permit_deny_test.go

@@ -21,9 +21,9 @@ func TestPermitDenyService_RightsQuery(t *testing.T) {
 		Body: wire.SNAC_0x09_0x03_PermitDenyRightsReply{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.PermitDenyTLVMaxDenies, uint16(100)),
-					wire.NewTLV(wire.PermitDenyTLVMaxPermits, uint16(100)),
-					wire.NewTLV(wire.PermitDenyTLVMaxTempPermits, uint16(100)),
+					wire.NewTLVBE(wire.PermitDenyTLVMaxDenies, uint16(100)),
+					wire.NewTLVBE(wire.PermitDenyTLVMaxPermits, uint16(100)),
+					wire.NewTLVBE(wire.PermitDenyTLVMaxTempPermits, uint16(100)),
 				},
 			},
 		},

+ 12 - 4
foodgroup/test_helpers.go

@@ -77,10 +77,19 @@ type retrieveSessionParams []struct {
 // icqUserFinderParams is a helper struct that contains mock parameters for
 // ICQUserFinder methods
 type icqUserFinderParams struct {
-	findByUINParams
-	findByEmailParams
 	findByDetailsParams
+	findByEmailParams
 	findByInterestsParams
+	findByKeywordParams
+	findByUINParams
+}
+
+// findByKeywordParams is the list of parameters passed at the mock
+// ICQUserFinder.FindByKeyword call site
+type findByKeywordParams []struct {
+	keyword string
+	result  []state.User
+	err     error
 }
 
 // findByUINParams is the list of parameters passed at the mock
@@ -345,7 +354,6 @@ type relayToScreenNamesParams []struct {
 // relayToScreenNameParams is the list of parameters passed at the mock
 // MessageRelayer.RelayToScreenName call site
 type relayToScreenNameParams []struct {
-	cookie     string
 	screenName state.IdentScreenName
 	message    wire.SNACMessage
 }
@@ -690,7 +698,7 @@ func newTestSession(screenName state.DisplayScreenName, options ...func(session
 
 func userInfoWithBARTIcon(sess *state.Session, bid wire.BARTID) wire.TLVUserInfo {
 	info := sess.TLVUserInfo()
-	info.Append(wire.NewTLV(wire.OServiceUserInfoBARTInfo, bid))
+	info.Append(wire.NewTLVBE(wire.OServiceUserInfoBARTInfo, bid))
 	return info
 }
 

+ 9 - 3
foodgroup/types.go

@@ -175,14 +175,20 @@ type AccountManager interface {
 }
 
 type ICQUserFinder interface {
-	// FindByUIN returns a user where the UIN matches the ident screen name.
+	// FindByUIN returns a user with a matching UIN.
 	FindByUIN(UIN uint32) (state.User, error)
 	// FindByEmail returns a user with a matching email address.
 	FindByEmail(email string) (state.User, error)
-	// FindByDetails returns a user with either a matching first name, last name, or nickname.
+	// FindByDetails returns users with either a matching first name, last
+	// name, and nickname. Empty values are not included in the search
+	// parameters.
 	FindByDetails(firstName, lastName, nickName string) ([]state.User, error)
-	// FindByInterests returns a user who has at least one matching interest.
+	// FindByInterests returns users who have at least one matching interest
+	// for a given category code.
 	FindByInterests(code uint16, keywords []string) ([]state.User, error)
+	// FindByKeyword returns users with matching interest keyword across all
+	// interest categories.
+	FindByKeyword(keyword string) ([]state.User, error)
 }
 
 type ICQUserUpdater interface {

+ 1 - 1
server/http/mgmt_api.go

@@ -473,7 +473,7 @@ func postInstantMessageHandler(w http.ResponseWriter, r *http.Request, messageRe
 			},
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.ICBMTLVAOLIMData, tlv),
+					wire.NewTLVBE(wire.ICBMTLVAOLIMData, tlv),
 				},
 			},
 		},

+ 1 - 1
server/http/mgmt_api_test.go

@@ -1315,7 +1315,7 @@ func TestInstantMessageHandler_POST(t *testing.T) {
 					body := msg.Body.(wire.SNAC_0x04_0x07_ICBMChannelMsgToClient)
 					assert.Equal(t, params.sender.String(), body.TLVUserInfo.ScreenName)
 
-					b, ok := body.Slice(wire.ICBMTLVAOLIMData)
+					b, ok := body.Bytes(wire.ICBMTLVAOLIMData)
 					assert.True(t, ok)
 
 					txt, err := wire.UnmarshalICBMMessageText(b)

+ 1 - 1
server/oscar/admin.go

@@ -68,7 +68,7 @@ func (rt AdminServer) handleNewConnection(ctx context.Context, rwc io.ReadWriteC
 		return err
 	}
 
-	authCookie, ok := flap.Slice(wire.OServiceTLVTagsLoginCookie)
+	authCookie, ok := flap.Bytes(wire.OServiceTLVTagsLoginCookie)
 	if !ok {
 		return errors.New("unable to get session id from payload")
 	}

+ 1 - 1
server/oscar/auth.go

@@ -77,7 +77,7 @@ func (rt AuthServer) handleNewConnection(rwc io.ReadWriteCloser) error {
 	// presence of the roasted password TLV, however that proved an unreliable
 	// indicator of FLAP-auth because older ICQ clients appear to omit the
 	// roasted password TLV when the password is not stored client-side.
-	if _, hasScreenName := signonFrame.Uint16(wire.LoginTLVTagsScreenName); hasScreenName {
+	if _, hasScreenName := signonFrame.Uint16BE(wire.LoginTLVTagsScreenName); hasScreenName {
 		return rt.processFLAPAuth(signonFrame, flapc)
 	}
 

+ 1 - 1
server/oscar/bos.go

@@ -71,7 +71,7 @@ func (rt BOSServer) handleNewConnection(ctx context.Context, rwc io.ReadWriteClo
 		return err
 	}
 
-	authCookie, ok := flap.Slice(wire.OServiceTLVTagsLoginCookie)
+	authCookie, ok := flap.Bytes(wire.OServiceTLVTagsLoginCookie)
 	if !ok {
 		return errors.New("unable to get session id from payload")
 	}

+ 1 - 1
server/oscar/bos_test.go

@@ -45,7 +45,7 @@ func TestBOSService_handleNewConnection(t *testing.T) {
 		flapSignonFrame = wire.FLAPSignonFrame{
 			FLAPVersion: 1,
 		}
-		flapSignonFrame.Append(wire.NewTLV(wire.OServiceTLVTagsLoginCookie, []byte("the-cookie")))
+		flapSignonFrame.Append(wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, []byte("the-cookie")))
 		buf := &bytes.Buffer{}
 		assert.NoError(t, wire.MarshalBE(flapSignonFrame, buf))
 		flap = wire.FLAPFrame{

+ 1 - 1
server/oscar/chat.go

@@ -62,7 +62,7 @@ func (rt ChatServer) handleNewConnection(ctx context.Context, rwc io.ReadWriteCl
 		return err
 	}
 
-	authCookie, ok := flap.Slice(wire.OServiceTLVTagsLoginCookie)
+	authCookie, ok := flap.Bytes(wire.OServiceTLVTagsLoginCookie)
 	if !ok {
 		return errors.New("unable to get login cookie from payload")
 	}

+ 1 - 1
server/oscar/chat_test.go

@@ -31,7 +31,7 @@ func TestChatService_handleNewConnection(t *testing.T) {
 		flapSignonFrame = wire.FLAPSignonFrame{
 			FLAPVersion: 1,
 		}
-		flapSignonFrame.Append(wire.NewTLV(wire.OServiceTLVTagsLoginCookie, []byte(`the-chat-login-cookie`)))
+		flapSignonFrame.Append(wire.NewTLVBE(wire.OServiceTLVTagsLoginCookie, []byte(`the-chat-login-cookie`)))
 		buf := &bytes.Buffer{}
 		assert.NoError(t, wire.MarshalBE(flapSignonFrame, buf))
 		flap = wire.FLAPFrame{

+ 4 - 4
server/oscar/handler/admin_test.go

@@ -56,7 +56,7 @@ func TestAdminHandler_InfoQuery_RegistrationStatus(t *testing.T) {
 		Body: wire.SNAC_0x07_0x02_AdminInfoQuery{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.AdminTLVRegistrationStatus, uint16(0x00))},
+					wire.NewTLVBE(wire.AdminTLVRegistrationStatus, uint16(0x00))},
 			},
 		},
 	}
@@ -69,7 +69,7 @@ func TestAdminHandler_InfoQuery_RegistrationStatus(t *testing.T) {
 			Permissions: wire.AdminInfoPermissionsReadWrite,
 			TLVBlock: wire.TLVBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.AdminTLVRegistrationStatus, wire.AdminInfoRegStatusFullDisclosure),
+					wire.NewTLVBE(wire.AdminTLVRegistrationStatus, wire.AdminInfoRegStatusFullDisclosure),
 				},
 			},
 		},
@@ -102,7 +102,7 @@ func TestAdminHandler_InfoChangeRequest_ScreenNameFormatted(t *testing.T) {
 		Body: wire.SNAC_0x07_0x04_AdminInfoChangeRequest{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.AdminTLVScreenNameFormatted, "Chatting Chuck")},
+					wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "Chatting Chuck")},
 			},
 		},
 	}
@@ -115,7 +115,7 @@ func TestAdminHandler_InfoChangeRequest_ScreenNameFormatted(t *testing.T) {
 			Permissions: wire.AdminInfoPermissionsReadWrite,
 			TLVBlock: wire.TLVBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.AdminTLVScreenNameFormatted, "Chatting Chuck"),
+					wire.NewTLVBE(wire.AdminTLVScreenNameFormatted, "Chatting Chuck"),
 				},
 			},
 		},

+ 2 - 2
server/oscar/handler/buddy_test.go

@@ -20,7 +20,7 @@ func TestBuddyHandler_RightsQuery(t *testing.T) {
 		Body: wire.SNAC_0x03_0x02_BuddyRightsQuery{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(0x01, uint16(1000)),
+					wire.NewTLVBE(0x01, uint16(1000)),
 				},
 			},
 		},
@@ -33,7 +33,7 @@ func TestBuddyHandler_RightsQuery(t *testing.T) {
 		Body: wire.SNAC_0x03_0x03_BuddyRightsReply{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(0x01, uint16(1000)),
+					wire.NewTLVBE(0x01, uint16(1000)),
 				},
 			},
 		},

+ 13 - 0
server/oscar/handler/feedbag.go

@@ -16,6 +16,7 @@ type FeedbagService interface {
 	DeleteItem(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x13_0x0A_FeedbagDeleteItem) (wire.SNACMessage, error)
 	Query(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame) (wire.SNACMessage, error)
 	QueryIfModified(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x13_0x05_FeedbagQueryIfModified) (wire.SNACMessage, error)
+	RespondAuthorizeToHost(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost) error
 	RightsQuery(ctx context.Context, inFrame wire.SNACFrame) wire.SNACMessage
 	StartCluster(ctx context.Context, inFrame wire.SNACFrame, inBody wire.SNAC_0x13_0x11_FeedbagStartCluster)
 	UpsertItem(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, items []wire.FeedbagItem) (wire.SNACMessage, error)
@@ -126,3 +127,15 @@ func (h FeedbagHandler) EndCluster(ctx context.Context, _ *state.Session, inFram
 	h.LogRequest(ctx, inFrame, nil)
 	return nil
 }
+
+func (h FeedbagHandler) RespondAuthorizeToHost(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, r io.Reader, rw oscar.ResponseWriter) error {
+	inBody := wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost{}
+	if err := wire.UnmarshalBE(&inBody, r); err != nil {
+		return err
+	}
+	if err := h.FeedbagService.RespondAuthorizeToHost(ctx, sess, inFrame, inBody); err != nil {
+		return err
+	}
+	h.LogRequest(ctx, inFrame, inBody)
+	return nil
+}

+ 25 - 0
server/oscar/handler/feedbag_test.go

@@ -345,3 +345,28 @@ func TestFeedbagHandler_Use(t *testing.T) {
 
 	assert.NoError(t, h.Use(nil, nil, input.Frame, buf, responseWriter))
 }
+
+func TestFeedbagHandler_RespondAuthorizeToHost(t *testing.T) {
+	input := wire.SNACMessage{
+		Frame: wire.SNACFrame{
+			FoodGroup: wire.Feedbag,
+			SubGroup:  wire.FeedbagRequestAuthorizeToHost,
+		},
+		Body: wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost{
+			ScreenName: "theScreenName",
+		},
+	}
+
+	svc := newMockFeedbagService(t)
+	svc.EXPECT().
+		RespondAuthorizeToHost(mock.Anything, mock.Anything, input.Frame, input.Body).
+		Return(nil)
+
+	h := NewFeedbagHandler(slog.Default(), svc)
+	responseWriter := newMockResponseWriter(t)
+
+	buf := &bytes.Buffer{}
+	assert.NoError(t, wire.MarshalBE(input.Body, buf))
+
+	assert.NoError(t, h.RespondAuthorizeToHost(nil, nil, input.Frame, buf, responseWriter))
+}

+ 28 - 1
server/oscar/handler/icq.go

@@ -19,8 +19,11 @@ type ICQService interface {
 	DeleteMsgReq(ctx context.Context, sess *state.Session, seq uint16) error
 	FindByDetails(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0515_DBQueryMetaReqSearchByDetails, seq uint16) error
 	FindByEmail(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0529_DBQueryMetaReqSearchByEmail, seq uint16) error
+	FindByEmail3(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3, seq uint16) error
 	FindByInterests(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages, seq uint16) error
 	FindByUIN(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error
+	FindByUIN2(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2, seq uint16) error
+	FindByWhitePages2(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2, seq uint16) error
 	FullUserInfo(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error
 	OfflineMsgReq(ctx context.Context, sess *state.Session, seq uint16) error
 	SetAffiliations(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x041A_DBQueryMetaReqSetAffiliations, seq uint16) error
@@ -60,7 +63,7 @@ func (rt ICQHandler) DBQuery(ctx context.Context, sess *state.Session, inFrame w
 		return err
 	}
 
-	md, ok := inBody.Slice(wire.ICQTLVTagsMetadata)
+	md, ok := inBody.Bytes(wire.ICQTLVTagsMetadata)
 	if !ok {
 		return errors.New("invalid ICQ frame")
 	}
@@ -126,6 +129,14 @@ func (rt ICQHandler) DBQuery(ctx context.Context, sess *state.Session, inFrame w
 			if err := rt.ICQService.FindByUIN(ctx, sess, req, icqMD.Seq); err != nil {
 				return err
 			}
+		case wire.ICQDBQueryMetaReqSearchByUIN2:
+			req := wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2{}
+			if err := wire.UnmarshalLE(&req, buf); err != nil {
+				return err
+			}
+			if err := rt.ICQService.FindByUIN2(ctx, sess, req, icqMD.Seq); err != nil {
+				return err
+			}
 		case wire.ICQDBQueryMetaReqSearchByEmail:
 			req := wire.ICQ_0x07D0_0x0529_DBQueryMetaReqSearchByEmail{}
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
@@ -134,6 +145,14 @@ func (rt ICQHandler) DBQuery(ctx context.Context, sess *state.Session, inFrame w
 			if err := rt.ICQService.FindByEmail(ctx, sess, req, icqMD.Seq); err != nil {
 				return err
 			}
+		case wire.ICQDBQueryMetaReqSearchByEmail3:
+			req := wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3{}
+			if err := wire.UnmarshalLE(&req, buf); err != nil {
+				return err
+			}
+			if err := rt.ICQService.FindByEmail3(ctx, sess, req, icqMD.Seq); err != nil {
+				return err
+			}
 		case wire.ICQDBQueryMetaReqSearchByDetails:
 			req := wire.ICQ_0x07D0_0x0515_DBQueryMetaReqSearchByDetails{}
 			if err := wire.UnmarshalLE(&req, buf); err != nil {
@@ -150,6 +169,14 @@ func (rt ICQHandler) DBQuery(ctx context.Context, sess *state.Session, inFrame w
 			if err := rt.ICQService.FindByInterests(ctx, sess, req, icqMD.Seq); err != nil {
 				return err
 			}
+		case wire.ICQDBQueryMetaReqSearchWhitePages2:
+			req := wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2{}
+			if err := wire.UnmarshalLE(&req, buf); err != nil {
+				return err
+			}
+			if err := rt.ICQService.FindByWhitePages2(ctx, sess, req, icqMD.Seq); err != nil {
+				return err
+			}
 		case wire.ICQDBQueryMetaReqSetBasicInfo:
 			req := wire.ICQ_0x07D0_0x03EA_DBQueryMetaReqSetBasicInfo{}
 			if err := wire.UnmarshalLE(&req, buf); err != nil {

+ 173 - 38
server/oscar/handler/icq_test.go

@@ -34,23 +34,26 @@ func TestICQHandler_DBQuery(t *testing.T) {
 		wantErr error
 	}
 	type allMockParams struct {
-		deleteMsgReq    *mockParam
-		findByDetails   *mockParam
-		findByEmail     *mockParam
-		findByInterests *mockParam
-		findByUIN       *mockParam
-		fullUserInfo    *mockParam
-		offlineMsgReq   *mockParam
-		setAffiliations *mockParam
-		setBasicInfo    *mockParam
-		setEmails       *mockParam
-		setInterests    *mockParam
-		setMoreInfo     *mockParam
-		setPermissions  *mockParam
-		setUserNotes    *mockParam
-		setWorkInfo     *mockParam
-		shortUserInfo   *mockParam
-		xmlReqData      *mockParam
+		deleteMsgReq      *mockParam
+		findByDetails     *mockParam
+		findByEmail       *mockParam
+		findByEmail3      *mockParam
+		findByInterests   *mockParam
+		findByUIN         *mockParam
+		findByUIN2        *mockParam
+		findByWhitePages2 *mockParam
+		fullUserInfo      *mockParam
+		offlineMsgReq     *mockParam
+		setAffiliations   *mockParam
+		setBasicInfo      *mockParam
+		setEmails         *mockParam
+		setInterests      *mockParam
+		setMoreInfo       *mockParam
+		setPermissions    *mockParam
+		setUserNotes      *mockParam
+		setWorkInfo       *mockParam
+		shortUserInfo     *mockParam
+		xmlReqData        *mockParam
 	}
 	tests := []struct {
 		name          string
@@ -64,7 +67,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -96,7 +99,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -128,7 +131,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -160,7 +163,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -192,7 +195,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -224,7 +227,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -249,6 +252,46 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "MetaReqSearchByUIN2 - happy path",
+			reqParams: reqParams{
+				sess: &state.Session{},
+				inBody: wire.SNAC_0x15_0x02_BQuery{
+					TLVRestBlock: wire.TLVRestBlock{
+						TLVList: wire.TLVList{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+								Message: ICQMetaRequest{
+									ICQMetadata: wire.ICQMetadata{
+										ReqType: wire.ICQDBQueryMetaReq,
+										Seq:     1,
+									},
+									ReqSubType: wire.ICQDBQueryMetaReqSearchByUIN2,
+									MetaRequest: wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2{
+										TLVRestBlock: wire.TLVRestBlock{
+											TLVList: wire.TLVList{
+												wire.NewTLVBE(1, uint16(1)),
+											},
+										},
+									},
+								},
+							}),
+						},
+					},
+				},
+				seq: 1,
+			},
+			allMockParams: allMockParams{
+				findByUIN2: &mockParam{
+					req: wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2{
+						TLVRestBlock: wire.TLVRestBlock{
+							TLVList: wire.TLVList{
+								wire.NewTLVBE(1, uint16(1)),
+							},
+						},
+					},
+				},
+			},
+		},
 		{
 			name: "MetaReqSearchByEmail - happy path",
 			reqParams: reqParams{
@@ -256,7 +299,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -281,6 +324,46 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "MetaReqSearchByEmail3 - happy path",
+			reqParams: reqParams{
+				sess: &state.Session{},
+				inBody: wire.SNAC_0x15_0x02_BQuery{
+					TLVRestBlock: wire.TLVRestBlock{
+						TLVList: wire.TLVList{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+								Message: ICQMetaRequest{
+									ICQMetadata: wire.ICQMetadata{
+										ReqType: wire.ICQDBQueryMetaReq,
+										Seq:     1,
+									},
+									ReqSubType: wire.ICQDBQueryMetaReqSearchByEmail3,
+									MetaRequest: wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3{
+										TLVRestBlock: wire.TLVRestBlock{
+											TLVList: wire.TLVList{
+												wire.NewTLVBE(1, uint16(1)),
+											},
+										},
+									},
+								},
+							}),
+						},
+					},
+				},
+				seq: 1,
+			},
+			allMockParams: allMockParams{
+				findByEmail3: &mockParam{
+					req: wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3{
+						TLVRestBlock: wire.TLVRestBlock{
+							TLVList: wire.TLVList{
+								wire.NewTLVBE(1, uint16(1)),
+							},
+						},
+					},
+				},
+			},
+		},
 		{
 			name: "MetaReqSearchByDetails - happy path",
 			reqParams: reqParams{
@@ -288,7 +371,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -320,7 +403,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -345,6 +428,46 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "MetaReqSearchWhitePages2 - happy path",
+			reqParams: reqParams{
+				sess: &state.Session{},
+				inBody: wire.SNAC_0x15_0x02_BQuery{
+					TLVRestBlock: wire.TLVRestBlock{
+						TLVList: wire.TLVList{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+								Message: ICQMetaRequest{
+									ICQMetadata: wire.ICQMetadata{
+										ReqType: wire.ICQDBQueryMetaReq,
+										Seq:     1,
+									},
+									ReqSubType: wire.ICQDBQueryMetaReqSearchWhitePages2,
+									MetaRequest: wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2{
+										TLVRestBlock: wire.TLVRestBlock{
+											TLVList: wire.TLVList{
+												wire.NewTLVBE(1, uint16(1)),
+											},
+										},
+									},
+								},
+							}),
+						},
+					},
+				},
+				seq: 1,
+			},
+			allMockParams: allMockParams{
+				findByWhitePages2: &mockParam{
+					req: wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2{
+						TLVRestBlock: wire.TLVRestBlock{
+							TLVList: wire.TLVList{
+								wire.NewTLVBE(1, uint16(1)),
+							},
+						},
+					},
+				},
+			},
+		},
 		{
 			name: "MetaReqSetBasicInfo - happy path",
 			reqParams: reqParams{
@@ -352,7 +475,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -384,7 +507,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -416,7 +539,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -448,7 +571,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -480,7 +603,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -526,7 +649,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -572,7 +695,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -634,7 +757,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -657,7 +780,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryMetaReq,
@@ -681,7 +804,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: wire.ICQMetadataWithSubType{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryOfflineMsgReq,
@@ -705,7 +828,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: wire.ICQMetadataWithSubType{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: wire.ICQDBQueryDeleteMsgReq,
@@ -729,7 +852,7 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				inBody: wire.SNAC_0x15_0x02_BQuery{
 					TLVRestBlock: wire.TLVRestBlock{
 						TLVList: wire.TLVList{
-							wire.NewTLV(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
+							wire.NewTLVBE(wire.ICQTLVTagsMetadata, wire.ICQMessageReplyEnvelope{
 								Message: ICQMetaRequest{
 									ICQMetadata: wire.ICQMetadata{
 										ReqType: 0x13B4,
@@ -772,10 +895,18 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				icqService.EXPECT().
 					FindByUIN(mock.Anything, tt.reqParams.sess, tt.allMockParams.findByUIN.req, tt.reqParams.seq).
 					Return(tt.allMockParams.findByUIN.wantErr)
+			case tt.allMockParams.findByUIN2 != nil:
+				icqService.EXPECT().
+					FindByUIN2(mock.Anything, tt.reqParams.sess, tt.allMockParams.findByUIN2.req, tt.reqParams.seq).
+					Return(tt.allMockParams.findByUIN2.wantErr)
 			case tt.allMockParams.findByEmail != nil:
 				icqService.EXPECT().
 					FindByEmail(mock.Anything, tt.reqParams.sess, tt.allMockParams.findByEmail.req, tt.reqParams.seq).
 					Return(tt.allMockParams.findByEmail.wantErr)
+			case tt.allMockParams.findByEmail3 != nil:
+				icqService.EXPECT().
+					FindByEmail3(mock.Anything, tt.reqParams.sess, tt.allMockParams.findByEmail3.req, tt.reqParams.seq).
+					Return(tt.allMockParams.findByEmail3.wantErr)
 			case tt.allMockParams.findByDetails != nil:
 				icqService.EXPECT().
 					FindByDetails(mock.Anything, tt.reqParams.sess, tt.allMockParams.findByDetails.req, tt.reqParams.seq).
@@ -784,6 +915,10 @@ func TestICQHandler_DBQuery(t *testing.T) {
 				icqService.EXPECT().
 					FindByInterests(mock.Anything, tt.reqParams.sess, tt.allMockParams.findByInterests.req, tt.reqParams.seq).
 					Return(tt.allMockParams.findByInterests.wantErr)
+			case tt.allMockParams.findByWhitePages2 != nil:
+				icqService.EXPECT().
+					FindByWhitePages2(mock.Anything, tt.reqParams.sess, tt.allMockParams.findByWhitePages2.req, tt.reqParams.seq).
+					Return(tt.allMockParams.findByWhitePages2.wantErr)
 			case tt.allMockParams.setBasicInfo != nil:
 				icqService.EXPECT().
 					SetBasicInfo(mock.Anything, tt.reqParams.sess, tt.allMockParams.setBasicInfo.req, tt.reqParams.seq).

+ 1 - 1
server/oscar/handler/locate_test.go

@@ -48,7 +48,7 @@ func TestLocateHandler_RightsQuery(t *testing.T) {
 		Body: wire.SNAC_0x02_0x03_LocateRightsReply{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(0x01, uint16(1000)),
+					wire.NewTLVBE(0x01, uint16(1000)),
 				},
 			},
 		},

+ 49 - 0
server/oscar/handler/mock_feedbag_test.go

@@ -200,6 +200,55 @@ func (_c *mockFeedbagService_QueryIfModified_Call) RunAndReturn(run func(context
 	return _c
 }
 
+// RespondAuthorizeToHost provides a mock function with given fields: ctx, sess, inFrame, inBody
+func (_m *mockFeedbagService) RespondAuthorizeToHost(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost) error {
+	ret := _m.Called(ctx, sess, inFrame, inBody)
+
+	if len(ret) == 0 {
+		panic("no return value specified for RespondAuthorizeToHost")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *state.Session, wire.SNACFrame, wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost) error); ok {
+		r0 = rf(ctx, sess, inFrame, inBody)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// mockFeedbagService_RespondAuthorizeToHost_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RespondAuthorizeToHost'
+type mockFeedbagService_RespondAuthorizeToHost_Call struct {
+	*mock.Call
+}
+
+// RespondAuthorizeToHost is a helper method to define mock.On call
+//   - ctx context.Context
+//   - sess *state.Session
+//   - inFrame wire.SNACFrame
+//   - inBody wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost
+func (_e *mockFeedbagService_Expecter) RespondAuthorizeToHost(ctx interface{}, sess interface{}, inFrame interface{}, inBody interface{}) *mockFeedbagService_RespondAuthorizeToHost_Call {
+	return &mockFeedbagService_RespondAuthorizeToHost_Call{Call: _e.mock.On("RespondAuthorizeToHost", ctx, sess, inFrame, inBody)}
+}
+
+func (_c *mockFeedbagService_RespondAuthorizeToHost_Call) Run(run func(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, inBody wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost)) *mockFeedbagService_RespondAuthorizeToHost_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context), args[1].(*state.Session), args[2].(wire.SNACFrame), args[3].(wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost))
+	})
+	return _c
+}
+
+func (_c *mockFeedbagService_RespondAuthorizeToHost_Call) Return(_a0 error) *mockFeedbagService_RespondAuthorizeToHost_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockFeedbagService_RespondAuthorizeToHost_Call) RunAndReturn(run func(context.Context, *state.Session, wire.SNACFrame, wire.SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost) error) *mockFeedbagService_RespondAuthorizeToHost_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // RightsQuery provides a mock function with given fields: ctx, inFrame
 func (_m *mockFeedbagService) RightsQuery(ctx context.Context, inFrame wire.SNACFrame) wire.SNACMessage {
 	ret := _m.Called(ctx, inFrame)

+ 147 - 0
server/oscar/handler/mock_icq_service_test.go

@@ -170,6 +170,55 @@ func (_c *mockICQService_FindByEmail_Call) RunAndReturn(run func(context.Context
 	return _c
 }
 
+// FindByEmail3 provides a mock function with given fields: ctx, sess, req, seq
+func (_m *mockICQService) FindByEmail3(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3, seq uint16) error {
+	ret := _m.Called(ctx, sess, req, seq)
+
+	if len(ret) == 0 {
+		panic("no return value specified for FindByEmail3")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *state.Session, wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3, uint16) error); ok {
+		r0 = rf(ctx, sess, req, seq)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// mockICQService_FindByEmail3_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindByEmail3'
+type mockICQService_FindByEmail3_Call struct {
+	*mock.Call
+}
+
+// FindByEmail3 is a helper method to define mock.On call
+//   - ctx context.Context
+//   - sess *state.Session
+//   - req wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3
+//   - seq uint16
+func (_e *mockICQService_Expecter) FindByEmail3(ctx interface{}, sess interface{}, req interface{}, seq interface{}) *mockICQService_FindByEmail3_Call {
+	return &mockICQService_FindByEmail3_Call{Call: _e.mock.On("FindByEmail3", ctx, sess, req, seq)}
+}
+
+func (_c *mockICQService_FindByEmail3_Call) Run(run func(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3, seq uint16)) *mockICQService_FindByEmail3_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context), args[1].(*state.Session), args[2].(wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3), args[3].(uint16))
+	})
+	return _c
+}
+
+func (_c *mockICQService_FindByEmail3_Call) Return(_a0 error) *mockICQService_FindByEmail3_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockICQService_FindByEmail3_Call) RunAndReturn(run func(context.Context, *state.Session, wire.ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3, uint16) error) *mockICQService_FindByEmail3_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // FindByInterests provides a mock function with given fields: ctx, sess, req, seq
 func (_m *mockICQService) FindByInterests(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages, seq uint16) error {
 	ret := _m.Called(ctx, sess, req, seq)
@@ -268,6 +317,104 @@ func (_c *mockICQService_FindByUIN_Call) RunAndReturn(run func(context.Context,
 	return _c
 }
 
+// FindByUIN2 provides a mock function with given fields: ctx, sess, req, seq
+func (_m *mockICQService) FindByUIN2(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2, seq uint16) error {
+	ret := _m.Called(ctx, sess, req, seq)
+
+	if len(ret) == 0 {
+		panic("no return value specified for FindByUIN2")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *state.Session, wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2, uint16) error); ok {
+		r0 = rf(ctx, sess, req, seq)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// mockICQService_FindByUIN2_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindByUIN2'
+type mockICQService_FindByUIN2_Call struct {
+	*mock.Call
+}
+
+// FindByUIN2 is a helper method to define mock.On call
+//   - ctx context.Context
+//   - sess *state.Session
+//   - req wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2
+//   - seq uint16
+func (_e *mockICQService_Expecter) FindByUIN2(ctx interface{}, sess interface{}, req interface{}, seq interface{}) *mockICQService_FindByUIN2_Call {
+	return &mockICQService_FindByUIN2_Call{Call: _e.mock.On("FindByUIN2", ctx, sess, req, seq)}
+}
+
+func (_c *mockICQService_FindByUIN2_Call) Run(run func(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2, seq uint16)) *mockICQService_FindByUIN2_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context), args[1].(*state.Session), args[2].(wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2), args[3].(uint16))
+	})
+	return _c
+}
+
+func (_c *mockICQService_FindByUIN2_Call) Return(_a0 error) *mockICQService_FindByUIN2_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockICQService_FindByUIN2_Call) RunAndReturn(run func(context.Context, *state.Session, wire.ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2, uint16) error) *mockICQService_FindByUIN2_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// FindByWhitePages2 provides a mock function with given fields: ctx, sess, req, seq
+func (_m *mockICQService) FindByWhitePages2(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2, seq uint16) error {
+	ret := _m.Called(ctx, sess, req, seq)
+
+	if len(ret) == 0 {
+		panic("no return value specified for FindByWhitePages2")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *state.Session, wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2, uint16) error); ok {
+		r0 = rf(ctx, sess, req, seq)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// mockICQService_FindByWhitePages2_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindByWhitePages2'
+type mockICQService_FindByWhitePages2_Call struct {
+	*mock.Call
+}
+
+// FindByWhitePages2 is a helper method to define mock.On call
+//   - ctx context.Context
+//   - sess *state.Session
+//   - req wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2
+//   - seq uint16
+func (_e *mockICQService_Expecter) FindByWhitePages2(ctx interface{}, sess interface{}, req interface{}, seq interface{}) *mockICQService_FindByWhitePages2_Call {
+	return &mockICQService_FindByWhitePages2_Call{Call: _e.mock.On("FindByWhitePages2", ctx, sess, req, seq)}
+}
+
+func (_c *mockICQService_FindByWhitePages2_Call) Run(run func(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2, seq uint16)) *mockICQService_FindByWhitePages2_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context), args[1].(*state.Session), args[2].(wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2), args[3].(uint16))
+	})
+	return _c
+}
+
+func (_c *mockICQService_FindByWhitePages2_Call) Return(_a0 error) *mockICQService_FindByWhitePages2_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockICQService_FindByWhitePages2_Call) RunAndReturn(run func(context.Context, *state.Session, wire.ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2, uint16) error) *mockICQService_FindByWhitePages2_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // FullUserInfo provides a mock function with given fields: ctx, sess, req, seq
 func (_m *mockICQService) FullUserInfo(ctx context.Context, sess *state.Session, req wire.ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN, seq uint16) error {
 	ret := _m.Called(ctx, sess, req, seq)

+ 3 - 3
server/oscar/handler/oservice_test.go

@@ -65,7 +65,7 @@ func TestOServiceBOSHandler_ServiceRequest(t *testing.T) {
 		Body: wire.SNAC_0x01_0x05_OServiceServiceResponse{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(0x01, uint16(1000)),
+					wire.NewTLVBE(0x01, uint16(1000)),
 				},
 			},
 		},
@@ -262,7 +262,7 @@ func TestOServiceHandler_RateParamsSubAdd(t *testing.T) {
 		Body: wire.SNAC_0x01_0x08_OServiceRateParamsSubAdd{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(0x01, []byte{1, 2, 3, 4}),
+					wire.NewTLVBE(0x01, []byte{1, 2, 3, 4}),
 				},
 			},
 		},
@@ -296,7 +296,7 @@ func TestOServiceHandler_SetUserInfoFields(t *testing.T) {
 		Body: wire.SNAC_0x01_0x1E_OServiceSetUserInfoFields{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(0x01, []byte{1, 2, 3, 4}),
+					wire.NewTLVBE(0x01, []byte{1, 2, 3, 4}),
 				},
 			},
 		},

+ 1 - 1
server/oscar/handler/permit_deny_test.go

@@ -27,7 +27,7 @@ func TestPermitDenyHandler_RightsQuery(t *testing.T) {
 		Body: wire.SNAC_0x09_0x03_PermitDenyRightsReply{
 			TLVRestBlock: wire.TLVRestBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(0x01, uint16(1000)),
+					wire.NewTLVBE(0x01, uint16(1000)),
 				},
 			},
 		},

+ 1 - 0
server/oscar/handler/routes.go

@@ -53,6 +53,7 @@ func NewBOSRouter(h Handlers) oscar.Router {
 	router.Register(wire.Feedbag, wire.FeedbagStartCluster, h.FeedbagHandler.StartCluster)
 	router.Register(wire.Feedbag, wire.FeedbagUpdateItem, h.FeedbagHandler.UpdateItem)
 	router.Register(wire.Feedbag, wire.FeedbagUse, h.FeedbagHandler.Use)
+	router.Register(wire.Feedbag, wire.FeedbagRespondAuthorizeToHost, h.FeedbagHandler.RespondAuthorizeToHost)
 
 	router.Register(wire.ICQ, wire.ICQDBQuery, h.ICQHandler.DBQuery)
 

+ 8 - 8
state/chat.go

@@ -104,19 +104,19 @@ func (c ChatRoom) TLVList() []wire.TLV {
 		// - 4 Instancing Allowed
 		// - 8 Occupant Peek Allowed
 		// It's unclear what effect they actually have.
-		wire.NewTLV(wire.ChatRoomTLVFlags, uint16(15)),
-		wire.NewTLV(wire.ChatRoomTLVCreateTime, uint32(c.createTime.Unix())),
-		wire.NewTLV(wire.ChatRoomTLVMaxMsgLen, uint16(1024)),
-		wire.NewTLV(wire.ChatRoomTLVMaxOccupancy, uint16(100)),
+		wire.NewTLVBE(wire.ChatRoomTLVFlags, uint16(15)),
+		wire.NewTLVBE(wire.ChatRoomTLVCreateTime, uint32(c.createTime.Unix())),
+		wire.NewTLVBE(wire.ChatRoomTLVMaxMsgLen, uint16(1024)),
+		wire.NewTLVBE(wire.ChatRoomTLVMaxOccupancy, uint16(100)),
 		// From protocols/oscar/family_chatnav.c in lib purple, these are the
 		// room creation permission values:
 		// - 0  creation not allowed
 		// - 1  room creation allowed
 		// - 2  exchange creation allowed
 		// It's unclear what effect they actually have.
-		wire.NewTLV(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
-		wire.NewTLV(wire.ChatRoomTLVFullyQualifiedName, c.name),
-		wire.NewTLV(wire.ChatRoomTLVRoomName, c.name),
-		wire.NewTLV(wire.ChatRoomTLVMaxMsgVisLen, uint16(1024)),
+		wire.NewTLVBE(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
+		wire.NewTLVBE(wire.ChatRoomTLVFullyQualifiedName, c.name),
+		wire.NewTLVBE(wire.ChatRoomTLVRoomName, c.name),
+		wire.NewTLVBE(wire.ChatRoomTLVMaxMsgVisLen, uint16(1024)),
 	}
 }

+ 8 - 8
state/chat_test.go

@@ -13,14 +13,14 @@ func TestChatRoom_TLVList(t *testing.T) {
 
 	have := room.TLVList()
 	want := []wire.TLV{
-		wire.NewTLV(wire.ChatRoomTLVFlags, uint16(15)),
-		wire.NewTLV(wire.ChatRoomTLVCreateTime, uint32(room.createTime.Unix())),
-		wire.NewTLV(wire.ChatRoomTLVMaxMsgLen, uint16(1024)),
-		wire.NewTLV(wire.ChatRoomTLVMaxOccupancy, uint16(100)),
-		wire.NewTLV(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
-		wire.NewTLV(wire.ChatRoomTLVFullyQualifiedName, room.name),
-		wire.NewTLV(wire.ChatRoomTLVRoomName, room.name),
-		wire.NewTLV(wire.ChatRoomTLVMaxMsgVisLen, uint16(1024)),
+		wire.NewTLVBE(wire.ChatRoomTLVFlags, uint16(15)),
+		wire.NewTLVBE(wire.ChatRoomTLVCreateTime, uint32(room.createTime.Unix())),
+		wire.NewTLVBE(wire.ChatRoomTLVMaxMsgLen, uint16(1024)),
+		wire.NewTLVBE(wire.ChatRoomTLVMaxOccupancy, uint16(100)),
+		wire.NewTLVBE(wire.ChatRoomTLVNavCreatePerms, uint8(2)),
+		wire.NewTLVBE(wire.ChatRoomTLVFullyQualifiedName, room.name),
+		wire.NewTLVBE(wire.ChatRoomTLVRoomName, room.name),
+		wire.NewTLVBE(wire.ChatRoomTLVMaxMsgVisLen, uint16(1024)),
 	}
 
 	assert.Equal(t, want, have)

+ 6 - 6
state/session.go

@@ -249,32 +249,32 @@ func (s *Session) userInfo() wire.TLVList {
 	tlvs := wire.TLVList{}
 
 	// sign-in timestamp
-	tlvs.Append(wire.NewTLV(wire.OServiceUserInfoSignonTOD, uint32(s.signonTime.Unix())))
+	tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(s.signonTime.Unix())))
 
 	// user info flags
 	uFlags := s.userInfoBitmask
 	if s.awayMessage != "" {
 		uFlags |= wire.OServiceUserFlagUnavailable
 	}
-	tlvs.Append(wire.NewTLV(wire.OServiceUserInfoUserFlags, uFlags))
+	tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uFlags))
 
 	// user status flags
-	tlvs.Append(wire.NewTLV(wire.OServiceUserInfoStatus, s.userStatusBitmask))
+	tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoStatus, s.userStatusBitmask))
 
 	// idle status
 	if s.idle {
-		tlvs.Append(wire.NewTLV(wire.OServiceUserInfoIdleTime, uint16(s.nowFn().Sub(s.idleTime).Minutes())))
+		tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(s.nowFn().Sub(s.idleTime).Minutes())))
 	}
 
 	// ICQ direct-connect info. The TLV is required for buddy arrival events to
 	// work in ICQ, even if the values are set to default.
 	if s.userInfoBitmask&wire.OServiceUserFlagICQ == wire.OServiceUserFlagICQ {
-		tlvs.Append(wire.NewTLV(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{}))
+		tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{}))
 	}
 
 	// capabilities (buddy icon, chat, etc...)
 	if len(s.caps) > 0 {
-		tlvs.Append(wire.NewTLV(wire.OServiceUserInfoOscarCaps, s.caps))
+		tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoOscarCaps, s.caps))
 	}
 
 	return tlvs

+ 27 - 27
state/session_test.go

@@ -80,9 +80,9 @@ func TestSession_TLVUserInfo(t *testing.T) {
 				WarningLevel: 10,
 				TLVBlock: wire.TLVBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceUserInfoSignonTOD, uint32(1)),
-						wire.NewTLV(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
-						wire.NewTLV(wire.OServiceUserInfoStatus, uint32(0x0000)),
+						wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
+						wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
+						wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
 					},
 				},
 			},
@@ -102,10 +102,10 @@ func TestSession_TLVUserInfo(t *testing.T) {
 				ScreenName: "1000003",
 				TLVBlock: wire.TLVBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceUserInfoSignonTOD, uint32(1)),
-						wire.NewTLV(wire.OServiceUserInfoUserFlags, wire.OServiceUserFlagOSCARFree|wire.OServiceUserFlagICQ),
-						wire.NewTLV(wire.OServiceUserInfoStatus, uint32(0x0000)),
-						wire.NewTLV(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{}),
+						wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
+						wire.NewTLVBE(wire.OServiceUserInfoUserFlags, wire.OServiceUserFlagOSCARFree|wire.OServiceUserFlagICQ),
+						wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
+						wire.NewTLVBE(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{}),
 					},
 				},
 			},
@@ -121,9 +121,9 @@ func TestSession_TLVUserInfo(t *testing.T) {
 			want: wire.TLVUserInfo{
 				TLVBlock: wire.TLVBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceUserInfoSignonTOD, uint32(1)),
-						wire.NewTLV(wire.OServiceUserInfoUserFlags, uint16(0x30)),
-						wire.NewTLV(wire.OServiceUserInfoStatus, uint32(0x0000)),
+						wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
+						wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x30)),
+						wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
 					},
 				},
 			},
@@ -139,9 +139,9 @@ func TestSession_TLVUserInfo(t *testing.T) {
 			want: wire.TLVUserInfo{
 				TLVBlock: wire.TLVBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceUserInfoSignonTOD, uint32(1)),
-						wire.NewTLV(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
-						wire.NewTLV(wire.OServiceUserInfoStatus, uint32(0x0100)),
+						wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
+						wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
+						wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0100)),
 					},
 				},
 			},
@@ -165,10 +165,10 @@ func TestSession_TLVUserInfo(t *testing.T) {
 			want: wire.TLVUserInfo{
 				TLVBlock: wire.TLVBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceUserInfoSignonTOD, uint32(0)),
-						wire.NewTLV(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
-						wire.NewTLV(wire.OServiceUserInfoStatus, uint32(0x0000)),
-						wire.NewTLV(wire.OServiceUserInfoIdleTime, uint16(6)),
+						wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(0)),
+						wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
+						wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
+						wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(6)),
 					},
 				},
 			},
@@ -185,9 +185,9 @@ func TestSession_TLVUserInfo(t *testing.T) {
 			want: wire.TLVUserInfo{
 				TLVBlock: wire.TLVBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceUserInfoSignonTOD, uint32(1)),
-						wire.NewTLV(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
-						wire.NewTLV(wire.OServiceUserInfoStatus, uint32(0x0000)),
+						wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
+						wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
+						wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
 					},
 				},
 			},
@@ -214,10 +214,10 @@ func TestSession_TLVUserInfo(t *testing.T) {
 			want: wire.TLVUserInfo{
 				TLVBlock: wire.TLVBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceUserInfoSignonTOD, uint32(1)),
-						wire.NewTLV(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
-						wire.NewTLV(wire.OServiceUserInfoStatus, uint32(0x0000)),
-						wire.NewTLV(wire.OServiceUserInfoOscarCaps, []byte{
+						wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
+						wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
+						wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
+						wire.NewTLVBE(wire.OServiceUserInfoOscarCaps, []byte{
 							// chat: "748F2420-6287-11D1-8222-444553540000"
 							0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
 							0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00,
@@ -240,9 +240,9 @@ func TestSession_TLVUserInfo(t *testing.T) {
 				WarningLevel: 0,
 				TLVBlock: wire.TLVBlock{
 					TLVList: wire.TLVList{
-						wire.NewTLV(wire.OServiceUserInfoSignonTOD, uint32(1)),
-						wire.NewTLV(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
-						wire.NewTLV(wire.OServiceUserInfoStatus, uint32(0x0000)),
+						wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
+						wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uint16(0x0010)),
+						wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
 					},
 				},
 			},

+ 73 - 58
state/user_store.go

@@ -103,11 +103,9 @@ func (f SQLiteUserStore) AllUsers() ([]User, error) {
 	return users, nil
 }
 
-// FindByUIN returns a user where the UIN matches the ident screen name.
+// FindByUIN returns a user with a matching UIN.
 func (f SQLiteUserStore) FindByUIN(UIN uint32) (User, error) {
-	users, err := getUsers(func() (string, []any) {
-		return `identScreenName = ?`, []any{strconv.Itoa(int(UIN))}
-	}, f.db)
+	users, err := f.queryUsers(`identScreenName = ?`, []any{strconv.Itoa(int(UIN))})
 	if err != nil {
 		return User{}, fmt.Errorf("FindByUIN: %w", err)
 	}
@@ -121,9 +119,7 @@ func (f SQLiteUserStore) FindByUIN(UIN uint32) (User, error) {
 
 // FindByEmail returns a user with a matching email address.
 func (f SQLiteUserStore) FindByEmail(email string) (User, error) {
-	users, err := getUsers(func() (string, []any) {
-		return `icq_basicInfo_emailAddress = ?`, []any{email}
-	}, f.db)
+	users, err := f.queryUsers(`icq_basicInfo_emailAddress = ?`, []any{email})
 	if err != nil {
 		return User{}, fmt.Errorf("FindByEmail: %w", err)
 	}
@@ -135,63 +131,89 @@ func (f SQLiteUserStore) FindByEmail(email string) (User, error) {
 	return users[0], nil
 }
 
-// FindByDetails returns a user with either a matching first name, last name, or nickname.
+// FindByDetails returns users with either a matching first name, last name,
+// and nickname. Empty values are not included in the search parameters.
 func (f SQLiteUserStore) FindByDetails(firstName, lastName, nickName string) ([]User, error) {
-	users, err := getUsers(func() (string, []any) {
-		var conds []string
-		var vals []any
-		if firstName != "" {
-			conds = append(conds, `LOWER(icq_basicInfo_firstName) = LOWER(?)`)
-			vals = append(vals, firstName)
-		}
-		if lastName != "" {
-			conds = append(conds, `LOWER(icq_basicInfo_lastName) = LOWER(?)`)
-			vals = append(vals, lastName)
-		}
-		if nickName != "" {
-			conds = append(conds, `LOWER(icq_basicInfo_nickName) = LOWER(?)`)
-			vals = append(vals, nickName)
-		}
-		return strings.Join(conds, " AND "), vals
-	}, f.db)
+	var args []any
+	var clauses []string
+
+	if firstName != "" {
+		args = append(args, firstName)
+		clauses = append(clauses, `LOWER(icq_basicInfo_firstName) = LOWER(?)`)
+	}
+
+	if lastName != "" {
+		args = append(args, lastName)
+		clauses = append(clauses, `LOWER(icq_basicInfo_lastName) = LOWER(?)`)
+	}
+
+	if nickName != "" {
+		args = append(args, nickName)
+		clauses = append(clauses, `LOWER(icq_basicInfo_nickName) = LOWER(?)`)
+	}
+
+	whereClause := strings.Join(clauses, " AND ")
+
+	users, err := f.queryUsers(whereClause, args)
 	if err != nil {
 		err = fmt.Errorf("FindByDetails: %w", err)
 	}
+
 	return users, nil
 }
 
-// FindByInterests returns a user who has at least one matching interest.
+// FindByInterests returns users who have at least one matching interest.
 func (f SQLiteUserStore) FindByInterests(code uint16, keywords []string) ([]User, error) {
-	users, err := getUsers(func() (string, []any) {
-		var conds []string
-		var vals []any
-
-		for i := 1; i <= 4; i++ {
-			var subConds []string
-			vals = append(vals, code)
-			for _, key := range keywords {
-				subConds = append(subConds, fmt.Sprintf("icq_interests_keyword%d LIKE ?", i))
-				vals = append(vals, "%"+key+"%")
-			}
-			conds = append(conds, fmt.Sprintf("(icq_interests_code%d = ? AND (%s))", i, strings.Join(subConds, " OR ")))
+	var args []any
+	var clauses []string
+
+	for i := 1; i <= 4; i++ {
+		var subClauses []string
+		args = append(args, code)
+		for _, key := range keywords {
+			subClauses = append(subClauses, fmt.Sprintf("icq_interests_keyword%d LIKE ?", i))
+			args = append(args, "%"+key+"%")
 		}
+		clauses = append(clauses, fmt.Sprintf("(icq_interests_code%d = ? AND (%s))", i, strings.Join(subClauses, " OR ")))
+	}
 
-		return strings.Join(conds, " OR "), vals
-	}, f.db)
+	cond := strings.Join(clauses, " OR ")
+
+	users, err := f.queryUsers(cond, args)
 	if err != nil {
 		err = fmt.Errorf("FindByInterests: %w", err)
 	}
+
+	return users, nil
+}
+
+// FindByKeyword returns users with matching interest keyword across all
+// interest categories.
+func (f SQLiteUserStore) FindByKeyword(keyword string) ([]User, error) {
+	var args []any
+	var clauses []string
+
+	for i := 1; i <= 4; i++ {
+		args = append(args, "%"+keyword+"%")
+		clauses = append(clauses, fmt.Sprintf("icq_interests_keyword%d LIKE ?", i))
+	}
+
+	whereClause := strings.Join(clauses, " OR ")
+
+	users, err := f.queryUsers(whereClause, args)
+	if err != nil {
+		err = fmt.Errorf("FindByKeyword: %w", err)
+	}
+
 	return users, nil
 }
 
 // User looks up a user by screen name. It populates the User record with
 // credentials that can be used to validate the user's password.
 func (f SQLiteUserStore) User(screenName IdentScreenName) (*User, error) {
-	users, err := getUsers(func() (string, []any) {
-		return `identScreenName = ?`, []any{screenName.String()}
-	}, f.db)
+	users, err := f.queryUsers(`identScreenName = ?`, []any{screenName.String()})
 	if err != nil {
-		return nil, fmt.Errorf("user: %w", err)
+		return nil, fmt.Errorf("User: %w", err)
 	}
 
 	if len(users) == 0 {
@@ -201,17 +223,10 @@ func (f SQLiteUserStore) User(screenName IdentScreenName) (*User, error) {
 	return &users[0], nil
 }
 
-type filterFN func() (string, []any)
-
-type queryer interface {
-	Query(query string, args ...any) (*sql.Rows, error)
-}
-
-// getUsers fetches users from the database by their screen name.
-func getUsers(filterFN filterFN, tx queryer) ([]User, error) {
-
-	cond, parms := filterFN()
-
+// queryUsers retrieves a list of users from the database based on the
+// specified WHERE clause and query parameters. Returns a slice of User objects
+// or an error if the query fails.
+func (f SQLiteUserStore) queryUsers(whereClause string, queryParams []any) ([]User, error) {
 	q := `
 		SELECT
 			identScreenName,
@@ -282,8 +297,8 @@ func getUsers(filterFN filterFN, tx queryer) ([]User, error) {
 		FROM users
 		WHERE %s
 	`
-	q = fmt.Sprintf(q, cond)
-	rows, err := tx.Query(q, parms...)
+	q = fmt.Sprintf(q, whereClause)
+	rows, err := f.db.Query(q, queryParams...)
 	if err != nil {
 		return nil, err
 	}
@@ -1306,7 +1321,7 @@ func (f SQLiteUserStore) BuddyIconRefByName(screenName IdentScreenName) (*wire.B
 	if err := wire.UnmarshalBE(&item.TLVLBlock, bytes.NewBuffer(attrs)); err != nil {
 		return nil, err
 	}
-	b, hasBuf := item.Slice(wire.FeedbagAttributesBartInfo)
+	b, hasBuf := item.Bytes(wire.FeedbagAttributesBartInfo)
 	if !hasBuf {
 		return nil, errors.New("unable to extract icon payload")
 	}

+ 79 - 3
state/user_store_test.go

@@ -72,7 +72,7 @@ func TestFeedbagDelete(t *testing.T) {
 			Name:    "spimmer1234",
 			TLVLBlock: wire.TLVLBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(0x01, uint16(1000)),
+					wire.NewTLVBE(0x01, uint16(1000)),
 				},
 			},
 		},
@@ -1443,6 +1443,82 @@ func TestSQLiteUserStore_FindByInterests(t *testing.T) {
 	})
 }
 
+func TestSQLiteUserStore_FindByKeyword(t *testing.T) {
+	// Cleanup after test
+	defer func() {
+		assert.NoError(t, os.Remove(testFile))
+	}()
+
+	// Initialize the SQLiteUserStore with a test database file
+	f, err := NewSQLiteUserStore(testFile)
+	assert.NoError(t, err)
+
+	// Create and set up test users with different interests
+	user1 := User{
+		IdentScreenName: NewIdentScreenName("user1"),
+	}
+	err = f.InsertUser(user1)
+	assert.NoError(t, err)
+	interests1 := ICQInterests{
+		Keyword1: "Coding",
+		Keyword2: "Music",
+	}
+	err = f.SetInterests(user1.IdentScreenName, interests1)
+	assert.NoError(t, err)
+
+	user2 := User{
+		IdentScreenName: NewIdentScreenName("user2"),
+	}
+	err = f.InsertUser(user2)
+	assert.NoError(t, err)
+	interests2 := ICQInterests{
+		Keyword1: "Coding",
+		Keyword3: "Gaming",
+	}
+	err = f.SetInterests(user2.IdentScreenName, interests2)
+	assert.NoError(t, err)
+
+	user3 := User{
+		IdentScreenName: NewIdentScreenName("user3"),
+	}
+	err = f.InsertUser(user3)
+	assert.NoError(t, err)
+	interests3 := ICQInterests{
+		Keyword3: "Music",
+		Keyword4: "Travel",
+	}
+	err = f.SetInterests(user3.IdentScreenName, interests3)
+	assert.NoError(t, err)
+
+	// Helper function to check if a user with a specific IdentScreenName exists in the results
+	containsUserWithScreenName := func(users []User, screenName IdentScreenName) bool {
+		for _, user := range users {
+			if user.IdentScreenName == screenName {
+				return true
+			}
+		}
+		return false
+	}
+
+	t.Run("Find Users by Keyword", func(t *testing.T) {
+		// Search for users interested in "Music"
+		users, err := f.FindByKeyword("Music")
+		assert.NoError(t, err)
+		assert.Len(t, users, 2)
+
+		// Check that the correct users are returned by IdentScreenName
+		assert.True(t, containsUserWithScreenName(users, user1.IdentScreenName))
+		assert.True(t, containsUserWithScreenName(users, user3.IdentScreenName))
+	})
+
+	t.Run("No Users Found", func(t *testing.T) {
+		// Search for users interested in a keyword that no user has
+		users, err := f.FindByKeyword("Knitting")
+		assert.NoError(t, err)
+		assert.Empty(t, users)
+	})
+}
+
 func TestSQLiteUserStore_FindByDetails(t *testing.T) {
 	// Cleanup after test
 	defer func() {
@@ -1813,7 +1889,7 @@ func TestSQLiteUserStore_BuddyIconRefByNameExistingRef(t *testing.T) {
 			ClassID: wire.FeedbagClassIdBart,
 			TLVLBlock: wire.TLVLBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
+					wire.NewTLVBE(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
 						Hash: testHash,
 					}),
 				},
@@ -1850,7 +1926,7 @@ func TestSQLiteUserStore_BuddyIconRefByNameMissingRef(t *testing.T) {
 			ClassID: wire.FeedbagClassIdBart,
 			TLVLBlock: wire.TLVLBlock{
 				TLVList: wire.TLVList{
-					wire.NewTLV(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
+					wire.NewTLVBE(wire.FeedbagAttributesBartInfo, wire.BARTInfo{
 						Hash: testHash,
 					}),
 				},

+ 6 - 6
wire/decode_test.go

@@ -287,8 +287,8 @@ func TestUnmarshal(t *testing.T) {
 				Val []TLV
 			}{
 				Val: []TLV{
-					NewTLV(10, uint16(1234)),
-					NewTLV(20, uint16(1234)),
+					NewTLVBE(10, uint16(1234)),
+					NewTLVBE(20, uint16(1234)),
 				},
 			},
 			given: []byte{0x0, 0xa, 0x0, 0x2, 0x4, 0xd2, 0x0, 0x14, 0x0, 0x2, 0x4, 0xd2},
@@ -310,8 +310,8 @@ func TestUnmarshal(t *testing.T) {
 				Val []TLV `oscar:"count_prefix=uint8"`
 			}{
 				Val: []TLV{
-					NewTLV(10, uint16(1234)),
-					NewTLV(20, uint16(1234)),
+					NewTLVBE(10, uint16(1234)),
+					NewTLVBE(20, uint16(1234)),
 				},
 			},
 			given: append(
@@ -347,8 +347,8 @@ func TestUnmarshal(t *testing.T) {
 				Val []TLV `oscar:"count_prefix=uint16"`
 			}{
 				Val: []TLV{
-					NewTLV(10, uint16(1234)),
-					NewTLV(20, uint16(1234)),
+					NewTLVBE(10, uint16(1234)),
+					NewTLVBE(20, uint16(1234)),
 				},
 			},
 			given: append(

+ 6 - 6
wire/encode_test.go

@@ -268,8 +268,8 @@ func TestMarshal(t *testing.T) {
 				Val []TLV `oscar:"count_prefix=uint8"`
 			}{
 				Val: []TLV{
-					NewTLV(10, uint16(1234)),
-					NewTLV(20, uint16(1234)),
+					NewTLVBE(10, uint16(1234)),
+					NewTLVBE(20, uint16(1234)),
 				},
 			},
 			want: append(
@@ -283,7 +283,7 @@ func TestMarshal(t *testing.T) {
 				Val []TLV `oscar:"count_prefix=uint8"`
 			}{
 				Val: []TLV{
-					NewTLV(10, uint16(1234)),
+					NewTLVBE(10, uint16(1234)),
 				},
 			},
 			wantErr: io.EOF,
@@ -295,8 +295,8 @@ func TestMarshal(t *testing.T) {
 				Val []TLV `oscar:"count_prefix=uint16"`
 			}{
 				Val: []TLV{
-					NewTLV(10, uint16(1234)),
-					NewTLV(20, uint16(1234)),
+					NewTLVBE(10, uint16(1234)),
+					NewTLVBE(20, uint16(1234)),
 				},
 			},
 			want: append(
@@ -310,7 +310,7 @@ func TestMarshal(t *testing.T) {
 				Val []TLV `oscar:"count_prefix=uint16"`
 			}{
 				Val: []TLV{
-					NewTLV(10, uint16(1234)),
+					NewTLVBE(10, uint16(1234)),
 				},
 			},
 			wantErr: io.EOF,

+ 133 - 28
wire/snacs.go

@@ -552,6 +552,25 @@ const (
 	ICBMChannelMIME       uint16 = 0x03
 	ICBMChannelICQ        uint16 = 0x04
 	ICBMChannelCoBrowser  uint16 = 0x05
+
+	ICBMMsgTypePlain    uint8 = 0x01 // Plain text (simple) message
+	ICBMMsgTypeChat     uint8 = 0x02 // Chat request message
+	ICBMMsgTypeFileReq  uint8 = 0x03 // File request / file ok message
+	ICBMMsgTypeUrl      uint8 = 0x04 // URL message (0xFE formatted)
+	ICBMMsgTypeAuthReq  uint8 = 0x06 // Authorization request message (0xFE formatted)
+	ICBMMsgTypeAuthDeny uint8 = 0x07 // Authorization denied message (0xFE formatted)
+	ICBMMsgTypeAuthOK   uint8 = 0x08 // Authorization given message (empty)
+	ICBMMsgTypeServer   uint8 = 0x09 // Message from OSCAR server (0xFE formatted)
+	ICBMMsgTypeAdded    uint8 = 0x0C // "You-were-added" message (0xFE formatted)
+	ICBMMsgTypeWWP      uint8 = 0x0D // Web pager message (0xFE formatted)
+	ICBMMsgTypeEExpress uint8 = 0x0E // Email express message (0xFE formatted)
+	ICBMMsgTypeContacts uint8 = 0x13 // Contact list message
+	ICBMMsgTypePlugin   uint8 = 0x1A // Plugin message described by text string
+	ICBMMsgTypeAutoAway uint8 = 0xE8 // Auto away message
+	ICBMMsgTypeAutoBusy uint8 = 0xE9 // Auto occupied message
+	ICBMMsgTypeAutoNA   uint8 = 0xEA // Auto not available message
+	ICBMMsgTypeAutoDND  uint8 = 0xEB // Auto do not disturb message
+	ICBMMsgTypeAutoFFC  uint8 = 0xEC // Auto free for chat message
 )
 
 // ICBMCh1Fragment represents an ICBM channel 1 (instant message) message
@@ -1266,6 +1285,24 @@ type SNAC_0x13_0x11_FeedbagStartCluster struct {
 	TLVRestBlock
 }
 
+type SNAC_0x13_0x18_FeedbagRequestAuthorizationToHost struct {
+	ScreenName string `oscar:"len_prefix=uint8"`
+	Reason     string `oscar:"len_prefix=uint16"`
+	Unknown    uint16
+}
+
+type SNAC_0x13_0x1A_FeedbagRespondAuthorizeToHost struct {
+	ScreenName string `oscar:"len_prefix=uint8"`
+	Accepted   uint8
+	Reason     string `oscar:"len_prefix=uint16"`
+}
+
+type SNAC_0x13_0x1B_FeedbagRespondAuthorizeToClient struct {
+	ScreenName string `oscar:"len_prefix=uint8"`
+	Accepted   uint8
+	Reason     string `oscar:"len_prefix=uint16"`
+}
+
 //
 // 0x15: ICQ
 //
@@ -1275,7 +1312,51 @@ const (
 	ICQDBQuery uint16 = 0x0002
 	ICQDBReply uint16 = 0x0003
 
-	ICQTLVTagsMetadata uint16 = 0x01
+	ICQTLVTagsMetadata                  uint16 = 0x0001
+	ICQTLVTagsUIN                       uint16 = 0x0136 // User UIN (search)
+	ICQTLVTagsFirstName                 uint16 = 0x0140 // User first name
+	ICQTLVTagsLastName                  uint16 = 0x014A // User last name
+	ICQTLVTagsNickname                  uint16 = 0x0154 // User nickname
+	ICQTLVTagsEmail                     uint16 = 0x015E // User email
+	ICQTLVTagsAgeRangeSearch            uint16 = 0x0168 // Age range to search (search)
+	ICQTLVTagsAge                       uint16 = 0x0172 // User age
+	ICQTLVTagsGender                    uint16 = 0x017C // User gender
+	ICQTLVTagsSpokenLanguage            uint16 = 0x0186 // User spoken language
+	ICQTLVTagsHomeCityName              uint16 = 0x0190 // User home city name
+	ICQTLVTagsHomeStateAbbr             uint16 = 0x019A // User home state abbreviation
+	ICQTLVTagsHomeCountryCode           uint16 = 0x01A4 // User home country code
+	ICQTLVTagsWorkCompanyName           uint16 = 0x01AE // User work company name
+	ICQTLVTagsWorkDepartmentName        uint16 = 0x01B8 // User work department name
+	ICQTLVTagsWorkPositionTitle         uint16 = 0x01C2 // User work position (title)
+	ICQTLVTagsWorkOccupationCode        uint16 = 0x01CC // User work occupation code
+	ICQTLVTagsAffiliationsNode          uint16 = 0x01D6 // User affiliations node
+	ICQTLVTagsInterestsNode             uint16 = 0x01EA // User interests node
+	ICQTLVTagsPastInfoNode              uint16 = 0x01FE // User past info node
+	ICQTLVTagsHomepageCategoryKeywords  uint16 = 0x0212 // User homepage category/keywords
+	ICQTLVTagsHomepageURL               uint16 = 0x0213 // User homepage URL
+	ICQTLVTagsWhitepagesSearchKeywords  uint16 = 0x0226 // Whitepages search keywords string (search)
+	ICQTLVTagsSearchOnlineUsersFlag     uint16 = 0x0230 // Search only online users flag (search)
+	ICQTLVTagsBirthdayInfo              uint16 = 0x023A // User birthday info (year, month, day)
+	ICQTLVTagsNotesText                 uint16 = 0x0258 // User notes (about) text
+	ICQTLVTagsHomeStreetAddress         uint16 = 0x0262 // User home street address
+	ICQTLVTagsHomeZipCode               uint16 = 0x026C // User home zip code
+	ICQTLVTagsHomePhoneNumber           uint16 = 0x0276 // User home phone number
+	ICQTLVTagsHomeFaxNumber             uint16 = 0x0280 // User home fax number
+	ICQTLVTagsHomeCellularPhoneNumber   uint16 = 0x028A // User home cellular phone number
+	ICQTLVTagsWorkStreetAddress         uint16 = 0x0294 // User work street address
+	ICQTLVTagsWorkCityName              uint16 = 0x029E // User work city name
+	ICQTLVTagsWorkStateName             uint16 = 0x02A8 // User work state name
+	ICQTLVTagsWorkCountryCode           uint16 = 0x02B2 // User work country code
+	ICQTLVTagsWorkZipCode               uint16 = 0x02BC // User work zip code
+	ICQTLVTagsWorkPhoneNumber           uint16 = 0x02C6 // User work phone number
+	ICQTLVTagsWorkFaxNumber             uint16 = 0x02D0 // User work fax number
+	ICQTLVTagsWorkWebpageURL            uint16 = 0x02DA // User work webpage URL
+	ICQTLVTagsShowWebStatusPermissions  uint16 = 0x02F8 // User 'show web status' permissions
+	ICQTLVTagsAuthorizationPermissions  uint16 = 0x030C // User authorization permissions
+	ICQTLVTagsGMTOffset                 uint16 = 0x0316 // User GMT offset
+	ICQTLVTagsOriginallyFromCity        uint16 = 0x0320 // User originally from city
+	ICQTLVTagsOriginallyFromState       uint16 = 0x032A // User originally from state
+	ICQTLVTagsOriginallyFromCountryCode uint16 = 0x0334 // User originally from country (code)
 
 	ICQStatusCodeOK   uint8 = 0x0A
 	ICQStatusCodeFail uint8 = 0x32
@@ -1295,33 +1376,36 @@ const (
 )
 
 const (
-	ICQDBQueryMetaReqSetBasicInfo     uint16 = 0x03EA
-	ICQDBQueryMetaReqSetWorkInfo      uint16 = 0x03F3
-	ICQDBQueryMetaReqSetMoreInfo      uint16 = 0x03FD
-	ICQDBQueryMetaReqSetNotes         uint16 = 0x0406
-	ICQDBQueryMetaReqSetEmails        uint16 = 0x040B
-	ICQDBQueryMetaReqSetInterests     uint16 = 0x0410
-	ICQDBQueryMetaReqSetAffiliations  uint16 = 0x041A
-	ICQDBQueryMetaReqSetPermissions   uint16 = 0x0424
-	ICQDBQueryMetaReqShortInfo        uint16 = 0x04BA
-	ICQDBQueryMetaReqFullInfo         uint16 = 0x04B2
-	ICQDBQueryMetaReqFullInfo2        uint16 = 0x04D0
-	ICQDBQueryMetaReqSearchByDetails  uint16 = 0x0515
-	ICQDBQueryMetaReqSearchByUIN      uint16 = 0x051F
-	ICQDBQueryMetaReqSearchByEmail    uint16 = 0x0529
-	ICQDBQueryMetaReqSearchWhitePages uint16 = 0x0533
-	ICQDBQueryMetaReqXMLReq           uint16 = 0x0898
-	ICQDBQueryMetaReqStat0a8c         uint16 = 0x0A8C
-	ICQDBQueryMetaReqStat0a96         uint16 = 0x0A96
-	ICQDBQueryMetaReqStat0aaa         uint16 = 0x0AAA
-	ICQDBQueryMetaReqStat0ab4         uint16 = 0x0AB4
-	ICQDBQueryMetaReqStat0ab9         uint16 = 0x0AB9
-	ICQDBQueryMetaReqStat0abe         uint16 = 0x0ABE
-	ICQDBQueryMetaReqStat0ac8         uint16 = 0x0AC8
-	ICQDBQueryMetaReqStat0acd         uint16 = 0x0ACD
-	ICQDBQueryMetaReqStat0ad2         uint16 = 0x0AD2
-	ICQDBQueryMetaReqStat0ad7         uint16 = 0x0AD7
-	ICQDBQueryMetaReqStat0758         uint16 = 0x0758
+	ICQDBQueryMetaReqSetBasicInfo      uint16 = 0x03EA
+	ICQDBQueryMetaReqSetWorkInfo       uint16 = 0x03F3
+	ICQDBQueryMetaReqSetMoreInfo       uint16 = 0x03FD
+	ICQDBQueryMetaReqSetNotes          uint16 = 0x0406
+	ICQDBQueryMetaReqSetEmails         uint16 = 0x040B
+	ICQDBQueryMetaReqSetInterests      uint16 = 0x0410
+	ICQDBQueryMetaReqSetAffiliations   uint16 = 0x041A
+	ICQDBQueryMetaReqSetPermissions    uint16 = 0x0424
+	ICQDBQueryMetaReqShortInfo         uint16 = 0x04BA
+	ICQDBQueryMetaReqFullInfo          uint16 = 0x04B2
+	ICQDBQueryMetaReqFullInfo2         uint16 = 0x04D0
+	ICQDBQueryMetaReqSearchByDetails   uint16 = 0x0515
+	ICQDBQueryMetaReqSearchByUIN       uint16 = 0x051F
+	ICQDBQueryMetaReqSearchByEmail     uint16 = 0x0529
+	ICQDBQueryMetaReqSearchWhitePages  uint16 = 0x0533
+	ICQDBQueryMetaReqSearchWhitePages2 uint16 = 0x055F
+	ICQDBQueryMetaReqSearchByUIN2      uint16 = 0x0569
+	ICQDBQueryMetaReqSearchByEmail3    uint16 = 0x0573
+	ICQDBQueryMetaReqStat0758          uint16 = 0x0758
+	ICQDBQueryMetaReqXMLReq            uint16 = 0x0898
+	ICQDBQueryMetaReqStat0a8c          uint16 = 0x0A8C
+	ICQDBQueryMetaReqStat0a96          uint16 = 0x0A96
+	ICQDBQueryMetaReqStat0aaa          uint16 = 0x0AAA
+	ICQDBQueryMetaReqStat0ab4          uint16 = 0x0AB4
+	ICQDBQueryMetaReqStat0ab9          uint16 = 0x0AB9
+	ICQDBQueryMetaReqStat0abe          uint16 = 0x0ABE
+	ICQDBQueryMetaReqStat0ac8          uint16 = 0x0AC8
+	ICQDBQueryMetaReqStat0acd          uint16 = 0x0ACD
+	ICQDBQueryMetaReqStat0ad2          uint16 = 0x0AD2
+	ICQDBQueryMetaReqStat0ad7          uint16 = 0x0AD7
 
 	ICQDBQueryMetaReplySetBasicInfo    uint16 = 0x0064
 	ICQDBQueryMetaReplySetWorkInfo     uint16 = 0x006E
@@ -1368,16 +1452,33 @@ type ICQ_0x07D0_0x051F_DBQueryMetaReqSearchByUIN struct {
 	UIN uint32
 }
 
+type ICQ_0x07D0_0x0569_DBQueryMetaReqSearchByUIN2 struct {
+	TLVRestBlock
+}
+
 type ICQ_0x07D0_0x0529_DBQueryMetaReqSearchByEmail struct {
 	Email string `oscar:"len_prefix=uint16,nullterm"`
 }
 
+type ICQ_0x07D0_0x0573_DBQueryMetaReqSearchByEmail3 struct {
+	TLVRestBlock
+}
+
 type ICQ_0x07D0_0x0515_DBQueryMetaReqSearchByDetails struct {
 	FirstName string `oscar:"len_prefix=uint16,nullterm"`
 	LastName  string `oscar:"len_prefix=uint16,nullterm"`
 	NickName  string `oscar:"len_prefix=uint16,nullterm"`
 }
 
+type ICQInterests struct {
+	Code    uint16
+	Keyword string `oscar:"len_prefix=uint16,nullterm"`
+}
+
+type ICQEmail struct {
+	Email string `oscar:"len_prefix=uint16,nullterm"`
+}
+
 type ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages struct {
 	FirstName           string `oscar:"len_prefix=uint16,nullterm"`
 	LastName            string `oscar:"len_prefix=uint16,nullterm"`
@@ -1405,6 +1506,10 @@ type ICQ_0x07D0_0x0533_DBQueryMetaReqSearchWhitePages struct {
 	SearchScope         uint8
 }
 
+type ICQ_0x07D0_0x055F_DBQueryMetaReqSearchWhitePages2 struct {
+	TLVRestBlock
+}
+
 type ICQ_0x07D0_0x03FD_DBQueryMetaReqSetMoreInfo struct {
 	Age          uint8 // not used because age is calculated from birthdate
 	Gender       uint16

+ 116 - 19
wire/tlv.go

@@ -14,8 +14,17 @@ type TLV struct {
 	Value []byte `oscar:"len_prefix=uint16"`
 }
 
-// NewTLV creates a new instance of TLV.
-func NewTLV(tag uint16, val any) TLV {
+// NewTLVBE creates a new TLV. Values are marshalled in big-endian order.
+func NewTLVBE(tag uint16, val any) TLV {
+	return newTLV(tag, val, binary.BigEndian)
+}
+
+// NewTLVLE creates a new TLV. Values are marshalled in little-endian order.
+func NewTLVLE(tag uint16, val any) TLV {
+	return newTLV(tag, val, binary.LittleEndian)
+}
+
+func newTLV(tag uint16, val any, order binary.ByteOrder) TLV {
 	t := TLV{
 		Tag: tag,
 	}
@@ -23,8 +32,15 @@ func NewTLV(tag uint16, val any) TLV {
 		t.Value = val.([]byte)
 	} else {
 		buf := &bytes.Buffer{}
-		if err := MarshalBE(val, buf); err != nil {
-			panic(fmt.Sprintf("unable to create TLV: %s", err.Error()))
+		switch order {
+		case binary.BigEndian:
+			if err := MarshalBE(val, buf); err != nil {
+				panic(fmt.Sprintf("unable to create TLV: %s", err.Error()))
+			}
+		case binary.LittleEndian:
+			if err := MarshalLE(val, buf); err != nil {
+				panic(fmt.Sprintf("unable to create TLV: %s", err.Error()))
+			}
 		}
 		t.Value = buf.Bytes()
 	}
@@ -67,8 +83,12 @@ func (s *TLVList) AppendList(tlvs []TLV) {
 	*s = append(*s, tlvs...)
 }
 
-// String retrieves the string value of a TLV with a tag value from the TLV
-// list. It returns false if the tag does not exist in the list.
+// String retrieves the string value associated with the specified tag from the
+// TLVList.
+//
+// If the specified tag is found, the function returns the associated string
+// value and true. If the tag is not found, the function returns an empty
+// string and false.
 func (s *TLVList) String(tag uint16) (string, bool) {
 	for _, tlv := range *s {
 		if tag == tlv.Tag {
@@ -78,9 +98,52 @@ func (s *TLVList) String(tag uint16) (string, bool) {
 	return "", false
 }
 
-// Slice retrieves the slice value of a TLV with a tag value from the TLV
-// list. It returns false if the tag does not exist in the list.
-func (s *TLVList) Slice(tag uint16) ([]byte, bool) {
+// ICQString retrieves the ICQ string value associated with the specified tag
+// from the TLVList.
+//
+// An ICQ string is a string that is prefixed with its length and ends with a
+// null terminator.
+//
+// If the specified tag is found, the function returns the extracted string
+// value and true. If the tag is not found or the string is malformed, the
+// function returns an empty string and false.
+func (s *TLVList) ICQString(tag uint16) (string, bool) {
+	// Find the TLV entry with the specified tag
+	for _, tlv := range *s {
+		if tag != tlv.Tag {
+			continue
+		}
+
+		// Ensure the value is long enough to contain a valid length prefix and value
+		if len(tlv.Value) < 3 {
+			break
+		}
+
+		// Extract the length prefix (first 2 bytes) as a uint16
+		expectedLength := binary.LittleEndian.Uint16(tlv.Value[0:2])
+
+		// Extract the actual string value, excluding the length prefix
+		value := tlv.Value[2:]
+
+		// Check if the length matches the value length (including the null terminator)
+		if int(expectedLength) != len(value) {
+			break
+		}
+
+		// Remove the null terminator
+		return string(value[:len(value)-1]), true
+	}
+
+	// Tag not found
+	return "", false
+}
+
+// Bytes retrieves the byte payload associated with the specified tag from the
+// TLVList.
+//
+// If the specified tag is found, the function returns the associated byte
+// slice and true. If the tag is not found, the function returns nil and false.
+func (s *TLVList) Bytes(tag uint16) ([]byte, bool) {
 	for _, tlv := range *s {
 		if tag == tlv.Tag {
 			return tlv.Value, true
@@ -89,25 +152,59 @@ func (s *TLVList) Slice(tag uint16) ([]byte, bool) {
 	return nil, false
 }
 
-// Uint16 retrieves the uint16 value of a TLV with a tag value from the TLV
-// list. It returns false if the tag does not exist in the list. It may panic
-// if the TLV value is not uint16.
-func (s *TLVList) Uint16(tag uint16) (uint16, bool) {
+// Uint16BE retrieves a 16-bit unsigned integer value from the TLVList
+// associated with the specified tag, interpreting the bytes in big-endian
+// format.
+//
+// If the specified tag is found, the function returns the associated value
+// as a uint16 and true. If the tag is not found, the function returns 0 and
+// false.
+func (s *TLVList) Uint16BE(tag uint16) (uint16, bool) {
+	return s.uint16(tag, binary.BigEndian)
+}
+
+// Uint16LE retrieves a 16-bit unsigned integer value from the TLVList
+// associated with the specified tag, interpreting the bytes in little-endian
+// format.
+//
+// If the specified tag is found, the function returns the associated value
+// as a uint16 and true. If the tag is not found, the function returns 0 and
+// false.
+func (s *TLVList) Uint16LE(tag uint16) (uint16, bool) {
+	return s.uint16(tag, binary.LittleEndian)
+}
+
+func (s *TLVList) uint16(tag uint16, order binary.ByteOrder) (uint16, bool) {
 	for _, tlv := range *s {
 		if tag == tlv.Tag {
-			return binary.BigEndian.Uint16(tlv.Value), true
+			return order.Uint16(tlv.Value), true
 		}
 	}
 	return 0, false
 }
 
-// Uint32 retrieves the uint32 value of a TLV with a tag value from the TLV
-// list. It returns false if the tag does not exist in the list. It may panic
-// if the TLV value is not uint32.
-func (s *TLVList) Uint32(tag uint16) (uint32, bool) {
+// Uint32BE retrieves a 32-bit unsigned integer value from the TLVList
+// associated with the specified tag, interpreting the bytes in big-endian format.
+//
+// If the specified tag is found, the function returns the associated value
+// as a uint32 and true. If the tag is not found, the function returns 0 and false.
+func (s *TLVList) Uint32BE(tag uint16) (uint32, bool) {
+	return s.uint32(tag, binary.BigEndian)
+}
+
+// Uint32LE retrieves a 32-bit unsigned integer value from the TLVList
+// associated with the specified tag, interpreting the bytes in little-endian format.
+//
+// If the specified tag is found, the function returns the associated value
+// as a uint32 and true. If the tag is not found, the function returns 0 and false.
+func (s *TLVList) Uint32LE(tag uint16) (uint32, bool) {
+	return s.uint32(tag, binary.LittleEndian)
+}
+
+func (s *TLVList) uint32(tag uint16, order binary.ByteOrder) (uint32, bool) {
 	for _, tlv := range *s {
 		if tag == tlv.Tag {
-			return binary.BigEndian.Uint32(tlv.Value), true
+			return order.Uint32(tlv.Value), true
 		}
 	}
 	return 0, false

+ 182 - 51
wire/tlv_test.go

@@ -23,9 +23,9 @@ func TestTLVList_Append(t *testing.T) {
 	}
 
 	have := TLVList{}
-	have.Append(NewTLV(0, []byte(`0`)))
-	have.Append(NewTLV(1, []byte(`1`)))
-	have.Append(NewTLV(2, []byte(`2`)))
+	have.Append(NewTLVBE(0, []byte(`0`)))
+	have.Append(NewTLVBE(1, []byte(`1`)))
+	have.Append(NewTLVBE(2, []byte(`2`)))
 
 	assert.Equal(t, want, have)
 }
@@ -48,18 +48,15 @@ func TestTLVList_AppendList(t *testing.T) {
 
 	have := TLVList{}
 	have.AppendList([]TLV{
-		NewTLV(0, []byte(`0`)),
-		NewTLV(1, []byte(`1`)),
-		NewTLV(2, []byte(`2`)),
+		NewTLVBE(0, []byte(`0`)),
+		NewTLVBE(1, []byte(`1`)),
+		NewTLVBE(2, []byte(`2`)),
 	})
 
 	assert.Equal(t, want, have)
 }
 
 func TestTLVList_Getters(t *testing.T) {
-	type args struct {
-		tType uint16
-	}
 	tests := []struct {
 		name   string
 		given  []TLV
@@ -70,53 +67,105 @@ func TestTLVList_Getters(t *testing.T) {
 		panic  bool
 	}{
 		{
-			name: "given a TLV of uint32, expect found value",
+			name: "given a TLV of big-endian uint32, expect found value",
+			given: []TLV{
+				NewTLVBE(0, uint32(12)),
+				NewTLVBE(1, uint32(34)),
+				NewTLVBE(2, uint32(56)),
+			},
+			lookup: func(l TLVList) (any, bool) {
+				return l.Uint32BE(1)
+			},
+			expect: uint32(34),
+			found:  true,
+		},
+		{
+			name: "given a TLV of big-endian uint32, expect not found value",
+			given: []TLV{
+				NewTLVBE(0, uint32(12)),
+				NewTLVBE(1, uint32(34)),
+				NewTLVBE(2, uint32(56)),
+			},
+			lookup: func(l TLVList) (any, bool) {
+				return l.Uint32BE(3)
+			},
+			expect: uint32(0),
+			found:  false,
+		},
+		{
+			name: "given a TLV of big-endian uint16, expect found value",
+			given: []TLV{
+				NewTLVBE(0, uint16(12)),
+				NewTLVBE(1, uint16(34)),
+				NewTLVBE(2, uint16(56)),
+			},
+			lookup: func(l TLVList) (any, bool) {
+				return l.Uint16BE(1)
+			},
+			expect: uint16(34),
+			found:  true,
+		},
+		{
+			name: "given a TLV of big-endian uint16, expect not found value",
+			given: []TLV{
+				NewTLVBE(0, uint16(12)),
+				NewTLVBE(1, uint16(34)),
+				NewTLVBE(2, uint16(56)),
+			},
+			lookup: func(l TLVList) (any, bool) {
+				return l.Uint16BE(3)
+			},
+			expect: uint16(0),
+			found:  false,
+		},
+		{
+			name: "given a TLV of little-endian uint32, expect found value",
 			given: []TLV{
-				NewTLV(0, uint32(12)),
-				NewTLV(1, uint32(34)),
-				NewTLV(2, uint32(56)),
+				NewTLVLE(0, uint32(12)),
+				NewTLVLE(1, uint32(34)),
+				NewTLVLE(2, uint32(56)),
 			},
 			lookup: func(l TLVList) (any, bool) {
-				return l.Uint32(1)
+				return l.Uint32LE(1)
 			},
 			expect: uint32(34),
 			found:  true,
 		},
 		{
-			name: "given a TLV of uint32, expect not found value",
+			name: "given a TLV of little-endian uint32, expect not found value",
 			given: []TLV{
-				NewTLV(0, uint32(12)),
-				NewTLV(1, uint32(34)),
-				NewTLV(2, uint32(56)),
+				NewTLVLE(0, uint32(12)),
+				NewTLVLE(1, uint32(34)),
+				NewTLVLE(2, uint32(56)),
 			},
 			lookup: func(l TLVList) (any, bool) {
-				return l.Uint32(3)
+				return l.Uint32LE(3)
 			},
 			expect: uint32(0),
 			found:  false,
 		},
 		{
-			name: "given a TLV of uint16, expect found value",
+			name: "given a TLV of little-endian uint16, expect found value",
 			given: []TLV{
-				NewTLV(0, uint16(12)),
-				NewTLV(1, uint16(34)),
-				NewTLV(2, uint16(56)),
+				NewTLVLE(0, uint16(12)),
+				NewTLVLE(1, uint16(34)),
+				NewTLVLE(2, uint16(56)),
 			},
 			lookup: func(l TLVList) (any, bool) {
-				return l.Uint16(1)
+				return l.Uint16LE(1)
 			},
 			expect: uint16(34),
 			found:  true,
 		},
 		{
-			name: "given a TLV of uint16, expect not found value",
+			name: "given a TLV of little-endian uint16, expect not found value",
 			given: []TLV{
-				NewTLV(0, uint16(12)),
-				NewTLV(1, uint16(34)),
-				NewTLV(2, uint16(56)),
+				NewTLVLE(0, uint16(12)),
+				NewTLVLE(1, uint16(34)),
+				NewTLVLE(2, uint16(56)),
 			},
 			lookup: func(l TLVList) (any, bool) {
-				return l.Uint16(3)
+				return l.Uint16LE(3)
 			},
 			expect: uint16(0),
 			found:  false,
@@ -124,9 +173,9 @@ func TestTLVList_Getters(t *testing.T) {
 		{
 			name: "given a TLV of string, expect found value",
 			given: []TLV{
-				NewTLV(0, "12"),
-				NewTLV(1, "34"),
-				NewTLV(2, "56"),
+				NewTLVBE(0, "12"),
+				NewTLVBE(1, "34"),
+				NewTLVBE(2, "56"),
 			},
 			lookup: func(l TLVList) (any, bool) {
 				return l.String(1)
@@ -137,9 +186,9 @@ func TestTLVList_Getters(t *testing.T) {
 		{
 			name: "given a TLV of string, expect not found value",
 			given: []TLV{
-				NewTLV(0, "12"),
-				NewTLV(1, "34"),
-				NewTLV(2, "56"),
+				NewTLVBE(0, "12"),
+				NewTLVBE(1, "34"),
+				NewTLVBE(2, "56"),
 			},
 			lookup: func(l TLVList) (any, bool) {
 				return l.String(3)
@@ -150,12 +199,12 @@ func TestTLVList_Getters(t *testing.T) {
 		{
 			name: "given a TLV of slice, expect found value",
 			given: []TLV{
-				NewTLV(0, []byte(`12`)),
-				NewTLV(1, []byte(`34`)),
-				NewTLV(2, []byte(`56`)),
+				NewTLVBE(0, []byte(`12`)),
+				NewTLVBE(1, []byte(`34`)),
+				NewTLVBE(2, []byte(`56`)),
 			},
 			lookup: func(l TLVList) (any, bool) {
-				return l.Slice(1)
+				return l.Bytes(1)
 			},
 			expect: []byte(`34`),
 			found:  true,
@@ -163,25 +212,37 @@ func TestTLVList_Getters(t *testing.T) {
 		{
 			name: "given a TLV of string, expect not found value",
 			given: []TLV{
-				NewTLV(0, []byte(`12`)),
-				NewTLV(1, []byte(`34`)),
-				NewTLV(2, []byte(`56`)),
+				NewTLVBE(0, []byte(`12`)),
+				NewTLVBE(1, []byte(`34`)),
+				NewTLVBE(2, []byte(`56`)),
 			},
 			lookup: func(l TLVList) (any, bool) {
-				return l.Slice(3)
+				return l.Bytes(3)
 			},
 			expect: []byte(nil),
 			found:  false,
 		},
 		{
-			name: "expect a panic when there's a type mismatch",
+			name: "expect a panic when there's a type mismatch between big-endian uint16 and uint32",
+			given: []TLV{
+				NewTLVBE(0, uint16(12)),
+				NewTLVBE(1, uint16(34)),
+				NewTLVBE(2, uint16(56)),
+			},
+			lookup: func(l TLVList) (any, bool) {
+				return l.Uint32BE(1)
+			},
+			panic: true,
+		},
+		{
+			name: "expect a panic when there's a type mismatch between little-endian uint16 and uint32",
 			given: []TLV{
-				NewTLV(0, uint16(12)),
-				NewTLV(1, uint16(34)),
-				NewTLV(2, uint16(56)),
+				NewTLVLE(0, uint16(12)),
+				NewTLVLE(1, uint16(34)),
+				NewTLVLE(2, uint16(56)),
 			},
 			lookup: func(l TLVList) (any, bool) {
-				return l.Uint32(1)
+				return l.Uint32LE(1)
 			},
 			panic: true,
 		},
@@ -199,10 +260,80 @@ func TestTLVList_Getters(t *testing.T) {
 	}
 }
 
-func TestTLVList_NewTLVPanic(t *testing.T) {
-	// make sure NewTLV panics when it encounters an unsupported type, in this
+func TestTLVList_NewTLVBEPanic(t *testing.T) {
+	// make sure NewTLVBE panics when it encounters an unsupported type, in this
 	// case it's int.
 	assert.Panics(t, func() {
-		NewTLV(1, 30)
+		NewTLVBE(1, 30)
+	})
+}
+
+func TestTLVList_NewTLVLEPanic(t *testing.T) {
+	// make sure NewTLVLE panics when it encounters an unsupported type, in this
+	// case it's int.
+	assert.Panics(t, func() {
+		NewTLVLE(1, 30)
+	})
+}
+
+func TestTLVList_ICQString(t *testing.T) {
+	// Create a new TLV list
+	tlv := TLVList{}
+
+	// Add a valid ICQ string TLV entry to the list
+	tlv.Append(NewTLVLE(0x01, []byte{0x09, 0x00, 'k', 'n', 'i', 't', 't', 'i', 'n', 'g', '\x00'}))
+
+	t.Run("Valid ICQString", func(t *testing.T) {
+		// Test retrieving a valid ICQ string
+		str, ok := tlv.ICQString(0x01)
+		assert.True(t, ok)
+		assert.Equal(t, "knitting", str)
+	})
+
+	t.Run("Non-existent Tag", func(t *testing.T) {
+		// Test retrieving an ICQ string for a non-existent tag
+		str, ok := tlv.ICQString(0x02)
+		assert.False(t, ok)
+		assert.Empty(t, str)
+	})
+
+	t.Run("Malformed ICQString", func(t *testing.T) {
+		// Add a malformed TLV entry (length prefix too short)
+		tlvMalformed := TLVList{}
+		tlvMalformed.Append(NewTLVLE(0x03, []byte{0x02, 0x00, 'a'})) // Length 2 but only 1 character and no null terminator
+
+		str, ok := tlvMalformed.ICQString(0x03)
+		assert.False(t, ok)
+		assert.Empty(t, str)
+	})
+
+	t.Run("Incorrect Length Prefix", func(t *testing.T) {
+		// Add an incorrect length prefix (does not match actual string length)
+		tlvIncorrectLength := TLVList{}
+		tlvIncorrectLength.Append(NewTLVLE(0x04, []byte{0x0A, 0x00, 'k', 'n', 'i', 't', 't', 'i', 'n', 'g', '\x00'})) // Length prefix is 9 but actual length is 7 + 1 (null terminator)
+
+		str, ok := tlvIncorrectLength.ICQString(0x04)
+		assert.False(t, ok)
+		assert.Empty(t, str)
+	})
+
+	t.Run("Short Length Prefix", func(t *testing.T) {
+		// Add a TLV with a length prefix, but the data is too short to contain a valid ICQ string
+		tlvShortLength := TLVList{}
+		tlvShortLength.Append(NewTLVLE(0x05, []byte{0x05, 0x00})) // Length prefix is 5 but no data
+
+		str, ok := tlvShortLength.ICQString(0x05)
+		assert.False(t, ok)
+		assert.Empty(t, str)
+	})
+
+	t.Run("Empty String", func(t *testing.T) {
+		// Add a TLV with an empty ICQ string (just the length prefix and null terminator)
+		tlvEmptyString := TLVList{}
+		tlvEmptyString.Append(NewTLVLE(0x06, []byte{0x01, 0x00, '\x00'})) // Length prefix is 1 with just null terminator
+
+		str, ok := tlvEmptyString.ICQString(0x06)
+		assert.True(t, ok)
+		assert.Empty(t, str)
 	})
 }

Some files were not shown because too many files changed in this diff