diff --git a/package.json b/package.json
index 87e19fa..fbb4845 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cosmos-server",
- "version": "0.5.0-unstable20",
+ "version": "0.5.0-unstable21",
"description": "",
"main": "test-server.js",
"bugs": {
diff --git a/src/icons.go b/src/icons.go
index 357e600..04eda0d 100644
--- a/src/icons.go
+++ b/src/icons.go
@@ -7,9 +7,11 @@ import (
"strings"
"os"
"io/ioutil"
- "regexp"
- "path"
"encoding/json"
+ "strconv"
+ "path"
+
+ "go.deanishe.net/favicon"
"github.com/azukaar/cosmos-server/src/utils"
)
@@ -22,70 +24,6 @@ type CachedImage struct {
var cache = make(map[string]CachedImage)
-func ExtractFaviconMetaTag(filename string) string {
- // Read the contents of the file
- htmlBytes, err := ioutil.ReadFile(filename)
- if err != nil {
- return "/favicon.ico"
- }
- html := string(htmlBytes)
-
- // Regular expression pattern to match the favicon metatag
- pattern := `]*rel="icon"[^>]*(?:sizes="([^"]+)")?[^>]*href="([^"]+)"[^>]*>|]*name="msapplication-TileImage"[^>]*content="([^"]+)"[^>]*>`
-
- // Compile the regular expression pattern
- regex := regexp.MustCompile(pattern)
-
- // Find all matches in the HTML string
- matches := regex.FindAllStringSubmatch(html, -1)
-
- var faviconURL string
-
- // Iterate over the matches to find the appropriate favicon
- for _, match := range matches {
- sizes := match[1]
- href := match[2]
- msAppTileImage := match[3]
-
- // Check if the meta tag specifies msapplication-TileImage
- if msAppTileImage != "" {
- faviconURL = msAppTileImage
- break
- }
-
- // Check if the sizes attribute contains 96x96
- if strings.Contains(sizes, "96x96") {
- faviconURL = href
- break
- }
-
- // Check if the sizes attribute contains 64x64
- if strings.Contains(sizes, "64x64") {
- faviconURL = href
- continue
- }
-
- // Check if the sizes attribute contains 32x32
- if strings.Contains(sizes, "32x32") {
- faviconURL = href
- continue
- }
-
- // If no sizes specified, set faviconURL to the first match without sizes
- if faviconURL == "" && sizes == "" {
- faviconURL = href
- }
- }
-
- // If a favicon URL is found, return it
- if faviconURL != "" {
- return faviconURL
- }
-
- // Return an error if no favicon URL is found
- return "/favicon.ico"
-}
-
func sendImage(w http.ResponseWriter, image CachedImage) {
// Copy the response to the output
w.Header().Set("Content-Type", image.ContentType)
@@ -111,6 +49,7 @@ func sendFallback(w http.ResponseWriter) {
w.Write(fallback)
}
+var IconCacheLock = make(chan bool, 1)
func GetFavicon(w http.ResponseWriter, req *http.Request) {
if utils.LoggedInOnly(w, req) != nil {
@@ -120,6 +59,9 @@ func GetFavicon(w http.ResponseWriter, req *http.Request) {
// 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 {
@@ -147,70 +89,105 @@ func GetFavicon(w http.ResponseWriter, req *http.Request) {
return
}
- urlBody, err := ioutil.ReadAll(respNew.Body)
+ newsiteurl := respNew.Request.URL.String()
+
+ icons, err := favicon.Find(newsiteurl)
+
+ utils.Debug("Found Favicon: " + strconv.Itoa(len(icons)))
if err != nil {
utils.Error("FaviconFetch", err)
sendFallback(w)
return
}
+
+ if len(icons) == 0 {
+ utils.Error("FaviconFetch", err)
+ sendFallback(w)
+ return
+ }
+
+ // if !strings.Contains(icons[iconIndex].URL, "favicon.ico") {
+ // icons = append(icons, favicon.Icon{URL: "favicon.ico", Width: 0})
+ // }
- // get favicon meta tag from the response
- faviconURL := ExtractFaviconMetaTag((string)(urlBody))
+ // if !strings.Contains(icons[iconIndex].URL, "/favicon.ico") {
+ // icons = append(icons, favicon.Icon{URL: "/favicon.ico", Width: 0})
+ // }
- // if faviconURL is relative get hostname of the URL and append icon
- if !strings.HasPrefix(faviconURL, "http") {
- u, err := url.Parse(siteurl)
- if err != nil {
- utils.Error("FaviconFetch", err)
+ for _, icon := range icons {
+ utils.Debug("Favicon Width: " + icon.URL + " " + strconv.Itoa(icon.Width))
+ if icon.Width <= 256 {
+
+ iconURL := icon.URL
+ u, err := url.Parse(siteurl)
+ if err != nil {
+ utils.Error("FaviconFetch failed to parse ", err)
+ 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.Log("Favicon Trying to fetch " + iconURL)
+ // Fetch the favicon
+ resp, err := http.Get(iconURL)
+ if err != nil {
+ utils.Error("FaviconFetch", err)
+ continue
+ }
+
+ // check if 200 and if image
+ if resp.StatusCode != 200 {
+ utils.Error("FaviconFetch - " + iconURL + " - not 200 ", nil)
+ continue
+ } else if !strings.Contains(resp.Header.Get("Content-Type"), "image") {
+ utils.Error("FaviconFetch - " + iconURL + " - not image ", nil)
+ continue
+ } else {
+ utils.Log("Favicon found " + iconURL)
+
+ // Cache the response
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ utils.Error("FaviconFetch", err)
+ sendFallback(w)
+ return
+ }
+
+ 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
}
-
- if strings.HasPrefix(faviconURL, ".") {
- // Relative URL starting with "."
- // Resolve the relative URL based on the base URL
- baseURL := u.Scheme + "://" + u.Host
- faviconURL = baseURL + faviconURL[1:]
- } else if strings.HasPrefix(faviconURL, "/") {
- // Relative URL starting with "/"
- // Append the relative URL to the base URL
- faviconURL = u.Scheme + "://" + u.Host + faviconURL
- } 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)
- faviconURL = baseURL + baseURLPath + "/" + faviconURL
- }
- }
-
- utils.Log("Favicon: " + faviconURL)
-
- // Fetch the favicon
- resp, err := http.Get(faviconURL)
- if err != nil {
- utils.Error("FaviconFetch", err)
- sendFallback(w)
- return
- }
-
- // Cache the response
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- utils.Error("FaviconFetch", err)
- sendFallback(w)
- return
- }
-
- finalImage := CachedImage{
- ContentType: resp.Header.Get("Content-Type"),
- ETag: resp.Header.Get("ETag"),
- Body: body,
- }
-
- cache[siteurl] = finalImage
-
- sendImage(w, cache[siteurl])
+ }
+
} else {
utils.Error("Favicon: Method not allowed" + req.Method, nil)
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")