257 lines
6.4 KiB
Go
257 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
// "fmt"
|
|
"strings"
|
|
"os"
|
|
"io/ioutil"
|
|
"encoding/json"
|
|
"path"
|
|
|
|
"go.deanishe.net/favicon"
|
|
|
|
"github.com/azukaar/cosmos-server/src/utils"
|
|
)
|
|
|
|
type CachedImage struct {
|
|
ContentType string
|
|
ETag string
|
|
Body []byte
|
|
}
|
|
|
|
var cache = make(map[string]CachedImage)
|
|
|
|
func sendImage(w http.ResponseWriter, image CachedImage) {
|
|
// Copy the response to the output
|
|
w.Header().Set("Content-Type", image.ContentType)
|
|
w.Header().Set("ETag", image.ETag)
|
|
w.Header().Set("Cache-Control", "max-age=86400")
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write(image.Body)
|
|
}
|
|
|
|
func sendFallback(w http.ResponseWriter) {
|
|
// Send the fallback image
|
|
pwd,_ := os.Getwd()
|
|
imgsrc := "cosmos_gray.png"
|
|
fallback, err := ioutil.ReadFile(pwd + "/" + imgsrc)
|
|
if err != nil {
|
|
utils.Error("Favicon: fallback", err)
|
|
utils.HTTPError(w, "Favicon", http.StatusInternalServerError, "FA003")
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "image/png")
|
|
w.Header().Set("Cache-Control", "max-age=5")
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write(fallback)
|
|
}
|
|
|
|
var IconCacheLock = make(chan bool, 1)
|
|
|
|
func GetFavicon(w http.ResponseWriter, req *http.Request) {
|
|
if utils.LoggedInOnly(w, req) != nil {
|
|
return
|
|
}
|
|
|
|
// get url from query string
|
|
escsiteurl := req.URL.Query().Get("q")
|
|
|
|
IconCacheLock <- true
|
|
defer func() { <-IconCacheLock }()
|
|
|
|
// URL decode
|
|
siteurl, err := url.QueryUnescape(escsiteurl)
|
|
if err != nil {
|
|
utils.Error("Favicon: URL decode", err)
|
|
utils.HTTPError(w, "URL decode", http.StatusInternalServerError, "FA002")
|
|
return
|
|
}
|
|
|
|
if(req.Method == "GET") {
|
|
utils.Log("Fetch favicon for " + siteurl)
|
|
|
|
// Check if we have the favicon in cache
|
|
if _, ok := cache[siteurl]; ok {
|
|
utils.Debug("Favicon in cache")
|
|
resp := cache[siteurl]
|
|
sendImage(w, resp)
|
|
return
|
|
}
|
|
|
|
var icons []*favicon.Icon
|
|
var defaultIcons = []*favicon.Icon{
|
|
&favicon.Icon{URL: "favicon.png", Width: 0},
|
|
&favicon.Icon{URL: "/favicon.png", Width: 0},
|
|
&favicon.Icon{URL: "favicon.ico", Width: 0},
|
|
&favicon.Icon{URL: "/favicon.ico", Width: 0},
|
|
}
|
|
|
|
// follow siteurl and check if any redirect.
|
|
respNew, err := http.Get(siteurl)
|
|
|
|
if err != nil {
|
|
utils.Error("FaviconFetch", err)
|
|
icons = append(icons, defaultIcons...)
|
|
} else {
|
|
siteurl = respNew.Request.URL.String()
|
|
icons, err = favicon.Find(siteurl)
|
|
|
|
if err != nil || len(icons) == 0 {
|
|
icons = append(icons, defaultIcons...)
|
|
} else {
|
|
// Check if icons list is missing any default values
|
|
for _, defaultIcon := range defaultIcons {
|
|
found := false
|
|
for _, icon := range icons {
|
|
if icon.URL == defaultIcon.URL {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
icons = append(icons, defaultIcon)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, icon := range icons {
|
|
if icon.Width <= 256 {
|
|
|
|
iconURL := icon.URL
|
|
u, err := url.Parse(siteurl)
|
|
if err != nil {
|
|
utils.Debug("FaviconFetch failed to parse " + err.Error())
|
|
continue
|
|
}
|
|
|
|
if !strings.HasPrefix(iconURL, "http") {
|
|
if strings.HasPrefix(iconURL, ".") {
|
|
// Relative URL starting with "."
|
|
// Resolve the relative URL based on the base URL
|
|
baseURL := u.Scheme + "://" + u.Host
|
|
iconURL = baseURL + iconURL[1:]
|
|
} else if strings.HasPrefix(iconURL, "/") {
|
|
// Relative URL starting with "/"
|
|
// Append the relative URL to the base URL
|
|
iconURL = u.Scheme + "://" + u.Host + iconURL
|
|
} else {
|
|
// Relative URL without starting dot or slash
|
|
// Construct the absolute URL based on the current page's URL path
|
|
baseURL := u.Scheme + "://" + u.Host
|
|
baseURLPath := path.Dir(u.Path)
|
|
iconURL = baseURL + baseURLPath + "/" + iconURL
|
|
}
|
|
}
|
|
|
|
utils.Debug("Favicon Trying to fetch " + iconURL)
|
|
|
|
// Fetch the favicon
|
|
resp, err := http.Get(iconURL)
|
|
if err != nil {
|
|
utils.Debug("FaviconFetch" + err.Error())
|
|
continue
|
|
}
|
|
|
|
// check if 200 and if image
|
|
if resp.StatusCode != 200 {
|
|
utils.Debug("FaviconFetch - " + iconURL + " - not 200 ")
|
|
continue
|
|
} else if !strings.Contains(resp.Header.Get("Content-Type"), "image") && !strings.Contains(resp.Header.Get("Content-Type"), "octet-stream") {
|
|
utils.Debug("FaviconFetch - " + iconURL + " - not image ")
|
|
continue
|
|
} else {
|
|
utils.Log("Favicon found " + iconURL)
|
|
|
|
// Cache the response
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
utils.Debug("FaviconFetch - cant read " + err.Error())
|
|
continue
|
|
}
|
|
|
|
finalImage := CachedImage{
|
|
ContentType: resp.Header.Get("Content-Type"),
|
|
ETag: resp.Header.Get("ETag"),
|
|
Body: body,
|
|
}
|
|
|
|
cache[siteurl] = finalImage
|
|
|
|
sendImage(w, cache[siteurl])
|
|
return
|
|
}
|
|
}
|
|
}
|
|
utils.Log("Favicon final fallback")
|
|
sendFallback(w)
|
|
return
|
|
|
|
} else {
|
|
utils.Error("Favicon: Method not allowed" + req.Method, nil)
|
|
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
|
|
return
|
|
}
|
|
}
|
|
|
|
func PingURL(w http.ResponseWriter, req *http.Request) {
|
|
if utils.LoggedInOnly(w, req) != nil {
|
|
return
|
|
}
|
|
|
|
// get url from query string
|
|
escsiteurl := req.URL.Query().Get("q")
|
|
|
|
// URL decode
|
|
siteurl, err := url.QueryUnescape(escsiteurl)
|
|
if err != nil {
|
|
utils.Error("Ping: URL decode", err)
|
|
utils.HTTPError(w, "Ping URL decode", http.StatusInternalServerError, "FA002")
|
|
return
|
|
}
|
|
|
|
|
|
if(req.Method == "GET") {
|
|
utils.Log("Ping for " + siteurl)
|
|
|
|
resp, err := http.Get(siteurl)
|
|
if err != nil {
|
|
utils.Error("Ping", err)
|
|
utils.HTTPError(w, "URL decode", http.StatusInternalServerError, "PI0001")
|
|
return
|
|
}
|
|
|
|
if resp.StatusCode >= 500 {
|
|
utils.Error("Ping", err)
|
|
utils.HTTPError(w, "URL decode", http.StatusInternalServerError, "PI0002")
|
|
return
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"status": "OK",
|
|
"data": map[string]interface{}{
|
|
},
|
|
})
|
|
} else {
|
|
utils.Error("Favicon: Method not allowed" + req.Method, nil)
|
|
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
|
|
return
|
|
}
|
|
}
|
|
|
|
func SendLogo(w http.ResponseWriter, req *http.Request) {
|
|
pwd,_ := os.Getwd()
|
|
imgsrc := "Logo.png"
|
|
Logo, err := ioutil.ReadFile(pwd + "/" + imgsrc)
|
|
if err != nil {
|
|
utils.Error("Logo", err)
|
|
utils.HTTPError(w, "Favicon", http.StatusInternalServerError, "FA003")
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "image/png")
|
|
w.Header().Set("Cache-Control", "max-age=5")
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write(Logo)
|
|
} |