Compare commits
13 Commits
bac33043c1
...
7c4711da1b
Author | SHA1 | Date | |
---|---|---|---|
7c4711da1b | |||
7f16439dd7 | |||
bfe52d0f2a | |||
ec8b2805f5 | |||
7a9777b416 | |||
fd3a246f0d | |||
9c8cec2c5b | |||
67bc36bb66 | |||
e98d9545d0 | |||
637c223f0c | |||
164e809bf6 | |||
3c5a620d4f | |||
ca9be063cf |
|
@ -106,10 +106,6 @@ func newMainHandler(backend *memoryBackend, baseURL, templateDir string, pool *r
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *mainHandler) templateFile(filename string) string {
|
|
||||||
return fmt.Sprintf("%s/%s", h.TemplateDir, filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *mainHandler) renderTemplate(w io.Writer, filename string, data interface{}) error {
|
func (h *mainHandler) renderTemplate(w io.Writer, filename string, data interface{}) error {
|
||||||
fsys, err := fs.Sub(templates, "templates")
|
fsys, err := fs.Sub(templates, "templates")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -292,7 +288,7 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, fmt.Sprintf("Bad Request: %s", err.Error()), 400)
|
http.Error(w, fmt.Sprintf("Bad Request: %s", err.Error()), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,15 +313,19 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
} else if r.URL.Path == "/session/callback" {
|
} else if r.URL.Path == "/session/callback" {
|
||||||
c, err := r.Cookie("session")
|
c, err := r.Cookie("session")
|
||||||
if err == http.ErrNoCookie {
|
if err == http.ErrNoCookie {
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
http.Error(w, "could not read cookie", 500)
|
http.Error(w, "could not read cookie", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionVar := c.Value
|
sessionVar := c.Value
|
||||||
sess, err := loadSession(sessionVar, conn)
|
sess, err := loadSession(sessionVar, conn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "ERROR: %q\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
verified, authResponse, err := performIndieauthCallback(h.BaseURL, r, &sess)
|
verified, authResponse, err := performIndieauthCallback(h.BaseURL, r, &sess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -338,9 +338,9 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
saveSession(sessionVar, &sess, conn)
|
saveSession(sessionVar, &sess, conn)
|
||||||
log.Printf("SESSION: %#v\n", sess)
|
log.Printf("SESSION: %#v\n", sess)
|
||||||
if sess.NextURI != "" {
|
if sess.NextURI != "" {
|
||||||
http.Redirect(w, r, sess.NextURI, 302)
|
http.Redirect(w, r, sess.NextURI, http.StatusFound)
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -348,11 +348,16 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
} else if r.URL.Path == "/settings/channel" {
|
} else if r.URL.Path == "/settings/channel" {
|
||||||
c, err := r.Cookie("session")
|
c, err := r.Cookie("session")
|
||||||
if err == http.ErrNoCookie {
|
if err == http.ErrNoCookie {
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sessionVar := c.Value
|
sessionVar := c.Value
|
||||||
sess, err := loadSession(sessionVar, conn)
|
sess, err := loadSession(sessionVar, conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ERROR: %s\n", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !isLoggedIn(h.Backend, &sess) {
|
if !isLoggedIn(h.Backend, &sess) {
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
|
@ -364,7 +369,17 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
page.Session = sess
|
page.Session = sess
|
||||||
currentChannel := r.URL.Query().Get("uid")
|
currentChannel := r.URL.Query().Get("uid")
|
||||||
page.Channels, err = h.Backend.ChannelsGetList()
|
page.Channels, err = h.Backend.ChannelsGetList()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ERROR: %s\n", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
page.Feeds, err = h.Backend.FollowGetList(currentChannel)
|
page.Feeds, err = h.Backend.FollowGetList(currentChannel)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ERROR: %s\n", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, v := range page.Channels {
|
for _, v := range page.Channels {
|
||||||
if v.UID == currentChannel {
|
if v.UID == currentChannel {
|
||||||
|
@ -404,11 +419,16 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
} else if r.URL.Path == "/logs" {
|
} else if r.URL.Path == "/logs" {
|
||||||
c, err := r.Cookie("session")
|
c, err := r.Cookie("session")
|
||||||
if err == http.ErrNoCookie {
|
if err == http.ErrNoCookie {
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sessionVar := c.Value
|
sessionVar := c.Value
|
||||||
sess, err := loadSession(sessionVar, conn)
|
sess, err := loadSession(sessionVar, conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ERROR: %s\n", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !isLoggedIn(h.Backend, &sess) {
|
if !isLoggedIn(h.Backend, &sess) {
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
|
@ -427,11 +447,16 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
} else if r.URL.Path == "/settings" {
|
} else if r.URL.Path == "/settings" {
|
||||||
c, err := r.Cookie("session")
|
c, err := r.Cookie("session")
|
||||||
if err == http.ErrNoCookie {
|
if err == http.ErrNoCookie {
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sessionVar := c.Value
|
sessionVar := c.Value
|
||||||
sess, err := loadSession(sessionVar, conn)
|
sess, err := loadSession(sessionVar, conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ERROR: %s\n", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !isLoggedIn(h.Backend, &sess) {
|
if !isLoggedIn(h.Backend, &sess) {
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
|
@ -442,6 +467,11 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
var page settingsPage
|
var page settingsPage
|
||||||
page.Session = sess
|
page.Session = sess
|
||||||
page.Channels, err = h.Backend.ChannelsGetList()
|
page.Channels, err = h.Backend.ChannelsGetList()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ERROR: %s\n", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
// page.Feeds = h.Backend.Feeds
|
// page.Feeds = h.Backend.Feeds
|
||||||
|
|
||||||
err = h.renderTemplate(w, "settings.html", page)
|
err = h.renderTemplate(w, "settings.html", page)
|
||||||
|
@ -456,11 +486,16 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
sessionVar := getSessionCookie(w, r)
|
sessionVar := getSessionCookie(w, r)
|
||||||
|
|
||||||
sess, err := loadSession(sessionVar, conn)
|
sess, err := loadSession(sessionVar, conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ERROR: %s\n", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !isLoggedIn(h.Backend, &sess) {
|
if !isLoggedIn(h.Backend, &sess) {
|
||||||
sess.NextURI = r.URL.String()
|
sess.NextURI = r.URL.String()
|
||||||
saveSession(sessionVar, &sess, conn)
|
saveSession(sessionVar, &sess, conn)
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,6 +537,11 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
page.Scope = scope
|
page.Scope = scope
|
||||||
page.State = state
|
page.State = state
|
||||||
page.Channels, err = h.Backend.ChannelsGetList()
|
page.Channels, err = h.Backend.ChannelsGetList()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ERROR: %s\n", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
app, err := getAppInfo(clientID)
|
app, err := getAppInfo(clientID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -519,7 +559,7 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.URL.Path == "/session" {
|
if r.URL.Path == "/session" {
|
||||||
c, err := r.Cookie("session")
|
c, err := r.Cookie("session")
|
||||||
if err == http.ErrNoCookie {
|
if err == http.ErrNoCookie {
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,7 +570,7 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
endpoints, err := getEndpoints(me)
|
endpoints, err := getEndpoints(me)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, fmt.Sprintf("Bad Request: %s, %s", err.Error(), me), 400)
|
http.Error(w, fmt.Sprintf("Bad Request: %s, %s", err.Error(), me), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,7 +580,7 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
sess, err := loadSession(sessionVar, conn)
|
sess, err := loadSession(sessionVar, conn)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,12 +592,12 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
err = saveSession(sessionVar, &sess, conn)
|
err = saveSession(sessionVar, &sess, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticationURL := indieauth.CreateAuthenticationURL(*endpoints.AuthorizationEndpoint, endpoints.Me.String(), h.BaseURL, redirectURI, state)
|
authenticationURL := indieauth.CreateAuthenticationURL(*endpoints.AuthorizationEndpoint, endpoints.Me.String(), h.BaseURL, redirectURI, state)
|
||||||
http.Redirect(w, r, authenticationURL, 302)
|
http.Redirect(w, r, authenticationURL, http.StatusFound)
|
||||||
|
|
||||||
return
|
return
|
||||||
} else if r.URL.Path == "/session/logout" {
|
} else if r.URL.Path == "/session/logout" {
|
||||||
|
@ -610,7 +650,7 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
redirectURI.RawQuery = q.Encode()
|
redirectURI.RawQuery = q.Encode()
|
||||||
|
|
||||||
log.Println(redirectURI)
|
log.Println(redirectURI)
|
||||||
http.Redirect(w, r, redirectURI.String(), 302)
|
http.Redirect(w, r, redirectURI.String(), http.StatusFound)
|
||||||
return
|
return
|
||||||
} else if r.URL.Path == "/auth/token" {
|
} else if r.URL.Path == "/auth/token" {
|
||||||
grantType := r.FormValue("grant_type")
|
grantType := r.FormValue("grant_type")
|
||||||
|
@ -682,11 +722,11 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// }
|
// }
|
||||||
// h.Backend.Settings[uid] = setting
|
// h.Backend.Settings[uid] = setting
|
||||||
|
|
||||||
http.Redirect(w, r, "/settings", 302)
|
http.Redirect(w, r, "/settings", http.StatusFound)
|
||||||
return
|
return
|
||||||
} else if r.URL.Path == "/refresh" {
|
} else if r.URL.Path == "/refresh" {
|
||||||
h.Backend.RefreshFeeds()
|
h.Backend.RefreshFeeds()
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -697,14 +737,14 @@ func (h *mainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
func httpSessionLogout(r *http.Request, w http.ResponseWriter, conn redis.Conn) {
|
func httpSessionLogout(r *http.Request, w http.ResponseWriter, conn redis.Conn) {
|
||||||
c, err := r.Cookie("session")
|
c, err := r.Cookie("session")
|
||||||
if err == http.ErrNoCookie {
|
if err == http.ErrNoCookie {
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
sessionVar := c.Value
|
sessionVar := c.Value
|
||||||
_, _ = conn.Do("DEL", "session:"+sessionVar)
|
_, _ = conn.Do("DEL", "session:"+sessionVar)
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, "/", 302)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
type parsedEndpoints struct {
|
type parsedEndpoints struct {
|
||||||
|
|
|
@ -172,6 +172,9 @@ func (h *hubIncomingBackend) Feeds() ([]Feed, error) {
|
||||||
inner join channels c on c.id = f.channel_id
|
inner join channels c on c.id = f.channel_id
|
||||||
where hub is not null
|
where hub is not null
|
||||||
`)
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var feed Feed
|
var feed Feed
|
||||||
|
|
|
@ -51,13 +51,13 @@ func (h *incomingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
leaseSeconds, err := strconv.ParseInt(leaseStr, 10, 64)
|
leaseSeconds, err := strconv.ParseInt(leaseStr, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error in hub.lease_seconds format %q: %s", leaseSeconds, err)
|
log.Printf("error in hub.lease_seconds format %q: %s", leaseSeconds, err)
|
||||||
http.Error(w, fmt.Sprintf("error in hub.lease_seconds format %q: %s", leaseSeconds, err), 400)
|
http.Error(w, fmt.Sprintf("error in hub.lease_seconds format %q: %s", leaseSeconds, err), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = h.Backend.FeedSetLeaseSeconds(feed, leaseSeconds)
|
err = h.Backend.FeedSetLeaseSeconds(feed, leaseSeconds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error in while setting hub.lease_seconds: %s", err)
|
log.Printf("error in while setting hub.lease_seconds: %s", err)
|
||||||
http.Error(w, fmt.Sprintf("error in while setting hub.lease_seconds: %s", err), 400)
|
http.Error(w, fmt.Sprintf("error in while setting hub.lease_seconds: %s", err), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ func (h *incomingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
http.Error(w, "Method not allowed", 405)
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,18 +78,23 @@ func (h *incomingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
secret := h.Backend.GetSecret(feed)
|
secret := h.Backend.GetSecret(feed)
|
||||||
if secret == "" {
|
if secret == "" {
|
||||||
log.Printf("missing secret for feed %d\n", feed)
|
log.Printf("missing secret for feed %d\n", feed)
|
||||||
http.Error(w, "Unknown", 400)
|
http.Error(w, "Unknown", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
feedContent, err := ioutil.ReadAll(r.Body)
|
feedContent, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ERROR: %s\n", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// match signature
|
// match signature
|
||||||
sig := r.Header.Get("X-Hub-Signature")
|
sig := r.Header.Get("X-Hub-Signature")
|
||||||
if sig != "" {
|
if sig != "" {
|
||||||
if err := websub.ValidateHubSignature(sig, feedContent, []byte(secret)); err != nil {
|
if err := websub.ValidateHubSignature(sig, feedContent, []byte(secret)); err != nil {
|
||||||
log.Printf("could not validate signature: %+v", err)
|
log.Printf("could not validate signature: %+v", err)
|
||||||
http.Error(w, fmt.Sprintf("could not validate signature: %s", err), 400)
|
http.Error(w, fmt.Sprintf("could not validate signature: %s", err), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,9 +102,7 @@ func (h *incomingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
ct := r.Header.Get("Content-Type")
|
ct := r.Header.Get("Content-Type")
|
||||||
err = h.Backend.UpdateFeed(h.Processor, feed, ct, bytes.NewBuffer(feedContent))
|
err = h.Backend.UpdateFeed(h.Processor, feed, ct, bytes.NewBuffer(feedContent))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, fmt.Sprintf("could not update feed: %s (%s)", ct, err), 400)
|
http.Error(w, fmt.Sprintf("could not update feed: %s (%s)", ct, err), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,13 +86,13 @@ func WithAuth(handler http.Handler, b *memoryBackend) http.Handler {
|
||||||
}
|
}
|
||||||
if !authorized {
|
if !authorized {
|
||||||
log.Printf("Token could not be validated")
|
log.Printf("Token could not be validated")
|
||||||
http.Error(w, "Can't validate token", 403)
|
http.Error(w, "Can't validate token", http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.Me != b.Me { // FIXME: Me should be part of the request
|
if token.Me != b.Me { // FIXME: Me should be part of the request
|
||||||
log.Printf("Missing \"me\" in token response: %#v\n", token)
|
log.Printf("Missing \"me\" in token response: %#v\n", token)
|
||||||
http.Error(w, "Wrong me", 403)
|
http.Error(w, "Wrong me", http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,12 +84,6 @@ type newItemMessage struct {
|
||||||
Channel string `json:"channel"`
|
Channel string `json:"channel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type fetch2 struct{}
|
|
||||||
|
|
||||||
func (f *fetch2) Fetch(url string) (*http.Response, error) {
|
|
||||||
return Fetch2(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *memoryBackend) AuthTokenAccepted(header string, r *auth.TokenResponse) (bool, error) {
|
func (b *memoryBackend) AuthTokenAccepted(header string, r *auth.TokenResponse) (bool, error) {
|
||||||
conn := b.pool.Get()
|
conn := b.pool.Get()
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -142,10 +136,7 @@ func shouldRetryWithNewUID(err error, try int) bool {
|
||||||
|
|
||||||
if e, ok := err.(*pq.Error); ok {
|
if e, ok := err.(*pq.Error); ok {
|
||||||
if e.Code == "23505" && e.Constraint == "channels_uid_key" {
|
if e.Code == "23505" && e.Constraint == "channels_uid_key" {
|
||||||
if try > 5 {
|
return try <= 5
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -633,27 +624,22 @@ func (b *memoryBackend) channelAddItemWithMatcher(channel string, item microsub.
|
||||||
if len(item.RepostOf) > 0 {
|
if len(item.RepostOf) > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
break
|
|
||||||
case "like":
|
case "like":
|
||||||
if len(item.LikeOf) > 0 {
|
if len(item.LikeOf) > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
break
|
|
||||||
case "bookmark":
|
case "bookmark":
|
||||||
if len(item.BookmarkOf) > 0 {
|
if len(item.BookmarkOf) > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
break
|
|
||||||
case "reply":
|
case "reply":
|
||||||
if len(item.InReplyTo) > 0 {
|
if len(item.InReplyTo) > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
break
|
|
||||||
case "checkin":
|
case "checkin":
|
||||||
if item.Checkin != nil {
|
if item.Checkin != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -775,7 +761,10 @@ func WithCaching(pool *redis.Pool, ff fetch.Fetcher) fetch.Fetcher {
|
||||||
return nil, fmt.Errorf("error parsing %s as url: %s", fetchURL, err)
|
return nil, fmt.Errorf("error parsing %s as url: %s", fetchURL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", u.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
data, err := redis.Bytes(conn.Do("GET", cacheKey))
|
data, err := redis.Bytes(conn.Do("GET", cacheKey))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -821,7 +810,10 @@ func Fetch2(fetchURL string) (*http.Response, error) {
|
||||||
return nil, fmt.Errorf("error parsing %s as url: %s", fetchURL, err)
|
return nil, fmt.Errorf("error parsing %s as url: %s", fetchURL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", u.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
|
|
|
@ -40,7 +40,7 @@ func (h *micropubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "bad request", 400)
|
http.Error(w, "bad request", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,13 +50,13 @@ func (h *micropubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
channel, err = getChannelFromAuthorization(r, conn)
|
channel, err = getChannelFromAuthorization(r, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, "unauthorized", 401)
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// no channel is found
|
// no channel is found
|
||||||
if channel == "" {
|
if channel == "" {
|
||||||
http.Error(w, "bad request, unknown channel", 400)
|
http.Error(w, "bad request, unknown channel", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ func (h *micropubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
item, err := parseIncomingItem(r)
|
item, err := parseIncomingItem(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, err.Error(), 400)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf("Item published: %s", item.Published)
|
log.Printf("Item published: %s", item.Published)
|
||||||
|
@ -76,7 +76,7 @@ func (h *micropubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
newID, err := generateItemID(conn, channel)
|
newID, err := generateItemID(conn, channel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.ID = newID
|
item.ID = newID
|
||||||
|
@ -95,13 +95,13 @@ func (h *micropubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if err = json.NewEncoder(w).Encode(map[string]string{"ok": "1"}); err != nil {
|
if err = json.NewEncoder(w).Encode(map[string]string{"ok": "1"}); err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, "internal server error", 500)
|
http.Error(w, "internal server error", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Error(w, "Method not allowed", 405)
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateItemID(conn redis.Conn, channel string) (string, error) {
|
func generateItemID(conn redis.Conn, channel string) (string, error) {
|
||||||
|
|
|
@ -48,6 +48,9 @@ func FeedHeader(fetcher Fetcher, fetchURL, contentType string, body io.Reader) (
|
||||||
md := microformats.Parse(resp.Body, u)
|
md := microformats.Parse(resp.Body, u)
|
||||||
|
|
||||||
author, ok = jf2.SimplifyMicroformatDataAuthor(md)
|
author, ok = jf2.SimplifyMicroformatDataAuthor(md)
|
||||||
|
if !ok {
|
||||||
|
log.Println("Could not simplify the author")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,14 @@ func simplifyContent(k string, v []interface{}) *microsub.Content {
|
||||||
// CleanHTML removes white-space:pre from html
|
// CleanHTML removes white-space:pre from html
|
||||||
func CleanHTML(s string) (string, error) {
|
func CleanHTML(s string) (string, error) {
|
||||||
doc, err := html.Parse(strings.NewReader(s))
|
doc, err := html.Parse(strings.NewReader(s))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
whitespaceRegex, err := regexp.Compile(`white-space:\s*pre`)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -101,7 +109,7 @@ func CleanHTML(s string) (string, error) {
|
||||||
if a.Key != "style" {
|
if a.Key != "style" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if m, err := regexp.MatchString("white-space:\\s*pre", a.Val); err == nil && m {
|
if whitespaceRegex.MatchString(a.Val) {
|
||||||
removeIndex = i
|
removeIndex = i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ func (cs *charsetISO88591er) ReadByte() (b byte, err error) {
|
||||||
|
|
||||||
func (cs *charsetISO88591er) Read(p []byte) (int, error) {
|
func (cs *charsetISO88591er) Read(p []byte) (int, error) {
|
||||||
// Use ReadByte method.
|
// Use ReadByte method.
|
||||||
return 0, errors.New("Use ReadByte()")
|
return 0, errors.New("use ReadByte()")
|
||||||
}
|
}
|
||||||
|
|
||||||
func isCharsetISO88591(charset string) bool {
|
func isCharsetISO88591(charset string) bool {
|
||||||
|
|
|
@ -303,7 +303,7 @@ type Enclosure struct {
|
||||||
// Get uses http.Get to fetch an enclosure.
|
// Get uses http.Get to fetch an enclosure.
|
||||||
func (e *Enclosure) Get() (io.ReadCloser, error) {
|
func (e *Enclosure) Get() (io.ReadCloser, error) {
|
||||||
if e == nil || e.URL == "" {
|
if e == nil || e.URL == "" {
|
||||||
return nil, errors.New("No enclosure")
|
return nil, errors.New("no enclosure")
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := http.Get(e.URL)
|
res, err := http.Get(e.URL)
|
||||||
|
@ -325,7 +325,7 @@ type Image struct {
|
||||||
// Get uses http.Get to fetch an image.
|
// Get uses http.Get to fetch an image.
|
||||||
func (i *Image) Get() (io.ReadCloser, error) {
|
func (i *Image) Get() (io.ReadCloser, error) {
|
||||||
if i == nil || i.URL == "" {
|
if i == nil || i.URL == "" {
|
||||||
return nil, errors.New("No image")
|
return nil, errors.New("no image")
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := http.Get(i.URL)
|
res, err := http.Get(i.URL)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package rss
|
package rss
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -77,7 +76,7 @@ func TestParseItemDateOK(t *testing.T) {
|
||||||
t.Fatalf("Parsing %s: %v", name, err)
|
t.Fatalf("Parsing %s: %v", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fmt.Sprintf("%s", feed.Items[0].Date) != want {
|
if feed.Items[0].Date.String() != want {
|
||||||
t.Errorf("%s: got %q, want %q", name, feed.Items[0].Date, want)
|
t.Errorf("%s: got %q, want %q", name, feed.Items[0].Date, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +99,7 @@ func TestParseItemDateFailure(t *testing.T) {
|
||||||
t.Fatalf("Parsing %s: %v", name, err)
|
t.Fatalf("Parsing %s: %v", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fmt.Sprintf("%s", feed.Items[1].Date) != want {
|
if feed.Items[1].Date.String() != want {
|
||||||
t.Errorf("%s: got %q, want %q", name, feed.Items[1].Date, want)
|
t.Errorf("%s: got %q, want %q", name, feed.Items[1].Date, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
entryRegex = regexp.MustCompile("^entry\\[\\d+\\]$")
|
entryRegex = regexp.MustCompile(`^entry\[\d+\]$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Constants used for the responses
|
// Constants used for the responses
|
||||||
|
@ -36,7 +36,7 @@ func respondJSON(w http.ResponseWriter, value interface{}) {
|
||||||
w.Header().Add("Content-Type", OutputContentType)
|
w.Header().Add("Content-Type", OutputContentType)
|
||||||
err := jw.Encode(value)
|
err := jw.Encode(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
channels, err := h.backend.ChannelsGetList()
|
channels, err := h.backend.ChannelsGetList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
respondJSON(w, map[string][]microsub.Channel{
|
respondJSON(w, map[string][]microsub.Channel{
|
||||||
|
@ -80,7 +80,7 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
timeline, err := h.backend.TimelineGet(values.Get("before"), values.Get("after"), values.Get("channel"))
|
timeline, err := h.backend.TimelineGet(values.Get("before"), values.Get("after"), values.Get("channel"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
respondJSON(w, timeline)
|
respondJSON(w, timeline)
|
||||||
|
@ -88,7 +88,7 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
timeline, err := h.backend.PreviewURL(values.Get("url"))
|
timeline, err := h.backend.PreviewURL(values.Get("url"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
respondJSON(w, timeline)
|
respondJSON(w, timeline)
|
||||||
|
@ -97,7 +97,7 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
following, err := h.backend.FollowGetList(channel)
|
following, err := h.backend.FollowGetList(channel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
respondJSON(w, map[string][]microsub.Feed{
|
respondJSON(w, map[string][]microsub.Feed{
|
||||||
|
@ -107,7 +107,7 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
events, err := h.backend.Events()
|
events, err := h.backend.Events()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, "could not start sse connection", 500)
|
http.Error(w, "could not start sse connection", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove this client from the map of connected clients
|
// Remove this client from the map of connected clients
|
||||||
|
@ -117,7 +117,7 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Listen to connection close and un-register messageChan
|
// Listen to connection close and un-register messageChan
|
||||||
notify := w.(http.CloseNotifier).CloseNotify()
|
notify := r.Context().Done()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
<-notify
|
<-notify
|
||||||
|
@ -127,10 +127,10 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
err = sse.WriteMessages(w, events)
|
err = sse.WriteMessages(w, events)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, "internal server error", 500)
|
http.Error(w, "internal server error", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
http.Error(w, fmt.Sprintf("unknown action %s", action), 400)
|
http.Error(w, fmt.Sprintf("unknown action %s", action), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -147,7 +147,7 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
err := h.backend.ChannelsDelete(uid)
|
err := h.backend.ChannelsDelete(uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
respondJSON(w, []string{})
|
respondJSON(w, []string{})
|
||||||
|
@ -158,7 +158,7 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
channel, err := h.backend.ChannelsCreate(name)
|
channel, err := h.backend.ChannelsCreate(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
respondJSON(w, channel)
|
respondJSON(w, channel)
|
||||||
|
@ -166,7 +166,7 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
channel, err := h.backend.ChannelsUpdate(uid, name)
|
channel, err := h.backend.ChannelsUpdate(uid, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
respondJSON(w, channel)
|
respondJSON(w, channel)
|
||||||
|
@ -177,7 +177,7 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// h.HubIncomingBackend.CreateFeed(url, uid)
|
// h.HubIncomingBackend.CreateFeed(url, uid)
|
||||||
feed, err := h.backend.FollowURL(uid, url)
|
feed, err := h.backend.FollowURL(uid, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
respondJSON(w, feed)
|
respondJSON(w, feed)
|
||||||
|
@ -186,14 +186,14 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
url := values.Get("url")
|
url := values.Get("url")
|
||||||
err := h.backend.UnfollowURL(uid, url)
|
err := h.backend.UnfollowURL(uid, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
respondJSON(w, []string{})
|
respondJSON(w, []string{})
|
||||||
} else if action == "preview" {
|
} else if action == "preview" {
|
||||||
timeline, err := h.backend.PreviewURL(values.Get("url"))
|
timeline, err := h.backend.PreviewURL(values.Get("url"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
respondJSON(w, timeline)
|
respondJSON(w, timeline)
|
||||||
|
@ -250,22 +250,21 @@ func (h *microsubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
if len(markAsRead) > 0 {
|
if len(markAsRead) > 0 {
|
||||||
err := h.backend.MarkRead(channel, markAsRead)
|
err := h.backend.MarkRead(channel, markAsRead)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Println("No uids specified for mark read")
|
log.Println("No uids specified for mark read")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
http.Error(w, fmt.Sprintf("unknown method in timeline %s\n", method), 500)
|
http.Error(w, fmt.Sprintf("unknown method in timeline %s\n", method), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
respondJSON(w, []string{})
|
respondJSON(w, []string{})
|
||||||
} else {
|
} else {
|
||||||
http.Error(w, fmt.Sprintf("unknown action %s\n", action), 400)
|
http.Error(w, fmt.Sprintf("unknown action %s\n", action), http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user