Sfoglia il codice sorgente

toc2: support comma-delimited short caps

Mike 4 mesi fa
parent
commit
569e600ebd
4 ha cambiato i file con 127 aggiunte e 8 eliminazioni
  1. 21 6
      server/toc/cmd_client.go
  2. 53 2
      server/toc/cmd_client_test.go
  3. 23 0
      wire/snacs.go
  4. 30 0
      wire/snacs_test.go

+ 21 - 6
server/toc/cmd_client.go

@@ -2032,13 +2032,28 @@ func (s OSCARProxy) SetCaps(ctx context.Context, me *state.SessionInstance, args
 		return s.runtimeErr(ctx, fmt.Errorf("parseArgs: %w", err))
 	}
 
-	caps := make([]uuid.UUID, 0, 16*(len(params)+1))
-	for _, capStr := range params {
-		uid, err := uuid.Parse(capStr)
-		if err != nil {
-			return s.runtimeErr(ctx, fmt.Errorf("UUID.Parse: %w", err))
+	// Clients may send capabilities as space-separated UUIDs or as a single
+	// comma-separated string (e.g. TameClone: "UUID,1348,134B,..."). Split each
+	// param on comma. Accept full UUIDs and OSCAR short caps (1–4 hex digits
+	// expanded to 0946XXYY-4C7F-11D1-8222-444553540000); ignore unknown tokens.
+	caps := make([]uuid.UUID, 0, 16)
+	for _, param := range params {
+		for _, token := range strings.Split(param, ",") {
+			token = strings.TrimSpace(token)
+			if token == "" {
+				continue
+			}
+			uid, err := uuid.Parse(token)
+			if err != nil {
+				uid, ok := wire.ShortCapHexToUUID(token)
+				if !ok {
+					continue
+				}
+				caps = append(caps, uid)
+				continue
+			}
+			caps = append(caps, uid)
 		}
-		caps = append(caps, uid)
 	}
 	// assume client supports chat, although we may want to do this according
 	// to client ID

+ 53 - 2
server/toc/cmd_client_test.go

@@ -5623,10 +5623,61 @@ func TestOSCARProxy_RecvClientCmd_SetCaps(t *testing.T) {
 			wantMsg: []string{cmdInternalSvcErr},
 		},
 		{
-			name:     "set malformed capability UUID",
+			name:     "set malformed capability UUID is skipped",
 			me:       newTestSession("me"),
 			givenCmd: []byte(`toc_set_caps 09460000-`),
-			wantMsg:  []string{cmdInternalSvcErr},
+			mockParams: mockParams{
+				locateParams: locateParams{
+					setInfoParams: setInfoParams{
+						{
+							me: state.NewIdentScreenName("me"),
+							inBody: wire.SNAC_0x02_0x04_LocateSetInfo{
+								TLVRestBlock: wire.TLVRestBlock{
+									TLVList: wire.TLVList{
+										wire.NewTLVBE(wire.LocateTLVTagsInfoCapabilities, []uuid.UUID{
+											wire.CapChat,
+										}),
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantMsg: []string{},
+		},
+		{
+			name:     "set capabilities with comma-separated list (TameClone format)",
+			me:       newTestSession("me"),
+			givenCmd: []byte(`toc_set_caps 748F2420-6287-11D1-8222-444553540000,1348,134B,1341,1343,1FF,1345,1346,1347,`),
+			mockParams: mockParams{
+				locateParams: locateParams{
+					setInfoParams: setInfoParams{
+						{
+							me: state.NewIdentScreenName("me"),
+							inBody: wire.SNAC_0x02_0x04_LocateSetInfo{
+								TLVRestBlock: wire.TLVRestBlock{
+									TLVList: wire.TLVList{
+										wire.NewTLVBE(wire.LocateTLVTagsInfoCapabilities, []uuid.UUID{
+											wire.CapChat,              // 748F... from client
+											wire.CapFileSharing,       // 1348
+											wire.CapBuddyListTransfer, // 134B
+											wire.CapVoiceChat,         // 1341
+											wire.CapFileTransfer,      // 1343
+											wire.CapSmartCaps,         // 1FF
+											wire.CapDirectICBM,        // 1345
+											wire.CapAvatarService,     // 1346
+											wire.CapStocksAddins,      // 1347
+											wire.CapChat,              // auto-appended
+										}),
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantMsg: []string{},
 		},
 	}
 

+ 23 - 0
wire/snacs.go

@@ -4,6 +4,8 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
+	"strconv"
+	"strings"
 
 	"github.com/google/uuid"
 )
@@ -219,6 +221,27 @@ var (
 	CapGamesAlt = uuid.MustParse("0946134A-4C7F-11D1-2282-444553540000")
 )
 
+// ShortCapHexToUUID converts an OSCAR short capability code (1–4 hex digits, e.g. "1348", "1FF")
+// to the full UUID form 0946XXYY-4C7F-11D1-8222-444553540000. Returns the UUID and true if the
+// input is valid hex that fits in a uint16; otherwise returns zero UUID and false.
+func ShortCapHexToUUID(hexStr string) (uuid.UUID, bool) {
+	hexStr = strings.TrimSpace(hexStr)
+	if hexStr == "" || len(hexStr) > 4 {
+		return uuid.Nil, false
+	}
+	val, err := strconv.ParseUint(hexStr, 16, 16)
+	if err != nil {
+		return uuid.Nil, false
+	}
+	// Format: 0946XXYY-4C7F-11D1-8222-444553540000 (XXYY = 16-bit short cap in big-endian hex)
+	uuidStr := fmt.Sprintf("0946%04X-4C7F-11D1-8222-444553540000", val)
+	uid, err := uuid.Parse(uuidStr)
+	if err != nil {
+		return uuid.Nil, false
+	}
+	return uid, true
+}
+
 //
 // 0x01: OService
 //

+ 30 - 0
wire/snacs_test.go

@@ -292,6 +292,36 @@ func TestCapabilityUUIDs(t *testing.T) {
 	}
 }
 
+func TestShortCapHexToUUID(t *testing.T) {
+	tests := []struct {
+		name     string
+		hexStr   string
+		wantUUID uuid.UUID
+		wantOK   bool
+	}{
+		{"1348 -> CapFileSharing", "1348", CapFileSharing, true},
+		{"134B -> CapBuddyListTransfer", "134B", CapBuddyListTransfer, true},
+		{"1FF -> CapSmartCaps", "1FF", CapSmartCaps, true},
+		{"1341 -> CapVoiceChat", "1341", CapVoiceChat, true},
+		{"0000 -> CapShortCaps", "0000", CapShortCaps, true},
+		{"empty string", "", uuid.Nil, false},
+		{"invalid hex", "GGGG", uuid.Nil, false},
+		{"too large for uint16", "10000", uuid.Nil, false},
+		{"whitespace trimmed", "  1348  ", CapFileSharing, true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, ok := ShortCapHexToUUID(tt.hexStr)
+			assert.Equal(t, tt.wantOK, ok)
+			if tt.wantOK {
+				assert.Equal(t, tt.wantUUID, got)
+			} else {
+				assert.Equal(t, uuid.Nil, got)
+			}
+		})
+	}
+}
+
 func TestFeedbagItem_AppendOrderMembers(t *testing.T) {
 	t.Run("creates order TLV when none exists", func(t *testing.T) {
 		item := FeedbagItem{ClassID: FeedbagClassIdGroup, GroupID: 1}