2017-11-20 06:10:04 +01:00
|
|
|
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
|
|
|
// Use of this source code is governed by the Apache 2.0
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package feed
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/xml"
|
|
|
|
"errors"
|
2017-11-21 03:34:11 +01:00
|
|
|
"io"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2017-11-20 06:10:04 +01:00
|
|
|
"github.com/miniflux/miniflux2/helper"
|
|
|
|
"github.com/miniflux/miniflux2/model"
|
|
|
|
"github.com/miniflux/miniflux2/reader/feed/atom"
|
|
|
|
"github.com/miniflux/miniflux2/reader/feed/json"
|
2017-11-21 03:34:11 +01:00
|
|
|
"github.com/miniflux/miniflux2/reader/feed/rdf"
|
2017-11-20 06:10:04 +01:00
|
|
|
"github.com/miniflux/miniflux2/reader/feed/rss"
|
|
|
|
|
|
|
|
"golang.org/x/net/html/charset"
|
|
|
|
)
|
|
|
|
|
2017-11-21 03:34:11 +01:00
|
|
|
// List of feed formats.
|
2017-11-20 06:10:04 +01:00
|
|
|
const (
|
2017-11-21 03:34:11 +01:00
|
|
|
FormatRDF = "rdf"
|
|
|
|
FormatRSS = "rss"
|
2017-11-20 06:10:04 +01:00
|
|
|
FormatAtom = "atom"
|
2017-11-21 03:34:11 +01:00
|
|
|
FormatJSON = "json"
|
2017-11-20 06:10:04 +01:00
|
|
|
FormatUnknown = "unknown"
|
|
|
|
)
|
|
|
|
|
2017-11-21 03:34:11 +01:00
|
|
|
// DetectFeedFormat detect feed format from input data.
|
2017-11-20 06:10:04 +01:00
|
|
|
func DetectFeedFormat(data io.Reader) string {
|
|
|
|
defer helper.ExecutionTime(time.Now(), "[Feed:DetectFeedFormat]")
|
|
|
|
|
|
|
|
var buffer bytes.Buffer
|
|
|
|
tee := io.TeeReader(data, &buffer)
|
|
|
|
|
|
|
|
decoder := xml.NewDecoder(tee)
|
|
|
|
decoder.CharsetReader = charset.NewReaderLabel
|
|
|
|
|
|
|
|
for {
|
|
|
|
token, _ := decoder.Token()
|
|
|
|
if token == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if element, ok := token.(xml.StartElement); ok {
|
|
|
|
switch element.Name.Local {
|
|
|
|
case "rss":
|
2017-11-21 03:34:11 +01:00
|
|
|
return FormatRSS
|
2017-11-20 06:10:04 +01:00
|
|
|
case "feed":
|
|
|
|
return FormatAtom
|
2017-11-21 03:34:11 +01:00
|
|
|
case "RDF":
|
|
|
|
return FormatRDF
|
2017-11-20 06:10:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasPrefix(strings.TrimSpace(buffer.String()), "{") {
|
2017-11-21 03:34:11 +01:00
|
|
|
return FormatJSON
|
2017-11-20 06:10:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return FormatUnknown
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseFeed(data io.Reader) (*model.Feed, error) {
|
|
|
|
defer helper.ExecutionTime(time.Now(), "[Feed:ParseFeed]")
|
|
|
|
|
|
|
|
var buffer bytes.Buffer
|
|
|
|
io.Copy(&buffer, data)
|
|
|
|
|
|
|
|
reader := bytes.NewReader(buffer.Bytes())
|
|
|
|
format := DetectFeedFormat(reader)
|
|
|
|
reader.Seek(0, io.SeekStart)
|
|
|
|
|
|
|
|
switch format {
|
|
|
|
case FormatAtom:
|
|
|
|
return atom.Parse(reader)
|
2017-11-21 03:34:11 +01:00
|
|
|
case FormatRSS:
|
2017-11-20 06:10:04 +01:00
|
|
|
return rss.Parse(reader)
|
2017-11-21 03:34:11 +01:00
|
|
|
case FormatJSON:
|
2017-11-20 06:10:04 +01:00
|
|
|
return json.Parse(reader)
|
2017-11-21 03:34:11 +01:00
|
|
|
case FormatRDF:
|
|
|
|
return rdf.Parse(reader)
|
2017-11-20 06:10:04 +01:00
|
|
|
default:
|
|
|
|
return nil, errors.New("Unsupported feed format")
|
|
|
|
}
|
|
|
|
}
|