diff --git a/internal/googlereader/handler.go b/internal/googlereader/handler.go index 09f66ae7..8e280363 100644 --- a/internal/googlereader/handler.go +++ b/internal/googlereader/handler.go @@ -292,7 +292,7 @@ func (h *handler) editTagHandler(w http.ResponseWriter, r *http.Request) { itemIDs, err := parseItemIDsFromRequest(r) if err != nil { - json.ServerError(w, r, err) + json.BadRequest(w, r, err) return } @@ -709,7 +709,7 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque itemIDs, err := parseItemIDsFromRequest(r) if err != nil { - json.ServerError(w, r, err) + json.BadRequest(w, r, err) return } @@ -733,22 +733,11 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque return } - if len(entries) == 0 { - json.BadRequest(w, r, fmt.Errorf("googlereader: no items returned from the database for item IDs: %v", itemIDs)) - return - } - result := streamContentItems{ Direction: "ltr", - ID: fmt.Sprintf("feed/%d", entries[0].FeedID), - Title: entries[0].Feed.Title, - Alternate: []contentHREFType{ - { - HREF: entries[0].Feed.SiteURL, - Type: "text/html", - }, - }, - Updated: time.Now().Unix(), + ID: "user/-/state/com.google/reading-list", + Title: "Reading List", + Updated: time.Now().Unix(), Self: []contentHREF{ { HREF: config.Opts.RootURL() + route.Path(h.router, "StreamItemsContents"), diff --git a/internal/googlereader/item.go b/internal/googlereader/item.go index fd2d5ad0..d5c14b4c 100644 --- a/internal/googlereader/item.go +++ b/internal/googlereader/item.go @@ -4,7 +4,6 @@ package googlereader // import "miniflux.app/v2/internal/googlereader" import ( - "errors" "fmt" "net/http" "strconv" @@ -13,37 +12,47 @@ import ( const ( ItemIDPrefix = "tag:google.com,2005:reader/item/" + ItemIDFormat = "tag:google.com,2005:reader/item/%016x" ) func convertEntryIDToLongFormItemID(entryID int64) string { // The entry ID is a 64-bit integer, so we need to format it as a 16-character hexadecimal string. - return ItemIDPrefix + fmt.Sprintf("%016x", entryID) + return fmt.Sprintf(ItemIDFormat, entryID) } -// parseItemID parses a Google Reader ID string. -// It supports both the long form (tag:google.com,2005:reader/item/) and the short form (). +// Expected format: "tag:google.com,2005:reader/item/00000000148b9369" (hexadecimal string with prefix and padding) +// NetNewsWire uses this format: "tag:google.com,2005:reader/item/2f2" (hexadecimal string with prefix and no padding) +// Reeder uses this format: "000000000000048c" (hexadecimal string without prefix and padding) +// Liferea uses this format: "12345" (decimal string) // It returns the parsed ID as a int64 and an error if parsing fails. func parseItemID(itemIDValue string) (int64, error) { + var itemID int64 if strings.HasPrefix(itemIDValue, ItemIDPrefix) { - hexID := strings.TrimPrefix(itemIDValue, ItemIDPrefix) - - // It's always 16 characters wide. - if len(hexID) != 16 { - return 0, errors.New("long form ID has incorrect length") - } - - parsedID, err := strconv.ParseInt(hexID, 16, 64) + n, err := fmt.Sscanf(itemIDValue, ItemIDFormat, &itemID) if err != nil { - return 0, errors.New("failed to parse long form hex ID: " + err.Error()) + return 0, fmt.Errorf("failed to parse hexadecimal item ID %s: %w", itemIDValue, err) } - return parsedID, nil - } else { - parsedID, err := strconv.ParseInt(itemIDValue, 10, 64) - if err != nil { - return 0, errors.New("failed to parse short form decimal ID: " + err.Error()) + if n != 1 { + return 0, fmt.Errorf("failed to parse hexadecimal item ID %s: expected 1 value, got %d", itemIDValue, n) } - return parsedID, nil + if itemID == 0 { + return 0, fmt.Errorf("failed to parse hexadecimal item ID %s: item ID is zero", itemIDValue) + } + return itemID, nil } + + if len(itemIDValue) == 16 { + if n, err := fmt.Sscanf(itemIDValue, "%016x", &itemID); err == nil && n == 1 { + return itemID, nil + } + } + + itemID, err := strconv.ParseInt(itemIDValue, 10, 64) + if err != nil { + return 0, fmt.Errorf("failed to parse decimal item ID %s: %w", itemIDValue, err) + } + + return itemID, nil } func parseItemIDsFromRequest(r *http.Request) ([]int64, error) { diff --git a/internal/googlereader/item_test.go b/internal/googlereader/item_test.go index 7b812263..a9426904 100644 --- a/internal/googlereader/item_test.go +++ b/internal/googlereader/item_test.go @@ -23,7 +23,10 @@ func TestConvertEntryIDToLongFormItemID(t *testing.T) { func TestParseItemIDsFromRequest(t *testing.T) { formValues := url.Values{} formValues.Add("i", "12345") - formValues.Add("i", convertEntryIDToLongFormItemID(45678)) + formValues.Add("i", "tag:google.com,2005:reader/item/00000000148b9369") + formValues.Add("i", "tag:google.com,2005:reader/item/2f2") + formValues.Add("i", "000000000000046f") + formValues.Add("i", "tag:google.com,2005:reader/item/272") request := &http.Request{ Form: formValues, @@ -34,7 +37,7 @@ func TestParseItemIDsFromRequest(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - var expected = []int64{12345, 45678} + var expected = []int64{12345, 344691561, 754, 1135, 626} if !reflect.DeepEqual(result, expected) { t.Errorf("expected %v, got %v", expected, result) } @@ -51,7 +54,7 @@ func TestParseItemIDsFromRequest(t *testing.T) { } func TestParseItemID(t *testing.T) { - // Test with long form ID + // Test with long form ID and hex ID result, err := parseItemID("tag:google.com,2005:reader/item/0000000000000001") if err != nil { t.Fatalf("unexpected error: %v", err) @@ -61,6 +64,16 @@ func TestParseItemID(t *testing.T) { t.Errorf("expected %d, got %d", expected, result) } + // Test with hexadecimal long form ID + result, err = parseItemID("0000000000000468") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + expected = int64(1128) + if result != expected { + t.Errorf("expected %d, got %d", expected, result) + } + // Test with short form ID result, err = parseItemID("12345") if err != nil { @@ -88,22 +101,4 @@ func TestParseItemID(t *testing.T) { if err == nil { t.Fatalf("expected error, got nil") } - - // Test with ID that is too short - _, err = parseItemID("tag:google.com,2005:reader/item/00000000000000") - if err == nil { - t.Fatalf("expected error, got nil") - } - - // Test with ID that is too long - _, err = parseItemID("tag:google.com,2005:reader/item/000000000000000000") - if err == nil { - t.Fatalf("expected error, got nil") - } - - // Test with ID that is not a number - _, err = parseItemID("tag:google.com,2005:reader/item/abc") - if err == nil { - t.Fatalf("expected error, got nil") - } }