Explorar el Código

ref: use html tokenizer and remove advertisedssl

siohaza hace 5 meses
padre
commit
b2c2a292ca

+ 13 - 13
foodgroup/auth.go

@@ -282,9 +282,9 @@ func (s AuthService) BUCPChallenge(ctx context.Context, inBody wire.SNAC_0x17_0x
 // (wire.LoginTLVTagsReconnectHere) and an authorization cookie
 // (wire.LoginTLVTagsAuthorizationCookie). Else, an error code is set
 // (wire.LoginTLVTagsErrorSubcode).
-func (s AuthService) BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error) {
+func (s AuthService) BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error) {
 
-	block, err := s.login(ctx, inBody.TLVList, newUserFn, advertisedHost, advertisedHostSSL)
+	block, err := s.login(ctx, inBody.TLVList, newUserFn, advertisedHost)
 	if err != nil {
 		return wire.SNACMessage{}, err
 	}
@@ -310,8 +310,8 @@ func (s AuthService) BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_B
 // (wire.LoginTLVTagsReconnectHere) and an authorization cookie
 // (wire.LoginTLVTagsAuthorizationCookie). Else, an error code is set
 // (wire.LoginTLVTagsErrorSubcode).
-func (s AuthService) FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.TLVRestBlock, error) {
-	return s.login(ctx, inFrame.TLVList, newUserFn, advertisedHost, advertisedHostSSL)
+func (s AuthService) FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.TLVRestBlock, error) {
+	return s.login(ctx, inFrame.TLVList, newUserFn, advertisedHost)
 }
 
 // KerberosLogin handles AIM-style Kerberos authentication for AIM 6.0+.
@@ -322,7 +322,7 @@ func (s AuthService) FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame
 //
 // Several values in the response are poorly understood but necessary for proper
 // processing on the client side.
