Commit: 750fd099ba80b548cf374f87dffd7cb62af4c2d2
Parent: b78abfa9018f1bc9ae0caaf2d130f950082d42a8
Author: Vi Grey
Date: 2023-08-18 07:33 UTC
Summary: Remove live RSS functionality
CHANGELOG.md | 11 +++++++
src/config.go | 9 ------
src/gemini.go | 29 +++++++++---------
src/gopher.go | 39 ++++++++++---------------
src/http.go | 62 ++++++++++++++++++---------------------
src/init.go | 26 -----------------
src/mime.go | 6 ++++
src/rss.go | 126 ++++++++++---------------------------------------------------------------------
8 files changed, 89 insertions(+), 219 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6a30248..78a8c96 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+## [0.0.24] - 2023-08-18
+
+### Added
+
+- Ability to use static .rss or .atom file
+
+### Removed
+
+- RSS generation code
+
+
## [0.0.23] - 2023-08-16
### Changed
diff --git a/src/config.go b/src/config.go
index 06ff5f2..51077f5 100644
--- a/src/config.go
+++ b/src/config.go
@@ -21,7 +21,6 @@ type Config struct {
Gemini ConfigGemini `yaml:"gemini"`
HTTP ConfigHTTP `yaml:"http"`
Gopher ConfigGopher `yaml:"gopher"`
- RSS ConfigRSS `yaml:"rss"`
Finger ConfigFinger `yaml:"finger"`
}
@@ -85,14 +84,6 @@ type ConfigGopherTor struct {
ListeningLocation string `yaml:"listening_location"`
}
-type ConfigRSS struct {
- Enabled bool `yaml:"enabled"`
- FeedSourceGeminiPath string `yaml:"feed_source_gemini_path"`
- MaxEntries int `yaml:"max_entries"`
- Copyright string `yaml:"copyright"`
- Description string `yaml:"description"`
-}
-
type ConfigFinger struct {
Enabled bool `yaml:"enabled"`
ListeningLocation string `yaml:"listening_location"`
diff --git a/src/gemini.go b/src/gemini.go
index f74a8c5..0cd522b 100644
--- a/src/gemini.go
+++ b/src/gemini.go
@@ -144,27 +144,24 @@ func handleGeminiResponseBody(conn net.Conn, urlPath, host string) {
urlExtension := filepath.Ext(urlPath)
gmiFilePath, _ := gemtextFileExists(urlPath)
geminiDataPath := safeJoinPath(configData.Gemini.DataPath, gmiFilePath)
- if !isRSSFeed(urlPath) {
- mimeType := "text/gemini"
- content, exists := getGemtextContent(geminiDataPath)
- if exists {
- err := sendGeminiResponseHeader(conn, STATUS_SUCCESS, mimeType)
- if err == nil {
- conn.Write(content)
- }
+ mimeType := "text/gemini"
+ content, exists := getGemtextContent(geminiDataPath)
+ if exists {
+ err := sendGeminiResponseHeader(conn, STATUS_SUCCESS, mimeType)
+ if err == nil {
+ conn.Write(content)
+ }
+ } else if isRSSFeed(urlPath) {
+ handleGeminiServeFile(conn, geminiDataPath)
+ } else {
+ rssFilePath, isRSSFile := rssFileExists(urlPath, false, true)
+ if isRSSFile {
+ handleGeminiServeFile(conn, rssFilePath)
} else if urlExtension == "" {
sendGeminiResponseHeader(conn, STATUS_NOT_FOUND, "Page Not Found")
} else {
handleGeminiServeFile(conn, geminiDataPath)
}
- } else {
- mimeType := "application/rss+xml"
- if sendGeminiResponseHeader(conn, STATUS_SUCCESS, mimeType) != nil {
- return
- }
-
- buf := createRSSFeed("gemini://" + host)
- io.Copy(conn, buf)
}
}
diff --git a/src/gopher.go b/src/gopher.go
index a767c3e..b490252 100644
--- a/src/gopher.go
+++ b/src/gopher.go
@@ -50,6 +50,8 @@ func handleGopherConnection(conn net.Conn, tor bool) {
reqPath = path.Clean(reqPath)
gmiFilePath, gmiFileExists := gemtextFileExists(reqPath)
geminiDataPath := safeJoinPath(configData.Gemini.DataPath, gmiFilePath)
+ rssFilePath, isRSSFile := rssFileExists(reqPath, false, true)
+ rssDataPath := safeJoinPath(configData.Gemini.DataPath, rssFilePath)
gopherHostname := "localhost"
gopherPort := strconv.Itoa(configData.Gopher.Port)
if tor {
@@ -66,31 +68,21 @@ func handleGopherConnection(conn net.Conn, tor bool) {
gopherContentError := []byte("3The selected resource does not exist\t" +
"\t" + gopherHostname + "\t" + gopherPort + "\r\n.\r\n")
- if (!gmiFileExists && filepath.Ext(reqPath) != "") || isRSSFeed(reqPath) {
+ if (!gmiFileExists && filepath.Ext(reqPath) != "") || isRSSFile {
+ contentPath := geminiDataPath
// file is not a gemtext file
- if isRSSFeed(reqPath) {
- // RSS feed
- rssFeedURL := fmt.Sprintf("gopher://%s", gopherHostname)
- if gopherPort != strconv.Itoa(GOPHER_DEFAULT_PORT) {
- rssFeedURL += fmt.Sprintf(":%s", gopherPort)
- }
-
- buf := createRSSFeed(rssFeedURL + "/1/")
- io.Copy(conn, buf)
-
+ if isRSSFile {
+ contentPath = rssDataPath
+ }
+ f, err := os.Open(contentPath)
+ if err == nil {
+ defer f.Close()
+ io.Copy(conn, f)
break
} else {
- // is a file that is not an RSS feed or gemtext file
- f, err := os.Open(geminiDataPath)
- if err == nil {
- defer f.Close()
- io.Copy(conn, f)
- break
- } else {
- // Error or file doesn't exist
- conn.Write(gopherContentError)
- break
- }
+ // Error or file doesn't exist
+ conn.Write(gopherContentError)
+ break
}
} else {
gmiContent, exists := getGemtextContent(geminiDataPath)
@@ -289,7 +281,8 @@ func translateGemtextToGopher(gmi, gopherHostname, gopherPort, gmiDirPath string
gLine.port = port
gLine.displayString = g.text
gLine.selector = urlData.Path
- if urlData.Scheme == "" && isRSSFeed(g.path) {
+ _, isRSSFile := rssFileExists(g.path, false, true)
+ if urlData.Scheme == "" && (isRSSFeed(g.path) || isRSSFile) {
gLine.itemType = "0"
}
gopherLines = append(gopherLines, gLine)
diff --git a/src/http.go b/src/http.go
index 76a455c..8f06311 100644
--- a/src/http.go
+++ b/src/http.go
@@ -54,46 +54,40 @@ func htmlFileExists(resourcePath string) (resourceExtensionPath string, exists b
func catchAll(w http.ResponseWriter, r *http.Request) {
u := r.URL.Path
urlExtension := filepath.Ext(u)
- urlIsRSSFeed := isRSSFeed(u)
- if !urlIsRSSFeed {
- htmlFilePath, htmlFExists := htmlFileExists(u)
- if htmlFExists {
- handleHTTPFile(w, r, htmlFilePath)
- return
+ htmlFilePath, htmlFExists := htmlFileExists(u)
+ if htmlFExists {
+ handleHTTPFile(w, r, htmlFilePath)
+ return
+ }
+ // HTML file doesn't exist
+ gmiFilePath, _ := gemtextFileExists(u)
+ gemtextFilePath := safeJoinPath(configData.Gemini.DataPath, gmiFilePath)
+ textOnly := configData.HTTP.TextOnly && textSubdomain(r.Host)
+ content, pageTitle, exists := geminiToHTMLFileContent(gemtextFilePath, textOnly)
+ if exists {
+ w.Header().Set("content-type", getMIMEType(".html"))
+ w.WriteHeader(http.StatusOK)
+ layoutPath := configData.HTTP.LayoutHTMLPath
+ if textOnly {
+ layoutPath = configData.HTTP.LayoutTextOnlyHTMLPath
}
- // HTML file doesn't exist
- gmiFilePath, _ := gemtextFileExists(u)
- gemtextFilePath := safeJoinPath(configData.Gemini.DataPath, gmiFilePath)
- textOnly := configData.HTTP.TextOnly && textSubdomain(r.Host)
- content, pageTitle, exists := geminiToHTMLFileContent(gemtextFilePath, textOnly)
- if exists {
- w.Header().Set("content-type", getMIMEType(".html"))
- w.WriteHeader(http.StatusOK)
- layoutPath := configData.HTTP.LayoutHTMLPath
- if textOnly {
- layoutPath = configData.HTTP.LayoutTextOnlyHTMLPath
- }
- htmlLayoutContent, err := os.ReadFile(layoutPath)
- handleErr(err, fmt.Sprintf("Unable to read Layout HTML file %s\n", layoutPath))
- htmlLayoutContent = bytes.ReplaceAll(htmlLayoutContent, titleToken, pageTitle)
- htmlLayoutContent = bytes.ReplaceAll(htmlLayoutContent, geminiContentToken, bytes.ReplaceAll(content, []byte("$"), []byte("$$")))
- htmlLayoutContent = bytes.ReplaceAll(htmlLayoutContent, pagePathToken, []byte(u))
- w.Write(htmlLayoutContent)
+ htmlLayoutContent, err := os.ReadFile(layoutPath)
+ handleErr(err, fmt.Sprintf("Unable to read Layout HTML file %s\n", layoutPath))
+ htmlLayoutContent = bytes.ReplaceAll(htmlLayoutContent, titleToken, pageTitle)
+ htmlLayoutContent = bytes.ReplaceAll(htmlLayoutContent, geminiContentToken, bytes.ReplaceAll(content, []byte("$"), []byte("$$")))
+ htmlLayoutContent = bytes.ReplaceAll(htmlLayoutContent, pagePathToken, []byte(u))
+ w.Write(htmlLayoutContent)
+ } else if isRSSFeed(u) {
+ handleHTTPFile(w, r, u)
+ } else {
+ rssFilePath, isRSSFile := rssFileExists(u, true, true)
+ if isRSSFile {
+ handleHTTPFile(w, r, rssFilePath)
} else if urlExtension == "" {
w.WriteHeader(http.StatusNotFound)
} else {
handleHTTPFile(w, r, u)
}
- } else {
- w.Header().Set("content-type", "application/rss+xml")
- w.WriteHeader(http.StatusOK)
- rssHost := "http"
- if r.TLS != nil {
- rssHost += "s"
- }
- rssHost += "://" + r.Host
- buf := createRSSFeed(rssHost)
- io.Copy(w, buf)
}
}
diff --git a/src/init.go b/src/init.go
index 4215daf..7867375 100644
--- a/src/init.go
+++ b/src/init.go
@@ -170,12 +170,6 @@ func initConfigData() {
VirtualPort: GOPHER_DEFAULT_PORT,
},
},
- RSS: ConfigRSS{
- Enabled: false,
- FeedSourceGeminiPath: "/blog",
- MaxEntries: 20,
- Description: "My RSS Feed",
- },
Finger: ConfigFinger{
Enabled: false,
ServerMessageFilePath: "finger/.servermessage",
@@ -334,26 +328,6 @@ func initBergelmirProject() {
configData.HTTP.Tor.VirtualPort})
}
}
- // Ask if RSS feed should be enabled
- configData.RSS.Enabled = getUserInputYN(
- "Enable RSS feed at /rss and /feed URLs?",
- configData.RSS.Enabled)
- if configData.RSS.Enabled {
- // Ask which Gemini source path is used for the RSS feed
- configData.RSS.FeedSourceGeminiPath = getUserInputText(
- "Gemini source path for RSS feed",
- configData.RSS.FeedSourceGeminiPath)
- // Ask how many entries can be in a single RSS feed
- configData.RSS.MaxEntries = getUserInputInt(
- "Max number of RSS entries (0 means all entries will be shown)",
- 0, 2147483647, configData.RSS.MaxEntries, []int{})
- // Ask for RSS feed copyright license
- configData.RSS.Copyright = getUserInputText(
- "RSS feed copyright license (optional) (example: Creative Commons 4.0 International (CC BY 4.0) license)",
- "")
- configData.RSS.Description = getUserInputText(
- "RSS feed description", configData.RSS.Description)
- }
// Ask if Finger server should be enabled
configData.Finger.Enabled = getUserInputYN(
"Enable Finger server?", configData.Finger.Enabled)
diff --git a/src/mime.go b/src/mime.go
index 9ad795f..6283d12 100644
--- a/src/mime.go
+++ b/src/mime.go
@@ -10,6 +10,12 @@ func getMIMEType(path string) (mimeType string) {
mimeType = "application/octet-stream"
extension := strings.ToLower(filepath.Ext(path))
if len(extension) > 0 {
+ if extension == ".rss" {
+ return "application/rss+xml"
+ }
+ if extension == ".atom" {
+ return "application/atom+xml"
+ }
if extension == ".txt" {
return "text/plain;charset=UTF-8"
}
diff --git a/src/rss.go b/src/rss.go
index d3af769..417be4f 100644
--- a/src/rss.go
+++ b/src/rss.go
@@ -1,14 +1,9 @@
package main
import (
- "bytes"
- "fmt"
- "net/url"
- "path"
+ "os"
"regexp"
- "sort"
"strings"
- "time"
)
var (
@@ -24,117 +19,26 @@ type feedEntry struct {
// Check if rss value in config is enabled and if u is /feed or /rss
func isRSSFeed(u string) bool {
- return (configData.RSS.Enabled && (u == "/feed" || u == "/rss"))
+ mimeType := getMIMEType(u)
+ return (strings.Contains(mimeType, "rss") || strings.Contains(mimeType, "atom"))
}
-func createRSSFeed(host string) *bytes.Buffer {
- gmiFilePath, _ := gemtextFileExists(configData.RSS.FeedSourceGeminiPath)
- rssGeminiDataPath := safeJoinPath(configData.Gemini.DataPath, gmiFilePath)
- gmiContent, exists := getGemtextContent(rssGeminiDataPath)
- if exists {
- return translateGemtextToRSS(string(gmiContent), host)
- }
- return bytes.NewBufferString("")
-}
-
-func translateGemtextToRSS(gmi, host string) *bytes.Buffer {
- rssFeedBuffer := new(bytes.Buffer)
- rssFeedURL := joinPath(host,
- configData.RSS.FeedSourceGeminiPath)
- rssFeedEntries := []feedEntry{}
- feedTitle := ""
- gmi = strings.ReplaceAll(gmi, "\r\n", "\n")
- gmiLines := strings.Split(gmi, "\n")
- preformattedToggle := false
- feedTitleSet := false
- for _, line := range gmiLines {
- g := parseGemtextLine(line, preformattedToggle)
- switch g.lineType {
- case GEMTEXT_HEADING:
- if g.level == 1 && !feedTitleSet {
- feedTitle = escapeHTMLQuotes(escapeHTMLContent(g.text))
- }
- case GEMTEXT_PREFORMATTED_TOGGLE:
- preformattedToggle = !preformattedToggle
- case GEMTEXT_LINK:
- entry, valid := parseTextToFeedEntry(g.text)
- if valid {
- gemtextPathURL, _ := url.Parse(g.path)
- if !gemtextPathURL.IsAbs() {
- entry.link = joinPath(host, g.path)
- } else {
- entry.link = g.path
- }
- entry.path = g.path
- rssFeedEntries = append(rssFeedEntries, entry)
+func rssFileExists(resourcePath string, http, gemini bool) (resourceExtensionPath string, exists bool) {
+ rssExtensions := []string{".rss", ".atom", ".RSS", ".ATOM"}
+ // Check for resourcePath.(rss/atom)
+ for _, extension := range rssExtensions {
+ if http {
+ if _, err := os.Stat(safeJoinPath(configData.HTTP.DataPath,
+ resourcePath+extension)); err == nil {
+ return resourcePath + extension, true
}
}
- }
- sort.Slice(rssFeedEntries, func(i, j int) bool {
- return rssFeedEntries[i].pubDate > rssFeedEntries[j].pubDate
- })
- if len(rssFeedEntries) > 0 {
- latestFeedPubDate, _ := time.Parse(time.RFC3339, rssFeedEntries[0].pubDate)
- latestPubDate := latestFeedPubDate.Format(time.RFC1123Z)
- rssChannelDescription := "My Site's RSS Feed"
- if configData.RSS.Description != "" {
- rssChannelDescription = configData.RSS.Description
- }
- rssFeedBuffer.WriteString(fmt.Sprintf(
- "\n"+
- "
-- Leo's gemini proxy
-- Connecting to vigrey.com:1965...
-- Connected
-- Sending request
-- Meta line: 20 text/plain; charset=UTF-8
-- Response ended
-- Page fetched on Mon May 20 15:25:02 2024