-func (s AuthService) KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error) {
+func (s AuthService) KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error) {
 
 	b, ok := inBody.TicketRequestMetadata.Bytes(wire.KerberosTLVTicketRequest)
 	if !ok {
@@ -345,7 +345,7 @@ func (s AuthService) KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_
 		list = append(list, wire.NewTLVBE(wire.LoginTLVTagsPlaintextPassword, info.Password))
 	}
 
-	result, err := s.login(ctx, list, newUserFn, advertisedHost, advertisedHostSSL)
+	result, err := s.login(ctx, list, newUserFn, advertisedHost)
 	if err != nil {
 		return wire.SNACMessage{}, fmt.Errorf("login: %w", err)
 	}
@@ -480,7 +480,7 @@ func (l *loginProperties) fromTLV(list wire.TLVList) error {
 
 // login validates a user's credentials and creates their session. it returns
 // metadata used in both BUCP and FLAP authentication responses.
-func (s AuthService) login(ctx context.Context, tlv wire.TLVList, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.TLVRestBlock, error) {
+func (s AuthService) login(ctx context.Context, tlv wire.TLVList, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.TLVRestBlock, error) {
 
 	props := loginProperties{}
 	if err := props.fromTLV(tlv); err != nil {
@@ -512,7 +512,7 @@ func (s AuthService) login(ctx context.Context, tlv wire.TLVList, newUserFn func
 		if s.config.DisableAuth {
 			// auth disabled, create the user
 			s.logger.Debug("login: auth disabled, creating user", "screen_name", props.screenName)
-			return s.createUser(ctx, props, newUserFn, advertisedHost, advertisedHostSSL)
+			return s.createUser(ctx, props, newUserFn, advertisedHost)
 		}
 		// auth enabled, return separate login errors for ICQ and AIM
 		loginErr := wire.LoginErrInvalidUsernameOrPassword
@@ -538,7 +538,7 @@ func (s AuthService) login(ctx context.Context, tlv wire.TLVList, newUserFn func
 	if s.config.DisableAuth {
 		// user exists, but don't validate
 		s.logger.Debug("login: auth disabled, skipping password validation", "screen_name", props.screenName)
-		return s.loginSuccessResponse(props, advertisedHost, advertisedHostSSL)
+		return s.loginSuccessResponse(props, advertisedHost)
 	}
 
 	var loginOK bool
@@ -588,10 +588,10 @@ func (s AuthService) login(ctx context.Context, tlv wire.TLVList, newUserFn func
 	}
 
 	s.logger.Debug("login: login successful", "screen_name", props.screenName)
-	return s.loginSuccessResponse(props, advertisedHost, advertisedHostSSL)
+	return s.loginSuccessResponse(props, advertisedHost)
 }
 
-func (s AuthService) createUser(ctx context.Context, props loginProperties, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.TLVRestBlock, error) {
+func (s AuthService) createUser(ctx context.Context, props loginProperties, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.TLVRestBlock, error) {
 
 	var err error
 	if props.screenName.IsUIN() {
@@ -621,10 +621,10 @@ func (s AuthService) createUser(ctx context.Context, props loginProperties, newU
 		return wire.TLVRestBlock{}, err
 	}
 
-	return s.loginSuccessResponse(props, advertisedHost, advertisedHostSSL)
+	return s.loginSuccessResponse(props, advertisedHost)
 }
 
-func (s AuthService) loginSuccessResponse(props loginProperties, advertisedHost string, advertisedHostSSL string) (wire.TLVRestBlock, error) {
+func (s AuthService) loginSuccessResponse(props loginProperties, advertisedHost string) (wire.TLVRestBlock, error) {
 	loginCookie := state.ServerCookie{
 		Service:       wire.BOS,
 		ScreenName:    props.screenName,

+ 3 - 3
foodgroup/auth_test.go

@@ -788,7 +788,7 @@ func TestAuthService_BUCPLoginRequest(t *testing.T) {
 				maxConcurrentLoginsPerUser: 2,
 				logger:                     slog.Default(),
 			}
-			outputSNAC, err := svc.BUCPLogin(context.Background(), tc.inputSNAC, tc.newUserFn, tc.advertisedHost, "")
+			outputSNAC, err := svc.BUCPLogin(context.Background(), tc.inputSNAC, tc.newUserFn, tc.advertisedHost)
 			assert.ErrorIs(t, err, tc.wantErr)
 			assert.Equal(t, tc.expectOutput, outputSNAC)
 		})
@@ -1231,7 +1231,7 @@ func TestAuthService_FLAPLogin(t *testing.T) {
 				userManager: userManager,
 				logger:      slog.Default(),
 			}
-			outputSNAC, err := svc.FLAPLogin(context.Background(), tc.inputSNAC, tc.newUserFn, tc.advertisedHost, "")
+			outputSNAC, err := svc.FLAPLogin(context.Background(), tc.inputSNAC, tc.newUserFn, tc.advertisedHost)
 			assert.ErrorIs(t, err, tc.wantErr)
 			assert.Equal(t, tc.expectOutput, outputSNAC)
 		})
@@ -1572,7 +1572,7 @@ func TestAuthService_KerberosLogin(t *testing.T) {
 				maxConcurrentLoginsPerUser: 2,
 				logger:                     slog.Default(),
 			}
-			outputSNAC, err := svc.KerberosLogin(context.Background(), tc.inputSNAC, tc.newUserFn, tc.advertisedHost, "")
+			outputSNAC, err := svc.KerberosLogin(context.Background(), tc.inputSNAC, tc.newUserFn, tc.advertisedHost)
 			assert.ErrorIs(t, err, tc.wantErr)
 			assert.Equal(t, tc.expectOutput, outputSNAC)
 		})

+ 37 - 3
foodgroup/icbm.go

@@ -5,11 +5,14 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"io"
 	"log/slog"
+	"strings"
 	"sync"
 	"time"
 
 	"github.com/patrickmn/go-cache"
+	"golang.org/x/net/html"
 
 	"github.com/mk6i/open-oscar-server/state"
 	"github.com/mk6i/open-oscar-server/wire"
@@ -148,8 +151,10 @@ func (s ICBMService) ChannelMsgToHost(ctx context.Context, instance *state.Sessi
 				return nil, fmt.Errorf("addExternalIP: %w", err)
 			}
 		}
-		// Strip HTML from message if recipient doesn't support XHTML
-		if (clientIM.ChannelID == wire.ICBMChannelIM || clientIM.ChannelID == wire.ICBMChannelMIME) &&
+		// Strip HTML from ICQ messages if recipient doesn't support XHTML.
+		// AIM clients send HTML formatted messages that should be preserved.
+		if instance.UIN() > 0 &&
+			(clientIM.ChannelID == wire.ICBMChannelIM || clientIM.ChannelID == wire.ICBMChannelMIME) &&
 			tlv.Tag == wire.ICBMTLVAOLIMData {
 			if !recipSess.HasCap(wire.CapXHTMLIM) {
 				if transformedTLV, err := stripHTMLFromICBMTLV(tlv); err == nil {
@@ -307,6 +312,35 @@ func addExternalIP(instance *state.SessionInstance, tlv wire.TLV) (wire.TLV, err
 	return tlv, nil
 }
 
+// stripHTML extracts plaintext from HTML content.
+func stripHTML(text []byte) []byte {
+	if len(text) == 0 {
+		return text
+	}
+
+	var result strings.Builder
+	tok := html.NewTokenizer(strings.NewReader(string(text)))
+
+	for {
+		tt := tok.Next()
+		switch tt {
+		case html.TextToken:
+			result.Write(tok.Text())
+		case html.SelfClosingTagToken, html.StartTagToken:
+			tn, _ := tok.TagName()
+			if string(tn) == "br" {
+				result.WriteByte('\n')
+			}
+		case html.ErrorToken:
+			if tok.Err() == io.EOF {
+				return []byte(result.String())
+			}
+			// on error return what we have
+			return []byte(result.String())
+		}
+	}
+}
+
 // stripHTMLFromICBMTLV transforms an ICBMTLVAOLIMData TLV by stripping HTML
 // from the message text for clients that don't support XHTML.
 func stripHTMLFromICBMTLV(tlv wire.TLV) (wire.TLV, error) {
@@ -324,7 +358,7 @@ func stripHTMLFromICBMTLV(tlv wire.TLV) (wire.TLV, error) {
 			}
 
 			// Strip HTML from message text
-			strippedText := wire.StripHTML(msg.Text)
+			strippedText := stripHTML(msg.Text)
 			if !bytes.Equal(strippedText, msg.Text) {
 				msg.Text = strippedText
 

+ 76 - 0
foodgroup/icbm_test.go

@@ -3443,3 +3443,79 @@ func Test_calcRefreshInterval(t *testing.T) {
 		assert.Equal(t, interval, newInterval)
 	})
 }
+
+func TestStripHTML(t *testing.T) {
+	tests := []struct {
+		name     string
+		input    []byte
+		expected []byte
+	}{
+		{
+			name:     "simple HTML tags",
+			input:    []byte("<HTML><BODY>Hello World</BODY></HTML>"),
+			expected: []byte("Hello World"),
+		},
+		{
+			name:     "BR tag to newline",
+			input:    []byte("Line 1<BR>Line 2"),
+			expected: []byte("Line 1\nLine 2"),
+		},
+		{
+			name:     "lowercase br tag",
+			input:    []byte("Line 1<br>Line 2"),
+			expected: []byte("Line 1\nLine 2"),
+		},
+		{
+			name:     "self-closing br tag",
+			input:    []byte("Line 1<br/>Line 2"),
+			expected: []byte("Line 1\nLine 2"),
+		},
+		{
+			name:     "HTML entities",
+			input:    []byte("&lt;test&gt; &amp; &quot;quoted&quot;"),
+			expected: []byte("<test> & \"quoted\""),
+		},
+		{
+			name:     "FONT tags with attributes",
+			input:    []byte(`<FONT FACE="Arial" SIZE="3">Text</FONT>`),
+			expected: []byte("Text"),
+		},
+		{
+			name:     "mixed formatting",
+			input:    []byte("<HTML><BODY><B>Bold</B> <I>Italic</I></BODY></HTML>"),
+			expected: []byte("Bold Italic"),
+		},
+		{
+			name:     "plain text unchanged",
+			input:    []byte("Hello World"),
+			expected: []byte("Hello World"),
+		},
+		{
+			name:     "empty input",
+			input:    []byte(""),
+			expected: []byte(""),
+		},
+		{
+			name:     "nbsp entity",
+			input:    []byte("Hello&nbsp;World"),
+			expected: []byte("Hello\u00a0World"),
+		},
+		{
+			name:     "realistic HTML message",
+			input:    []byte(`<HTML><BODY dir="ltr"><FONT color="#000000" size="2" face="Arial">Hello</FONT></BODY></HTML>`),
+			expected: []byte("Hello"),
+		},
+		{
+			name:     "apos entity",
+			input:    []byte("it&apos;s working"),
+			expected: []byte("it's working"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result := stripHTML(tt.input)
+			assert.Equal(t, tt.expected, result)
+		})
+	}
+}

+ 0 - 21
foodgroup/oservice_test.go

@@ -2039,27 +2039,6 @@ func TestOServiceService_UserInfoQuery(t *testing.T) {
 	}
 }
 
-func TestOServiceService_ProbeReq(t *testing.T) {
-	svc := NewOServiceService(config.Config{}, nil, slog.Default(), nil, nil, nil, nil, nil, wire.DefaultSNACRateLimits(), nil, nil, nil)
-
-	inFrame := wire.SNACFrame{
-		FoodGroup: wire.OService,
-		SubGroup:  wire.OServiceProbeReq,
-		RequestID: 12345,
-	}
-
-	want := wire.SNACMessage{
-		Frame: wire.SNACFrame{
-			FoodGroup: wire.OService,
-			SubGroup:  wire.OServiceProbeAck,
-			RequestID: 12345,
-		},
-	}
-
-	have := svc.ProbeReq(context.Background(), inFrame)
-	assert.Equal(t, want, have)
-}
-
 func TestOServiceService_IdleNotification(t *testing.T) {
 	tests := []struct {
 		name     string

+ 2 - 2
server/kerberos/kerberos.go

@@ -17,7 +17,7 @@ import (
 )
 
 type AuthService interface {
-	KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error)
+	KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error)
 }
 
 func NewKerberosServer(listeners []config.Listener, logger *slog.Logger, authService AuthService) *Server {
@@ -112,7 +112,7 @@ func postHandler(w http.ResponseWriter, r *http.Request, authService AuthService
 		return
 	}
 
-	response, err := authService.KerberosLogin(r.Context(), body, state.NewStubUser, "", listenAddress)
+	response, err := authService.KerberosLogin(r.Context(), body, state.NewStubUser, listenAddress)
 	if err != nil {
 		logger.Error("authService.KerberosLogin", "err", err.Error())
 		http.Error(w, "internal server error", http.StatusInternalServerError)

+ 1 - 1
server/kerberos/kerberos_test.go

@@ -201,7 +201,7 @@ func TestKerberosLoginHandler(t *testing.T) {
 				mockAuth := newMockAuthService(t)
 				if tt.expectLogin {
 					mockAuth.EXPECT().
-						KerberosLogin(mock.Anything, tt.request.Body, mock.Anything, mock.Anything, mock.Anything).
+						KerberosLogin(mock.Anything, tt.request.Body, mock.Anything, mock.Anything).
 						Return(tt.response, tt.responseErr)
 				}
 				srv = NewKerberosServer(tt.listeners, log, mockAuth)

+ 12 - 18
server/kerberos/mock_auth_test.go

@@ -40,8 +40,8 @@ func (_m *mockAuthService) EXPECT() *mockAuthService_Expecter {
 }
 
 // KerberosLogin provides a mock function for the type mockAuthService
-func (_mock *mockAuthService) KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error) {
-	ret := _mock.Called(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+func (_mock *mockAuthService) KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error) {
+	ret := _mock.Called(ctx, inBody, newUserFn, advertisedHost)
 
 	if len(ret) == 0 {
 		panic("no return value specified for KerberosLogin")
@@ -49,16 +49,16 @@ func (_mock *mockAuthService) KerberosLogin(ctx context.Context, inBody wire.SNA
 
 	var r0 wire.SNACMessage
 	var r1 error
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) (wire.SNACMessage, error)); ok {
-		return returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) (wire.SNACMessage, error)); ok {
+		return returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	}
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) wire.SNACMessage); ok {
-		r0 = returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) wire.SNACMessage); ok {
+		r0 = returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	} else {
 		r0 = ret.Get(0).(wire.SNACMessage)
 	}
-	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) error); ok {
-		r1 = returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) error); ok {
+		r1 = returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	} else {
 		r1 = ret.Error(1)
 	}
@@ -75,12 +75,11 @@ type mockAuthService_KerberosLogin_Call struct {
 //   - inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest
 //   - newUserFn func(screenName state.DisplayScreenName) (state.User, error)
 //   - advertisedHost string
-//   - advertisedHostSSL string
-func (_e *mockAuthService_Expecter) KerberosLogin(ctx interface{}, inBody interface{}, newUserFn interface{}, advertisedHost interface{}, advertisedHostSSL interface{}) *mockAuthService_KerberosLogin_Call {
-	return &mockAuthService_KerberosLogin_Call{Call: _e.mock.On("KerberosLogin", ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)}
+func (_e *mockAuthService_Expecter) KerberosLogin(ctx interface{}, inBody interface{}, newUserFn interface{}, advertisedHost interface{}) *mockAuthService_KerberosLogin_Call {
+	return &mockAuthService_KerberosLogin_Call{Call: _e.mock.On("KerberosLogin", ctx, inBody, newUserFn, advertisedHost)}
 }
 
-func (_c *mockAuthService_KerberosLogin_Call) Run(run func(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string)) *mockAuthService_KerberosLogin_Call {
+func (_c *mockAuthService_KerberosLogin_Call) Run(run func(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string)) *mockAuthService_KerberosLogin_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		var arg0 context.Context
 		if args[0] != nil {
@@ -98,16 +97,11 @@ func (_c *mockAuthService_KerberosLogin_Call) Run(run func(ctx context.Context,
 		if args[3] != nil {
 			arg3 = args[3].(string)
 		}
-		var arg4 string
-		if args[4] != nil {
-			arg4 = args[4].(string)
-		}
 		run(
 			arg0,
 			arg1,
 			arg2,
 			arg3,
-			arg4,
 		)
 	})
 	return _c
@@ -118,7 +112,7 @@ func (_c *mockAuthService_KerberosLogin_Call) Return(sNACMessage wire.SNACMessag
 	return _c
 }
 
-func (_c *mockAuthService_KerberosLogin_Call) RunAndReturn(run func(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error)) *mockAuthService_KerberosLogin_Call {
+func (_c *mockAuthService_KerberosLogin_Call) RunAndReturn(run func(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error)) *mockAuthService_KerberosLogin_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 36 - 54
server/oscar/mock_auth_test.go

@@ -113,8 +113,8 @@ func (_c *mockAuthService_BUCPChallenge_Call) RunAndReturn(run func(ctx context.
 }
 
 // BUCPLogin provides a mock function for the type mockAuthService
-func (_mock *mockAuthService) BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error) {
-	ret := _mock.Called(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+func (_mock *mockAuthService) BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error) {
+	ret := _mock.Called(ctx, inBody, newUserFn, advertisedHost)
 
 	if len(ret) == 0 {
 		panic("no return value specified for BUCPLogin")
@@ -122,16 +122,16 @@ func (_mock *mockAuthService) BUCPLogin(ctx context.Context, inBody wire.SNAC_0x
 
 	var r0 wire.SNACMessage
 	var r1 error
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) (wire.SNACMessage, error)); ok {
-		return returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) (wire.SNACMessage, error)); ok {
+		return returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	}
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) wire.SNACMessage); ok {
-		r0 = returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) wire.SNACMessage); ok {
+		r0 = returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	} else {
 		r0 = ret.Get(0).(wire.SNACMessage)
 	}
-	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) error); ok {
-		r1 = returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) error); ok {
+		r1 = returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	} else {
 		r1 = ret.Error(1)
 	}
@@ -148,12 +148,11 @@ type mockAuthService_BUCPLogin_Call struct {
 //   - inBody wire.SNAC_0x17_0x02_BUCPLoginRequest
 //   - newUserFn func(screenName state.DisplayScreenName) (state.User, error)
 //   - advertisedHost string
-//   - advertisedHostSSL string
-func (_e *mockAuthService_Expecter) BUCPLogin(ctx interface{}, inBody interface{}, newUserFn interface{}, advertisedHost interface{}, advertisedHostSSL interface{}) *mockAuthService_BUCPLogin_Call {
-	return &mockAuthService_BUCPLogin_Call{Call: _e.mock.On("BUCPLogin", ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)}
+func (_e *mockAuthService_Expecter) BUCPLogin(ctx interface{}, inBody interface{}, newUserFn interface{}, advertisedHost interface{}) *mockAuthService_BUCPLogin_Call {
+	return &mockAuthService_BUCPLogin_Call{Call: _e.mock.On("BUCPLogin", ctx, inBody, newUserFn, advertisedHost)}
 }
 
-func (_c *mockAuthService_BUCPLogin_Call) Run(run func(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string)) *mockAuthService_BUCPLogin_Call {
+func (_c *mockAuthService_BUCPLogin_Call) Run(run func(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string)) *mockAuthService_BUCPLogin_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		var arg0 context.Context
 		if args[0] != nil {
@@ -171,16 +170,11 @@ func (_c *mockAuthService_BUCPLogin_Call) Run(run func(ctx context.Context, inBo
 		if args[3] != nil {
 			arg3 = args[3].(string)
 		}
-		var arg4 string
-		if args[4] != nil {
-			arg4 = args[4].(string)
-		}
 		run(
 			arg0,
 			arg1,
 			arg2,
 			arg3,
-			arg4,
 		)
 	})
 	return _c
@@ -191,7 +185,7 @@ func (_c *mockAuthService_BUCPLogin_Call) Return(sNACMessage wire.SNACMessage, e
 	return _c
 }
 
-func (_c *mockAuthService_BUCPLogin_Call) RunAndReturn(run func(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error)) *mockAuthService_BUCPLogin_Call {
+func (_c *mockAuthService_BUCPLogin_Call) RunAndReturn(run func(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error)) *mockAuthService_BUCPLogin_Call {
 	_c.Call.Return(run)
 	return _c
 }
@@ -257,8 +251,8 @@ func (_c *mockAuthService_CrackCookie_Call) RunAndReturn(run func(authCookie []b
 }
 
 // FLAPLogin provides a mock function for the type mockAuthService
-func (_mock *mockAuthService) FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.TLVRestBlock, error) {
-	ret := _mock.Called(ctx, inFrame, newUserFn, advertisedHost, advertisedHostSSL)
+func (_mock *mockAuthService) FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.TLVRestBlock, error) {
+	ret := _mock.Called(ctx, inFrame, newUserFn, advertisedHost)
 
 	if len(ret) == 0 {
 		panic("no return value specified for FLAPLogin")
@@ -266,16 +260,16 @@ func (_mock *mockAuthService) FLAPLogin(ctx context.Context, inFrame wire.FLAPSi
 
 	var r0 wire.TLVRestBlock
 	var r1 error
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string, string) (wire.TLVRestBlock, error)); ok {
-		return returnFunc(ctx, inFrame, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string) (wire.TLVRestBlock, error)); ok {
+		return returnFunc(ctx, inFrame, newUserFn, advertisedHost)
 	}
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string, string) wire.TLVRestBlock); ok {
-		r0 = returnFunc(ctx, inFrame, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string) wire.TLVRestBlock); ok {
+		r0 = returnFunc(ctx, inFrame, newUserFn, advertisedHost)
 	} else {
 		r0 = ret.Get(0).(wire.TLVRestBlock)
 	}
-	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string, string) error); ok {
-		r1 = returnFunc(ctx, inFrame, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string) error); ok {
+		r1 = returnFunc(ctx, inFrame, newUserFn, advertisedHost)
 	} else {
 		r1 = ret.Error(1)
 	}
@@ -292,12 +286,11 @@ type mockAuthService_FLAPLogin_Call struct {
 //   - inFrame wire.FLAPSignonFrame
 //   - newUserFn func(screenName state.DisplayScreenName) (state.User, error)
 //   - advertisedHost string
-//   - advertisedHostSSL string
-func (_e *mockAuthService_Expecter) FLAPLogin(ctx interface{}, inFrame interface{}, newUserFn interface{}, advertisedHost interface{}, advertisedHostSSL interface{}) *mockAuthService_FLAPLogin_Call {
-	return &mockAuthService_FLAPLogin_Call{Call: _e.mock.On("FLAPLogin", ctx, inFrame, newUserFn, advertisedHost, advertisedHostSSL)}
+func (_e *mockAuthService_Expecter) FLAPLogin(ctx interface{}, inFrame interface{}, newUserFn interface{}, advertisedHost interface{}) *mockAuthService_FLAPLogin_Call {
+	return &mockAuthService_FLAPLogin_Call{Call: _e.mock.On("FLAPLogin", ctx, inFrame, newUserFn, advertisedHost)}
 }
 
-func (_c *mockAuthService_FLAPLogin_Call) Run(run func(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string)) *mockAuthService_FLAPLogin_Call {
+func (_c *mockAuthService_FLAPLogin_Call) Run(run func(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string)) *mockAuthService_FLAPLogin_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		var arg0 context.Context
 		if args[0] != nil {
@@ -315,16 +308,11 @@ func (_c *mockAuthService_FLAPLogin_Call) Run(run func(ctx context.Context, inFr
 		if args[3] != nil {
 			arg3 = args[3].(string)
 		}
-		var arg4 string
-		if args[4] != nil {
-			arg4 = args[4].(string)
-		}
 		run(
 			arg0,
 			arg1,
 			arg2,
 			arg3,
-			arg4,
 		)
 	})
 	return _c
@@ -335,14 +323,14 @@ func (_c *mockAuthService_FLAPLogin_Call) Return(tLVRestBlock wire.TLVRestBlock,
 	return _c
 }
 
-func (_c *mockAuthService_FLAPLogin_Call) RunAndReturn(run func(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.TLVRestBlock, error)) *mockAuthService_FLAPLogin_Call {
+func (_c *mockAuthService_FLAPLogin_Call) RunAndReturn(run func(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.TLVRestBlock, error)) *mockAuthService_FLAPLogin_Call {
 	_c.Call.Return(run)
 	return _c
 }
 
 // KerberosLogin provides a mock function for the type mockAuthService
-func (_mock *mockAuthService) KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error) {
-	ret := _mock.Called(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+func (_mock *mockAuthService) KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error) {
+	ret := _mock.Called(ctx, inBody, newUserFn, advertisedHost)
 
 	if len(ret) == 0 {
 		panic("no return value specified for KerberosLogin")
@@ -350,16 +338,16 @@ func (_mock *mockAuthService) KerberosLogin(ctx context.Context, inBody wire.SNA
 
 	var r0 wire.SNACMessage
 	var r1 error
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) (wire.SNACMessage, error)); ok {
-		return returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) (wire.SNACMessage, error)); ok {
+		return returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	}
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) wire.SNACMessage); ok {
-		r0 = returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) wire.SNACMessage); ok {
+		r0 = returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	} else {
 		r0 = ret.Get(0).(wire.SNACMessage)
 	}
-	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) error); ok {
-		r1 = returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.SNAC_0x050C_0x0002_KerberosLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) error); ok {
+		r1 = returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	} else {
 		r1 = ret.Error(1)
 	}
@@ -376,12 +364,11 @@ type mockAuthService_KerberosLogin_Call struct {
 //   - inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest
 //   - newUserFn func(screenName state.DisplayScreenName) (state.User, error)
 //   - advertisedHost string
-//   - advertisedHostSSL string
-func (_e *mockAuthService_Expecter) KerberosLogin(ctx interface{}, inBody interface{}, newUserFn interface{}, advertisedHost interface{}, advertisedHostSSL interface{}) *mockAuthService_KerberosLogin_Call {
-	return &mockAuthService_KerberosLogin_Call{Call: _e.mock.On("KerberosLogin", ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)}
+func (_e *mockAuthService_Expecter) KerberosLogin(ctx interface{}, inBody interface{}, newUserFn interface{}, advertisedHost interface{}) *mockAuthService_KerberosLogin_Call {
+	return &mockAuthService_KerberosLogin_Call{Call: _e.mock.On("KerberosLogin", ctx, inBody, newUserFn, advertisedHost)}
 }
 
-func (_c *mockAuthService_KerberosLogin_Call) Run(run func(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string)) *mockAuthService_KerberosLogin_Call {
+func (_c *mockAuthService_KerberosLogin_Call) Run(run func(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string)) *mockAuthService_KerberosLogin_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		var arg0 context.Context
 		if args[0] != nil {
@@ -399,16 +386,11 @@ func (_c *mockAuthService_KerberosLogin_Call) Run(run func(ctx context.Context,
 		if args[3] != nil {
 			arg3 = args[3].(string)
 		}
-		var arg4 string
-		if args[4] != nil {
-			arg4 = args[4].(string)
-		}
 		run(
 			arg0,
 			arg1,
 			arg2,
 			arg3,
-			arg4,
 		)
 	})
 	return _c
@@ -419,7 +401,7 @@ func (_c *mockAuthService_KerberosLogin_Call) Return(sNACMessage wire.SNACMessag
 	return _c
 }
 
-func (_c *mockAuthService_KerberosLogin_Call) RunAndReturn(run func(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error)) *mockAuthService_KerberosLogin_Call {
+func (_c *mockAuthService_KerberosLogin_Call) RunAndReturn(run func(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error)) *mockAuthService_KerberosLogin_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 7 - 9
server/oscar/server.go

@@ -204,7 +204,7 @@ func (s oscarServer) routeConnection(ctx context.Context, conn net.Conn, listene
 		return err
 	}
 
-	flapc := wire.NewFlapClient(0, conn, conn)
+	flapc := wire.NewFlapClient(100, conn, conn)
 
 	// send flap signon with server capabilities
 	signonTLVs := []wire.TLV{
@@ -224,7 +224,7 @@ func (s oscarServer) routeConnection(ctx context.Context, conn net.Conn, listene
 		return s.connectToOSCARService(ctx, flap, flapc, conn, listener)
 	}
 
-	return s.authenticate(ctx, flap, ip, conn, flapc, listener.BOSAdvertisedHostPlain, listener.BOSAdvertisedHostSSL)
+	return s.authenticate(ctx, flap, ip, conn, flapc, listener.BOSAdvertisedHostPlain)
 }
 
 func (s oscarServer) connectToOSCARService(
@@ -408,7 +408,6 @@ func (s oscarServer) authenticate(
 	conn net.Conn,
 	flapc *wire.FlapClient,
 	advertisedHost string,
-	advertisedHostSSL string,
 ) error {
 	if ok, isBUCP := s.Allow(ip); !ok {
 		s.Logger.Error("user rate limited at login", "remote", ip)
@@ -444,12 +443,12 @@ func (s oscarServer) authenticate(
 	// 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 := flap.Uint16BE(wire.LoginTLVTagsScreenName); hasScreenName {
-		return s.processFLAPAuth(ctx, flap, flapc, advertisedHost, advertisedHostSSL)
+		return s.processFLAPAuth(ctx, flap, flapc, advertisedHost)
 	}
 
 	s.SetBUCP(ip)
 
-	return s.processBUCPAuth(ctx, flapc, advertisedHost, advertisedHostSSL)
+	return s.processBUCPAuth(ctx, flapc, advertisedHost)
 }
 
 func (s oscarServer) processFLAPAuth(
@@ -457,16 +456,15 @@ func (s oscarServer) processFLAPAuth(
 	signonFrame wire.FLAPSignonFrame,
 	flapc *wire.FlapClient,
 	advertisedHost string,
-	advertisedHostSSL string,
 ) error {
-	tlv, err := s.AuthService.FLAPLogin(ctx, signonFrame, state.NewStubUser, advertisedHost, advertisedHostSSL)
+	tlv, err := s.AuthService.FLAPLogin(ctx, signonFrame, state.NewStubUser, advertisedHost)
 	if err != nil {
 		return err
 	}
 	return flapc.NewSignoff(tlv)
 }
 
-func (s oscarServer) processBUCPAuth(ctx context.Context, flapc *wire.FlapClient, advertisedHost string, advertisedHostSSL string) error {
+func (s oscarServer) processBUCPAuth(ctx context.Context, flapc *wire.FlapClient, advertisedHost string) error {
 	frames := 0
 
 	for {
@@ -517,7 +515,7 @@ func (s oscarServer) processBUCPAuth(ctx context.Context, flapc *wire.FlapClient
 				if err := wire.UnmarshalBE(&loginRequest, buf); err != nil {
 					return err
 				}
-				outSNAC, err := s.BUCPLogin(ctx, loginRequest, state.NewStubUser, advertisedHost, advertisedHostSSL)
+				outSNAC, err := s.BUCPLogin(ctx, loginRequest, state.NewStubUser, advertisedHost)
 				if err != nil {
 					return err
 				}

+ 2 - 35
server/oscar/server_test.go

@@ -222,7 +222,7 @@ func TestOscarServer_RouteConnection_Auth_BUCP(t *testing.T) {
 			Body: wire.SNAC_0x17_0x07_BUCPChallengeResponse{},
 		}, nil)
 	authService.EXPECT().
-		BUCPLogin(matchContext(), mock.Anything, mock.Anything, "localhost:5190", "").
+		BUCPLogin(matchContext(), mock.Anything, mock.Anything, "localhost:5190").
 		Return(wire.SNACMessage{
 			Frame: wire.SNACFrame{
 				FoodGroup: wire.BUCP,
@@ -297,7 +297,7 @@ func TestOscarServer_RouteConnection_Auth_FLAP(t *testing.T) {
 
 	authService := newMockAuthService(t)
 	authService.EXPECT().
-		FLAPLogin(matchContext(), mock.Anything, mock.Anything, "localhost:5190", "").
+		FLAPLogin(matchContext(), mock.Anything, mock.Anything, "localhost:5190").
 		Return(wire.TLVRestBlock{
 			TLVList: []wire.TLV{
 				wire.NewTLVBE(wire.LoginTLVTagsScreenName, "testuser"),
@@ -316,39 +316,6 @@ func TestOscarServer_RouteConnection_Auth_FLAP(t *testing.T) {
 	wg.Wait()
 }
 
-func TestOscarServer_RouteConnection_ConnectionProbe(t *testing.T) {
-	serverConn, clientConn := net.Pipe()
-	addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
-	assert.NoError(t, err)
-
-	clientFake := fakeConn{
-		Conn:   serverConn,
-		local:  addr,
-		remote: addr,
-	}
-
-	go func() {
-		// < receive FLAPSignonFrame from server
-		flap := wire.FLAPFrame{}
-		assert.NoError(t, wire.UnmarshalBE(&flap, clientConn))
-		flapSignonFrame := wire.FLAPSignonFrame{}
-		assert.NoError(t, wire.UnmarshalBE(&flapSignonFrame, bytes.NewBuffer(flap.Payload)))
-
-		// Immediately close connection without sending anything back
-		// This simulates connection probes that some ICQ clients do
-		_ = clientConn.Close()
-	}()
-
-	rt := oscarServer{
-		Logger:        slog.Default(),
-		IPRateLimiter: NewIPRateLimiter(rate.Every(1*time.Minute), 10, 1*time.Minute),
-	}
-
-	// routeConnection should return nil (not an error) for connection probes
-	err = rt.routeConnection(context.Background(), clientFake, config.Listener{BOSAdvertisedHostPlain: "localhost:5190"})
-	assert.NoError(t, err, "connection probe should not be treated as an error")
-}
-
 func TestOscarServer_RouteConnection_BOS(t *testing.T) {
 	instance := state.NewSession().AddInstance()
 

+ 3 - 3
server/oscar/types.go

@@ -47,10 +47,10 @@ type RateLimitUpdater interface {
 
 type AuthService interface {
 	BUCPChallenge(ctx context.Context, inBody wire.SNAC_0x17_0x06_BUCPChallengeRequest, newUUID func() uuid.UUID) (wire.SNACMessage, error)
-	BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error)
+	BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error)
 	CrackCookie(authCookie []byte) (state.ServerCookie, error)
-	FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.TLVRestBlock, error)
-	KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error)
+	FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.TLVRestBlock, error)
+	KerberosLogin(ctx context.Context, inBody wire.SNAC_0x050C_0x0002_KerberosLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error)
 	RegisterBOSSession(ctx context.Context, authCookie state.ServerCookie) (*state.SessionInstance, error)
 	RegisterChatSession(ctx context.Context, authCookie state.ServerCookie) (*state.SessionInstance, error)
 	RetrieveBOSSession(ctx context.Context, authCookie state.ServerCookie) (*state.SessionInstance, error)

+ 1 - 1
server/toc/cmd_client.go

@@ -1675,7 +1675,7 @@ func (s OSCARProxy) Signon(ctx context.Context, args []byte) (*state.SessionInst
 	signonFrame.Append(wire.NewTLVBE(wire.LoginTLVTagsScreenName, userName))
 	signonFrame.Append(wire.NewTLVBE(wire.LoginTLVTagsRoastedTOCPassword, passwordHash))
 
-	block, err := s.AuthService.FLAPLogin(ctx, signonFrame, state.NewStubUser, "", "")
+	block, err := s.AuthService.FLAPLogin(ctx, signonFrame, state.NewStubUser, "")
 	if err != nil {
 		return nil, []string{s.runtimeErr(ctx, fmt.Errorf("AuthService.FLAPLogin: %w", err))}
 	}

+ 1 - 1
server/toc/cmd_client_test.go

@@ -4163,7 +4163,7 @@ func TestOSCARProxy_Signon(t *testing.T) {
 			authSvc := newMockAuthService(t)
 			for _, params := range tc.mockParams.flapLoginParams {
 				authSvc.EXPECT().
-					FLAPLogin(matchContext(), params.frame, mock.Anything, "", "").
+					FLAPLogin(matchContext(), params.frame, mock.Anything, "").
 					Return(params.tlv, params.err)
 			}
 			for _, params := range tc.mockParams.crackCookieParams {

+ 24 - 36
server/toc/mock_auth_service_test.go

@@ -113,8 +113,8 @@ func (_c *mockAuthService_BUCPChallenge_Call) RunAndReturn(run func(ctx context.
 }
 
 // BUCPLogin provides a mock function for the type mockAuthService
-func (_mock *mockAuthService) BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error) {
-	ret := _mock.Called(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+func (_mock *mockAuthService) BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error) {
+	ret := _mock.Called(ctx, inBody, newUserFn, advertisedHost)
 
 	if len(ret) == 0 {
 		panic("no return value specified for BUCPLogin")
@@ -122,16 +122,16 @@ func (_mock *mockAuthService) BUCPLogin(ctx context.Context, inBody wire.SNAC_0x
 
 	var r0 wire.SNACMessage
 	var r1 error
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) (wire.SNACMessage, error)); ok {
-		return returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) (wire.SNACMessage, error)); ok {
+		return returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	}
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) wire.SNACMessage); ok {
-		r0 = returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) wire.SNACMessage); ok {
+		r0 = returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	} else {
 		r0 = ret.Get(0).(wire.SNACMessage)
 	}
-	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string, string) error); ok {
-		r1 = returnFunc(ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.SNAC_0x17_0x02_BUCPLoginRequest, func(screenName state.DisplayScreenName) (state.User, error), string) error); ok {
+		r1 = returnFunc(ctx, inBody, newUserFn, advertisedHost)
 	} else {
 		r1 = ret.Error(1)
 	}
@@ -148,12 +148,11 @@ type mockAuthService_BUCPLogin_Call struct {
 //   - inBody wire.SNAC_0x17_0x02_BUCPLoginRequest
 //   - newUserFn func(screenName state.DisplayScreenName) (state.User, error)
 //   - advertisedHost string
-//   - advertisedHostSSL string
-func (_e *mockAuthService_Expecter) BUCPLogin(ctx interface{}, inBody interface{}, newUserFn interface{}, advertisedHost interface{}, advertisedHostSSL interface{}) *mockAuthService_BUCPLogin_Call {
-	return &mockAuthService_BUCPLogin_Call{Call: _e.mock.On("BUCPLogin", ctx, inBody, newUserFn, advertisedHost, advertisedHostSSL)}
+func (_e *mockAuthService_Expecter) BUCPLogin(ctx interface{}, inBody interface{}, newUserFn interface{}, advertisedHost interface{}) *mockAuthService_BUCPLogin_Call {
+	return &mockAuthService_BUCPLogin_Call{Call: _e.mock.On("BUCPLogin", ctx, inBody, newUserFn, advertisedHost)}
 }
 
-func (_c *mockAuthService_BUCPLogin_Call) Run(run func(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string)) *mockAuthService_BUCPLogin_Call {
+func (_c *mockAuthService_BUCPLogin_Call) Run(run func(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string)) *mockAuthService_BUCPLogin_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		var arg0 context.Context
 		if args[0] != nil {
@@ -171,16 +170,11 @@ func (_c *mockAuthService_BUCPLogin_Call) Run(run func(ctx context.Context, inBo
 		if args[3] != nil {
 			arg3 = args[3].(string)
 		}
-		var arg4 string
-		if args[4] != nil {
-			arg4 = args[4].(string)
-		}
 		run(
 			arg0,
 			arg1,
 			arg2,
 			arg3,
-			arg4,
 		)
 	})
 	return _c
@@ -191,7 +185,7 @@ func (_c *mockAuthService_BUCPLogin_Call) Return(sNACMessage wire.SNACMessage, e
 	return _c
 }
 
-func (_c *mockAuthService_BUCPLogin_Call) RunAndReturn(run func(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error)) *mockAuthService_BUCPLogin_Call {
+func (_c *mockAuthService_BUCPLogin_Call) RunAndReturn(run func(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error)) *mockAuthService_BUCPLogin_Call {
 	_c.Call.Return(run)
 	return _c
 }
@@ -257,8 +251,8 @@ func (_c *mockAuthService_CrackCookie_Call) RunAndReturn(run func(authCookie []b
 }
 
 // FLAPLogin provides a mock function for the type mockAuthService
-func (_mock *mockAuthService) FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.TLVRestBlock, error) {
-	ret := _mock.Called(ctx, inFrame, newUserFn, advertisedHost, advertisedHostSSL)
+func (_mock *mockAuthService) FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.TLVRestBlock, error) {
+	ret := _mock.Called(ctx, inFrame, newUserFn, advertisedHost)
 
 	if len(ret) == 0 {
 		panic("no return value specified for FLAPLogin")
@@ -266,16 +260,16 @@ func (_mock *mockAuthService) FLAPLogin(ctx context.Context, inFrame wire.FLAPSi
 
 	var r0 wire.TLVRestBlock
 	var r1 error
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string, string) (wire.TLVRestBlock, error)); ok {
-		return returnFunc(ctx, inFrame, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string) (wire.TLVRestBlock, error)); ok {
+		return returnFunc(ctx, inFrame, newUserFn, advertisedHost)
 	}
-	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string, string) wire.TLVRestBlock); ok {
-		r0 = returnFunc(ctx, inFrame, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(0).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string) wire.TLVRestBlock); ok {
+		r0 = returnFunc(ctx, inFrame, newUserFn, advertisedHost)
 	} else {
 		r0 = ret.Get(0).(wire.TLVRestBlock)
 	}
-	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string, string) error); ok {
-		r1 = returnFunc(ctx, inFrame, newUserFn, advertisedHost, advertisedHostSSL)
+	if returnFunc, ok := ret.Get(1).(func(context.Context, wire.FLAPSignonFrame, func(screenName state.DisplayScreenName) (state.User, error), string) error); ok {
+		r1 = returnFunc(ctx, inFrame, newUserFn, advertisedHost)
 	} else {
 		r1 = ret.Error(1)
 	}
@@ -292,12 +286,11 @@ type mockAuthService_FLAPLogin_Call struct {
 //   - inFrame wire.FLAPSignonFrame
 //   - newUserFn func(screenName state.DisplayScreenName) (state.User, error)
 //   - advertisedHost string
-//   - advertisedHostSSL string
-func (_e *mockAuthService_Expecter) FLAPLogin(ctx interface{}, inFrame interface{}, newUserFn interface{}, advertisedHost interface{}, advertisedHostSSL interface{}) *mockAuthService_FLAPLogin_Call {
-	return &mockAuthService_FLAPLogin_Call{Call: _e.mock.On("FLAPLogin", ctx, inFrame, newUserFn, advertisedHost, advertisedHostSSL)}
+func (_e *mockAuthService_Expecter) FLAPLogin(ctx interface{}, inFrame interface{}, newUserFn interface{}, advertisedHost interface{}) *mockAuthService_FLAPLogin_Call {
+	return &mockAuthService_FLAPLogin_Call{Call: _e.mock.On("FLAPLogin", ctx, inFrame, newUserFn, advertisedHost)}
 }
 
-func (_c *mockAuthService_FLAPLogin_Call) Run(run func(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string)) *mockAuthService_FLAPLogin_Call {
+func (_c *mockAuthService_FLAPLogin_Call) Run(run func(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string)) *mockAuthService_FLAPLogin_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		var arg0 context.Context
 		if args[0] != nil {
@@ -315,16 +308,11 @@ func (_c *mockAuthService_FLAPLogin_Call) Run(run func(ctx context.Context, inFr
 		if args[3] != nil {
 			arg3 = args[3].(string)
 		}
-		var arg4 string
-		if args[4] != nil {
-			arg4 = args[4].(string)
-		}
 		run(
 			arg0,
 			arg1,
 			arg2,
 			arg3,
-			arg4,
 		)
 	})
 	return _c
@@ -335,7 +323,7 @@ func (_c *mockAuthService_FLAPLogin_Call) Return(tLVRestBlock wire.TLVRestBlock,
 	return _c
 }
 
-func (_c *mockAuthService_FLAPLogin_Call) RunAndReturn(run func(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.TLVRestBlock, error)) *mockAuthService_FLAPLogin_Call {
+func (_c *mockAuthService_FLAPLogin_Call) RunAndReturn(run func(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.TLVRestBlock, error)) *mockAuthService_FLAPLogin_Call {
 	_c.Call.Return(run)
 	return _c
 }

+ 2 - 2
server/toc/types.go

@@ -44,9 +44,9 @@ type OServiceService interface {
 
 type AuthService interface {
 	BUCPChallenge(ctx context.Context, inBody wire.SNAC_0x17_0x06_BUCPChallengeRequest, newUUID func() uuid.UUID) (wire.SNACMessage, error)
-	BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error)
+	BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error)
 	CrackCookie(authCookie []byte) (state.ServerCookie, error)
-	FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.TLVRestBlock, error)
+	FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.TLVRestBlock, error)
 	RegisterBOSSession(ctx context.Context, authCookie state.ServerCookie) (*state.SessionInstance, error)
 	RegisterChatSession(ctx context.Context, authCookie state.ServerCookie) (*state.SessionInstance, error)
 	RetrieveBOSSession(ctx context.Context, authCookie state.ServerCookie) (*state.SessionInstance, error)

+ 1 - 1
server/webapi/handlers/session.go

@@ -33,7 +33,7 @@ type SessionHandler struct {
 // AuthService defines methods needed for authentication.
 type AuthService interface {
 	BUCPChallenge(ctx context.Context, bodyIn wire.SNAC_0x17_0x06_BUCPChallengeRequest, newUUID func() uuid.UUID) (wire.SNACMessage, error)
-	BUCPLogin(ctx context.Context, bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error)
+	BUCPLogin(ctx context.Context, bodyIn wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error)
 	RegisterBOSSession(ctx context.Context, authCookie state.ServerCookie) (*state.SessionInstance, error)
 }
 

+ 2 - 2
server/webapi/types.go

@@ -45,9 +45,9 @@ type OServiceService interface {
 
 type AuthService interface {
 	BUCPChallenge(ctx context.Context, inBody wire.SNAC_0x17_0x06_BUCPChallengeRequest, newUUID func() uuid.UUID) (wire.SNACMessage, error)
-	BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.SNACMessage, error)
+	BUCPLogin(ctx context.Context, inBody wire.SNAC_0x17_0x02_BUCPLoginRequest, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.SNACMessage, error)
 	CrackCookie(authCookie []byte) (state.ServerCookie, error)
-	FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string, advertisedHostSSL string) (wire.TLVRestBlock, error)
+	FLAPLogin(ctx context.Context, inFrame wire.FLAPSignonFrame, newUserFn func(screenName state.DisplayScreenName) (state.User, error), advertisedHost string) (wire.TLVRestBlock, error)
 	RegisterBOSSession(ctx context.Context, authCookie state.ServerCookie) (*state.SessionInstance, error)
 	RegisterChatSession(ctx context.Context, authCookie state.ServerCookie) (*state.SessionInstance, error)
 	RetrieveBOSSession(ctx context.Context, authCookie state.ServerCookie) (*state.SessionInstance, error)

+ 0 - 47
wire/snacs.go

@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
-	"strings"
 
 	"github.com/google/uuid"
 )
@@ -930,52 +929,6 @@ func MarshalICBMFragmentList(frags []ICBMCh1Fragment) ([]byte, error) {
 	return buf.Bytes(), nil
 }
 
-// StripHTML removes HTML tags and converts HTML entities to plain text.
-func StripHTML(text []byte) []byte {
-	if len(text) == 0 {
-		return text
-	}
-
-	s := string(text)
-
-	// Convert <BR> tags to newlines (case-insensitive)
-	s = strings.ReplaceAll(s, "<BR>", "\n")
-	s = strings.ReplaceAll(s, "<br>", "\n")
-	s = strings.ReplaceAll(s, "<br/>", "\n")
-	s = strings.ReplaceAll(s, "<br />", "\n")
-
-	// Strip all HTML tags
-	var result strings.Builder
-	result.Grow(len(s))
-	inTag := false
-
-	for i := 0; i < len(s); i++ {
-		if s[i] == '<' {
-			inTag = true
-			continue
-		}
-		if s[i] == '>' {
-			inTag = false
-			continue
-		}
-		if !inTag {
-			result.WriteByte(s[i])
-		}
-	}
-
-	s = result.String()
-
-	// Convert common HTML entities
-	s = strings.ReplaceAll(s, "&lt;", "<")
-	s = strings.ReplaceAll(s, "&gt;", ">")
-	s = strings.ReplaceAll(s, "&amp;", "&")
-	s = strings.ReplaceAll(s, "&quot;", "\"")
-	s = strings.ReplaceAll(s, "&nbsp;", " ")
-	s = strings.ReplaceAll(s, "&apos;", "'")
-
-	return []byte(s)
-}
-
 type SNAC_0x04_0x08_ICBMEvilRequest struct {
 	SendAs     uint16
 	ScreenName string `oscar:"len_prefix=uint8"`

+ 53 - 125
wire/snacs_test.go

@@ -149,131 +149,6 @@ func TestUnmarshalChatMessageText(t *testing.T) {
 	}
 }
 
-func TestCapabilityUUIDs(t *testing.T) {
-	tests := []struct {
-		name     string
-		cap      uuid.UUID
-		expected string
-	}{
-		// Original capabilities
-		{"CapChat", CapChat, "748f2420-6287-11d1-8222-444553540000"},
-		{"CapFileTransfer", CapFileTransfer, "09461343-4c7f-11d1-8222-444553540000"},
-
-		// Short caps
-		{"CapShortCaps", CapShortCaps, "09460000-4c7f-11d1-8222-444553540000"},
-		{"CapSecureIM", CapSecureIM, "09460001-4c7f-11d1-8222-444553540000"},
-		{"CapXHTMLIM", CapXHTMLIM, "09460002-4c7f-11d1-8222-444553540000"},
-		{"CapRTCVideo", CapRTCVideo, "09460101-4c7f-11d1-8222-444553540000"},
-		{"CapHasCamera", CapHasCamera, "09460102-4c7f-11d1-8222-444553540000"},
-		{"CapHasMicrophone", CapHasMicrophone, "09460103-4c7f-11d1-8222-444553540000"},
-		{"CapRTCAudio", CapRTCAudio, "09460104-4c7f-11d1-8222-444553540000"},
-		{"CapHostStatusTextAware", CapHostStatusTextAware, "0946010a-4c7f-11d1-8222-444553540000"},
-		{"CapRTIM", CapRTIM, "0946010b-4c7f-11d1-8222-444553540000"},
-		{"CapSmartCaps", CapSmartCaps, "094601ff-4c7f-11d1-8222-444553540000"},
-		{"CapVoiceChat", CapVoiceChat, "09461341-4c7f-11d1-8222-444553540000"},
-		{"CapDirectPlay", CapDirectPlay, "09461342-4c7f-11d1-8222-444553540000"},
-		{"CapRouteFinder", CapRouteFinder, "09461344-4c7f-11d1-8222-444553540000"},
-		{"CapDirectICBM", CapDirectICBM, "09461345-4c7f-11d1-8222-444553540000"},
-		{"CapAvatarService", CapAvatarService, "09461346-4c7f-11d1-8222-444553540000"},
-		{"CapStocksAddins", CapStocksAddins, "09461347-4c7f-11d1-8222-444553540000"},
-		{"CapFileSharing", CapFileSharing, "09461348-4c7f-11d1-8222-444553540000"},
-		{"CapICQCh2Extended", CapICQCh2Extended, "09461349-4c7f-11d1-8222-444553540000"},
-		{"CapGames", CapGames, "0946134a-4c7f-11d1-8222-444553540000"},
-		{"CapBuddyListTransfer", CapBuddyListTransfer, "0946134b-4c7f-11d1-8222-444553540000"},
-		{"CapSupportICQ", CapSupportICQ, "0946134d-4c7f-11d1-8222-444553540000"},
-		{"CapUTF8Messages", CapUTF8Messages, "0946134e-4c7f-11d1-8222-444553540000"},
-
-		// Full UUIDs
-		{"CapRTFMessages", CapRTFMessages, "97b12751-243c-4334-ad22-d6abf73f1492"},
-		{"CapTrillianSecureIM", CapTrillianSecureIM, "f2e7c7f4-fead-4dfb-b235-36798bdf0000"},
-		{"CapXtrazScript", CapXtrazScript, "3b60b3ef-d82a-6c45-a4e0-9c5a5e67e865"},
-
-		// Unknown/Legacy
-		{"CapUnknownICQ2001_2002", CapUnknownICQ2001_2002, "a0e93f37-4c7f-11d1-8222-444553540000"},
-		{"CapUnknownICQ2002", CapUnknownICQ2002, "10cf40d1-4c7f-11d1-8222-444553540000"},
-		{"CapUnknownICQ2001", CapUnknownICQ2001, "2e7a6475-fadf-4dc8-886f-ea3595fdb6df"},
-		{"CapUnknownICQLite", CapUnknownICQLite, "563fc809-0b6f-41bd-9f79-422609dfa2f3"},
-		{"CapGamesAlt", CapGamesAlt, "0946134a-4c7f-11d1-2282-444553540000"},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			assert.Equal(t, tt.expected, tt.cap.String())
-		})
-	}
-}
-
-func TestStripHTML(t *testing.T) {
-	tests := []struct {
-		name     string
-		input    []byte
-		expected []byte
-	}{
-		{
-			name:     "simple HTML tags",
-			input:    []byte("<HTML><BODY>Hello World</BODY></HTML>"),
-			expected: []byte("Hello World"),
-		},
-		{
-			name:     "BR tag to newline",
-			input:    []byte("Line 1<BR>Line 2"),
-			expected: []byte("Line 1\nLine 2"),
-		},
-		{
-			name:     "lowercase br tag",
-			input:    []byte("Line 1<br>Line 2"),
-			expected: []byte("Line 1\nLine 2"),
-		},
-		{
-			name:     "self-closing br tag",
-			input:    []byte("Line 1<br/>Line 2"),
-			expected: []byte("Line 1\nLine 2"),
-		},
-		{
-			name:     "HTML entities",
-			input:    []byte("&lt;test&gt; &amp; &quot;quoted&quot;"),
-			expected: []byte("<test> & \"quoted\""),
-		},
-		{
-			name:     "FONT tags with attributes",
-			input:    []byte(`<FONT FACE="Arial" SIZE="3">Text</FONT>`),
-			expected: []byte("Text"),
-		},
-		{
-			name:     "mixed formatting",
-			input:    []byte("<HTML><BODY><B>Bold</B> <I>Italic</I></BODY></HTML>"),
-			expected: []byte("Bold Italic"),
-		},
-		{
-			name:     "plain text unchanged",
-			input:    []byte("Hello World"),
-			expected: []byte("Hello World"),
-		},
-		{
-			name:     "empty input",
-			input:    []byte(""),
-			expected: []byte(""),
-		},
-		{
-			name:     "nbsp entity",
-			input:    []byte("Hello&nbsp;World"),
-			expected: []byte("Hello World"),
-		},
-		{
-			name:     "realistic HTML message",
-			input:    []byte(`<HTML><BODY dir="ltr"><FONT color="#000000" size="2" face="Arial">Hello</FONT></BODY></HTML>`),
-			expected: []byte("Hello"),
-		},
-		{
-			name:     "apos entity",
-			input:    []byte("it&apos;s working"),
-			expected: []byte("it's working"),
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result := StripHTML(tt.input)
-			assert.Equal(t, tt.expected, result)
 func TestTLVUserInfo_IsAway(t *testing.T) {
 	tests := []struct {
 		name     string
@@ -363,3 +238,56 @@ func TestTLVUserInfo_IsInvisible(t *testing.T) {
 		})
 	}
 }
+
+func TestCapabilityUUIDs(t *testing.T) {
+	tests := []struct {
+		name     string
+		cap      uuid.UUID
+		expected string
+	}{
+		// Original capabilities
+		{"CapChat", CapChat, "748f2420-6287-11d1-8222-444553540000"},
+		{"CapFileTransfer", CapFileTransfer, "09461343-4c7f-11d1-8222-444553540000"},
+
+		// Short caps
+		{"CapShortCaps", CapShortCaps, "09460000-4c7f-11d1-8222-444553540000"},
+		{"CapSecureIM", CapSecureIM, "09460001-4c7f-11d1-8222-444553540000"},
+		{"CapXHTMLIM", CapXHTMLIM, "09460002-4c7f-11d1-8222-444553540000"},
+		{"CapRTCVideo", CapRTCVideo, "09460101-4c7f-11d1-8222-444553540000"},
+		{"CapHasCamera", CapHasCamera, "09460102-4c7f-11d1-8222-444553540000"},
+		{"CapHasMicrophone", CapHasMicrophone, "09460103-4c7f-11d1-8222-444553540000"},
+		{"CapRTCAudio", CapRTCAudio, "09460104-4c7f-11d1-8222-444553540000"},
+		{"CapHostStatusTextAware", CapHostStatusTextAware, "0946010a-4c7f-11d1-8222-444553540000"},
+		{"CapRTIM", CapRTIM, "0946010b-4c7f-11d1-8222-444553540000"},
+		{"CapSmartCaps", CapSmartCaps, "094601ff-4c7f-11d1-8222-444553540000"},
+		{"CapVoiceChat", CapVoiceChat, "09461341-4c7f-11d1-8222-444553540000"},
+		{"CapDirectPlay", CapDirectPlay, "09461342-4c7f-11d1-8222-444553540000"},
+		{"CapRouteFinder", CapRouteFinder, "09461344-4c7f-11d1-8222-444553540000"},
+		{"CapDirectICBM", CapDirectICBM, "09461345-4c7f-11d1-8222-444553540000"},
+		{"CapAvatarService", CapAvatarService, "09461346-4c7f-11d1-8222-444553540000"},
+		{"CapStocksAddins", CapStocksAddins, "09461347-4c7f-11d1-8222-444553540000"},
+		{"CapFileSharing", CapFileSharing, "09461348-4c7f-11d1-8222-444553540000"},
+		{"CapICQCh2Extended", CapICQCh2Extended, "09461349-4c7f-11d1-8222-444553540000"},
+		{"CapGames", CapGames, "0946134a-4c7f-11d1-8222-444553540000"},
+		{"CapBuddyListTransfer", CapBuddyListTransfer, "0946134b-4c7f-11d1-8222-444553540000"},
+		{"CapSupportICQ", CapSupportICQ, "0946134d-4c7f-11d1-8222-444553540000"},
+		{"CapUTF8Messages", CapUTF8Messages, "0946134e-4c7f-11d1-8222-444553540000"},
+
+		// Full UUIDs
+		{"CapRTFMessages", CapRTFMessages, "97b12751-243c-4334-ad22-d6abf73f1492"},
+		{"CapTrillianSecureIM", CapTrillianSecureIM, "f2e7c7f4-fead-4dfb-b235-36798bdf0000"},
+		{"CapXtrazScript", CapXtrazScript, "3b60b3ef-d82a-6c45-a4e0-9c5a5e67e865"},
+
+		// Unknown/Legacy
+		{"CapUnknownICQ2001_2002", CapUnknownICQ2001_2002, "a0e93f37-4c7f-11d1-8222-444553540000"},
+		{"CapUnknownICQ2002", CapUnknownICQ2002, "10cf40d1-4c7f-11d1-8222-444553540000"},
+		{"CapUnknownICQ2001", CapUnknownICQ2001, "2e7a6475-fadf-4dc8-886f-ea3595fdb6df"},
+		{"CapUnknownICQLite", CapUnknownICQLite, "563fc809-0b6f-41bd-9f79-422609dfa2f3"},
+		{"CapGamesAlt", CapGamesAlt, "0946134a-4c7f-11d1-2282-444553540000"},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			assert.Equal(t, tt.expected, tt.cap.String())
+		})
+	}
+}