1/*
2 * Copyright © 2018-2021 A Bunch Tell LLC.
3 *
4 * This file is part of WriteFreely.
5 *
6 * WriteFreely is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License, included
8 * in the LICENSE file in this source code package.
9 */
10
11package writefreely
12
13import (
14	"fmt"
15	"html/template"
16	"net/http"
17	"net/url"
18	"runtime/debug"
19	"strconv"
20	"strings"
21	"time"
22
23	"github.com/gorilla/sessions"
24	"git.mills.io/prologic/go-gopher"
25	"github.com/writeas/impart"
26	"github.com/writeas/web-core/log"
27	"github.com/writefreely/writefreely/config"
28	"github.com/writefreely/writefreely/page"
29)
30
31// UserLevel represents the required user level for accessing an endpoint
32type UserLevel int
33
34const (
35	UserLevelNoneType         UserLevel = iota // user or not -- ignored
36	UserLevelOptionalType                      // user or not -- object fetched if user
37	UserLevelNoneRequiredType                  // non-user (required)
38	UserLevelUserType                          // user (required)
39)
40
41func UserLevelNone(cfg *config.Config) UserLevel {
42	return UserLevelNoneType
43}
44
45func UserLevelOptional(cfg *config.Config) UserLevel {
46	return UserLevelOptionalType
47}
48
49func UserLevelNoneRequired(cfg *config.Config) UserLevel {
50	return UserLevelNoneRequiredType
51}
52
53func UserLevelUser(cfg *config.Config) UserLevel {
54	return UserLevelUserType
55}
56
57// UserLevelReader returns the permission level required for any route where
58// users can read published content.
59func UserLevelReader(cfg *config.Config) UserLevel {
60	if cfg.App.Private {
61		return UserLevelUserType
62	}
63	return UserLevelOptionalType
64}
65
66type (
67	handlerFunc          func(app *App, w http.ResponseWriter, r *http.Request) error
68	gopherFunc           func(app *App, w gopher.ResponseWriter, r *gopher.Request) error
69	userHandlerFunc      func(app *App, u *User, w http.ResponseWriter, r *http.Request) error
70	userApperHandlerFunc func(apper Apper, u *User, w http.ResponseWriter, r *http.Request) error
71	dataHandlerFunc      func(app *App, w http.ResponseWriter, r *http.Request) ([]byte, string, error)
72	authFunc             func(app *App, r *http.Request) (*User, error)
73	UserLevelFunc        func(cfg *config.Config) UserLevel
74)
75
76type Handler struct {
77	errors       *ErrorPages
78	sessionStore sessions.Store
79	app          Apper
80}
81
82// ErrorPages hold template HTML error pages for displaying errors to the user.
83// In each, there should be a defined template named "base".
84type ErrorPages struct {
85	NotFound            *template.Template
86	Gone                *template.Template
87	InternalServerError *template.Template
88	UnavailableError    *template.Template
89	Blank               *template.Template
90}
91
92// NewHandler returns a new Handler instance, using the given StaticPage data,
93// and saving alias to the application's CookieStore.
94func NewHandler(apper Apper) *Handler {
95	h := &Handler{
96		errors: &ErrorPages{
97			NotFound:            template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>404</title></head><body><p>Not found.</p></body></html>{{end}}")),
98			Gone:                template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>410</title></head><body><p>Gone.</p></body></html>{{end}}")),
99			InternalServerError: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>500</title></head><body><p>Internal server error.</p></body></html>{{end}}")),
100			UnavailableError:    template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>503</title></head><body><p>Service is temporarily unavailable.</p></body></html>{{end}}")),
101			Blank:               template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>{{.Title}}</title></head><body><p>{{.Content}}</p></body></html>{{end}}")),
102		},
103		sessionStore: apper.App().SessionStore(),
104		app:          apper,
105	}
106
107	return h
108}
109
110// NewWFHandler returns a new Handler instance, using WriteFreely template files.
111// You MUST call writefreely.InitTemplates() before this.
112func NewWFHandler(apper Apper) *Handler {
113	h := NewHandler(apper)
114	h.SetErrorPages(&ErrorPages{
115		NotFound:            pages["404-general.tmpl"],
116		Gone:                pages["410.tmpl"],
117		InternalServerError: pages["500.tmpl"],
118		UnavailableError:    pages["503.tmpl"],
119		Blank:               pages["blank.tmpl"],
120	})
121	return h
122}
123
124// SetErrorPages sets the given set of ErrorPages as templates for any errors
125// that come up.
126func (h *Handler) SetErrorPages(e *ErrorPages) {
127	h.errors = e
128}
129
130// User handles requests made in the web application by the authenticated user.
131// This provides user-friendly HTML pages and actions that work in the browser.
132func (h *Handler) User(f userHandlerFunc) http.HandlerFunc {
133	return func(w http.ResponseWriter, r *http.Request) {
134		h.handleHTTPError(w, r, func() error {
135			var status int
136			start := time.Now()
137
138			defer func() {
139				if e := recover(); e != nil {
140					log.Error("%s: %s", e, debug.Stack())
141					h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
142					status = http.StatusInternalServerError
143				}
144
145				log.Info(h.app.ReqLog(r, status, time.Since(start)))
146			}()
147
148			u := getUserSession(h.app.App(), r)
149			if u == nil {
150				err := ErrNotLoggedIn
151				status = err.Status
152				return err
153			}
154
155			err := f(h.app.App(), u, w, r)
156			if err == nil {
157				status = http.StatusOK
158			} else if err, ok := err.(impart.HTTPError); ok {
159				status = err.Status
160			} else {
161				status = http.StatusInternalServerError
162			}
163
164			return err
165		}())
166	}
167}
168
169// Admin handles requests on /admin routes
170func (h *Handler) Admin(f userHandlerFunc) http.HandlerFunc {
171	return func(w http.ResponseWriter, r *http.Request) {
172		h.handleHTTPError(w, r, func() error {
173			var status int
174			start := time.Now()
175
176			defer func() {
177				if e := recover(); e != nil {
178					log.Error("%s: %s", e, debug.Stack())
179					h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
180					status = http.StatusInternalServerError
181				}
182
183				log.Info(h.app.ReqLog(r, status, time.Since(start)))
184			}()
185
186			u := getUserSession(h.app.App(), r)
187			if u == nil || !u.IsAdmin() {
188				err := impart.HTTPError{http.StatusNotFound, ""}
189				status = err.Status
190				return err
191			}
192
193			err := f(h.app.App(), u, w, r)
194			if err == nil {
195				status = http.StatusOK
196			} else if err, ok := err.(impart.HTTPError); ok {
197				status = err.Status
198			} else {
199				status = http.StatusInternalServerError
200			}
201
202			return err
203		}())
204	}
205}
206
207// AdminApper handles requests on /admin routes that require an Apper.
208func (h *Handler) AdminApper(f userApperHandlerFunc) http.HandlerFunc {
209	return func(w http.ResponseWriter, r *http.Request) {
210		h.handleHTTPError(w, r, func() error {
211			var status int
212			start := time.Now()
213
214			defer func() {
215				if e := recover(); e != nil {
216					log.Error("%s: %s", e, debug.Stack())
217					h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
218					status = http.StatusInternalServerError
219				}
220
221				log.Info(h.app.ReqLog(r, status, time.Since(start)))
222			}()
223
224			u := getUserSession(h.app.App(), r)
225			if u == nil || !u.IsAdmin() {
226				err := impart.HTTPError{http.StatusNotFound, ""}
227				status = err.Status
228				return err
229			}
230
231			err := f(h.app, u, w, r)
232			if err == nil {
233				status = http.StatusOK
234			} else if err, ok := err.(impart.HTTPError); ok {
235				status = err.Status
236			} else {
237				status = http.StatusInternalServerError
238			}
239
240			return err
241		}())
242	}
243}
244
245func apiAuth(app *App, r *http.Request) (*User, error) {
246	// Authorize user from Authorization header
247	t := r.Header.Get("Authorization")
248	if t == "" {
249		return nil, ErrNoAccessToken
250	}
251	u := &User{ID: app.db.GetUserID(t)}
252	if u.ID == -1 {
253		return nil, ErrBadAccessToken
254	}
255
256	return u, nil
257}
258
259// optionaAPIAuth is used for endpoints that accept authenticated requests via
260// Authorization header or cookie, unlike apiAuth. It returns a different err
261// in the case where no Authorization header is present.
262func optionalAPIAuth(app *App, r *http.Request) (*User, error) {
263	// Authorize user from Authorization header
264	t := r.Header.Get("Authorization")
265	if t == "" {
266		return nil, ErrNotLoggedIn
267	}
268	u := &User{ID: app.db.GetUserID(t)}
269	if u.ID == -1 {
270		return nil, ErrBadAccessToken
271	}
272
273	return u, nil
274}
275
276func webAuth(app *App, r *http.Request) (*User, error) {
277	u := getUserSession(app, r)
278	if u == nil {
279		return nil, ErrNotLoggedIn
280	}
281	return u, nil
282}
283
284// UserAPI handles requests made in the API by the authenticated user.
285// This provides user-friendly HTML pages and actions that work in the browser.
286func (h *Handler) UserAPI(f userHandlerFunc) http.HandlerFunc {
287	return h.UserAll(false, f, apiAuth)
288}
289
290// UserWebAPI handles endpoints that accept a user authorized either via the web (cookies) or an Authorization header.
291func (h *Handler) UserWebAPI(f userHandlerFunc) http.HandlerFunc {
292	return h.UserAll(false, f, func(app *App, r *http.Request) (*User, error) {
293		// Authorize user via cookies
294		u := getUserSession(app, r)
295		if u != nil {
296			return u, nil
297		}
298
299		// Fall back to access token, since user isn't logged in via web
300		var err error
301		u, err = apiAuth(app, r)
302		if err != nil {
303			return nil, err
304		}
305
306		return u, nil
307	})
308}
309
310func (h *Handler) UserAll(web bool, f userHandlerFunc, a authFunc) http.HandlerFunc {
311	return func(w http.ResponseWriter, r *http.Request) {
312		handleFunc := func() error {
313			var status int
314			start := time.Now()
315
316			defer func() {
317				if e := recover(); e != nil {
318					log.Error("%s: %s", e, debug.Stack())
319					impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "Something didn't work quite right."})
320					status = 500
321				}
322
323				log.Info(h.app.ReqLog(r, status, time.Since(start)))
324			}()
325
326			u, err := a(h.app.App(), r)
327			if err != nil {
328				if err, ok := err.(impart.HTTPError); ok {
329					status = err.Status
330				} else {
331					status = 500
332				}
333				return err
334			}
335
336			err = f(h.app.App(), u, w, r)
337			if err == nil {
338				status = 200
339			} else if err, ok := err.(impart.HTTPError); ok {
340				status = err.Status
341			} else {
342				status = 500
343			}
344
345			return err
346		}
347
348		if web {
349			h.handleHTTPError(w, r, handleFunc())
350		} else {
351			h.handleError(w, r, handleFunc())
352		}
353	}
354}
355
356func (h *Handler) RedirectOnErr(f handlerFunc, loc string) handlerFunc {
357	return func(app *App, w http.ResponseWriter, r *http.Request) error {
358		err := f(app, w, r)
359		if err != nil {
360			if ie, ok := err.(impart.HTTPError); ok {
361				// Override default redirect with returned error's, if it's a
362				// redirect error.
363				if ie.Status == http.StatusFound {
364					return ie
365				}
366			}
367			return impart.HTTPError{http.StatusFound, loc}
368		}
369		return nil
370	}
371}
372
373func (h *Handler) Page(n string) http.HandlerFunc {
374	return h.Web(func(app *App, w http.ResponseWriter, r *http.Request) error {
375		t, ok := pages[n]
376		if !ok {
377			return impart.HTTPError{http.StatusNotFound, "Page not found."}
378		}
379
380		sp := pageForReq(app, r)
381
382		err := t.ExecuteTemplate(w, "base", sp)
383		if err != nil {
384			log.Error("Unable to render page: %v", err)
385		}
386		return err
387	}, UserLevelOptional)
388}
389
390func (h *Handler) WebErrors(f handlerFunc, ul UserLevelFunc) http.HandlerFunc {
391	return func(w http.ResponseWriter, r *http.Request) {
392		// TODO: factor out this logic shared with Web()
393		h.handleHTTPError(w, r, func() error {
394			var status int
395			start := time.Now()
396
397			defer func() {
398				if e := recover(); e != nil {
399					u := getUserSession(h.app.App(), r)
400					username := "None"
401					if u != nil {
402						username = u.Username
403					}
404					log.Error("User: %s\n\n%s: %s", username, e, debug.Stack())
405					h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
406					status = 500
407				}
408
409				log.Info(h.app.ReqLog(r, status, time.Since(start)))
410			}()
411
412			var session *sessions.Session
413			var err error
414			if ul(h.app.App().cfg) != UserLevelNoneType {
415				session, err = h.sessionStore.Get(r, cookieName)
416				if err != nil && (ul(h.app.App().cfg) == UserLevelNoneRequiredType || ul(h.app.App().cfg) == UserLevelUserType) {
417					// Cookie is required, but we can ignore this error
418					log.Error("Handler: Unable to get session (for user permission %d); ignoring: %v", ul(h.app.App().cfg), err)
419				}
420
421				_, gotUser := session.Values[cookieUserVal].(*User)
422				if ul(h.app.App().cfg) == UserLevelNoneRequiredType && gotUser {
423					to := correctPageFromLoginAttempt(r)
424					log.Info("Handler: Required NO user, but got one. Redirecting to %s", to)
425					err := impart.HTTPError{http.StatusFound, to}
426					status = err.Status
427					return err
428				} else if ul(h.app.App().cfg) == UserLevelUserType && !gotUser {
429					log.Info("Handler: Required a user, but DIDN'T get one. Sending not logged in.")
430					err := ErrNotLoggedIn
431					status = err.Status
432					return err
433				}
434			}
435
436			// TODO: pass User object to function
437			err = f(h.app.App(), w, r)
438			if err == nil {
439				status = 200
440			} else if httpErr, ok := err.(impart.HTTPError); ok {
441				status = httpErr.Status
442				if status < 300 || status > 399 {
443					addSessionFlash(h.app.App(), w, r, httpErr.Message, session)
444					return impart.HTTPError{http.StatusFound, r.Referer()}
445				}
446			} else {
447				e := fmt.Sprintf("[Web handler] 500: %v", err)
448				if !strings.HasSuffix(e, "write: broken pipe") {
449					log.Error(e)
450				} else {
451					log.Error(e)
452				}
453				log.Info("Web handler internal error render")
454				h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
455				status = 500
456			}
457
458			return err
459		}())
460	}
461}
462
463func (h *Handler) CollectionPostOrStatic(w http.ResponseWriter, r *http.Request) {
464	if strings.Contains(r.URL.Path, ".") && !isRaw(r) {
465		start := time.Now()
466		status := 200
467		defer func() {
468			log.Info(h.app.ReqLog(r, status, time.Since(start)))
469		}()
470
471		// Serve static file
472		h.app.App().shttp.ServeHTTP(w, r)
473		return
474	}
475
476	h.Web(viewCollectionPost, UserLevelReader)(w, r)
477}
478
479// Web handles requests made in the web application. This provides user-
480// friendly HTML pages and actions that work in the browser.
481func (h *Handler) Web(f handlerFunc, ul UserLevelFunc) http.HandlerFunc {
482	return func(w http.ResponseWriter, r *http.Request) {
483		h.handleHTTPError(w, r, func() error {
484			var status int
485			start := time.Now()
486
487			defer func() {
488				if e := recover(); e != nil {
489					u := getUserSession(h.app.App(), r)
490					username := "None"
491					if u != nil {
492						username = u.Username
493					}
494					log.Error("User: %s\n\n%s: %s", username, e, debug.Stack())
495					log.Info("Web deferred internal error render")
496					h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
497					status = 500
498				}
499
500				log.Info(h.app.ReqLog(r, status, time.Since(start)))
501			}()
502
503			if ul(h.app.App().cfg) != UserLevelNoneType {
504				session, err := h.sessionStore.Get(r, cookieName)
505				if err != nil && (ul(h.app.App().cfg) == UserLevelNoneRequiredType || ul(h.app.App().cfg) == UserLevelUserType) {
506					// Cookie is required, but we can ignore this error
507					log.Error("Handler: Unable to get session (for user permission %d); ignoring: %v", ul(h.app.App().cfg), err)
508				}
509
510				_, gotUser := session.Values[cookieUserVal].(*User)
511				if ul(h.app.App().cfg) == UserLevelNoneRequiredType && gotUser {
512					to := correctPageFromLoginAttempt(r)
513					log.Info("Handler: Required NO user, but got one. Redirecting to %s", to)
514					err := impart.HTTPError{http.StatusFound, to}
515					status = err.Status
516					return err
517				} else if ul(h.app.App().cfg) == UserLevelUserType && !gotUser {
518					log.Info("Handler: Required a user, but DIDN'T get one. Sending not logged in.")
519					err := ErrNotLoggedIn
520					status = err.Status
521					return err
522				}
523			}
524
525			// TODO: pass User object to function
526			err := f(h.app.App(), w, r)
527			if err == nil {
528				status = 200
529			} else if httpErr, ok := err.(impart.HTTPError); ok {
530				status = httpErr.Status
531			} else {
532				e := fmt.Sprintf("[Web handler] 500: %v", err)
533				log.Error(e)
534				log.Info("Web internal error render")
535				h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
536				status = 500
537			}
538
539			return err
540		}())
541	}
542}
543
544func (h *Handler) All(f handlerFunc) http.HandlerFunc {
545	return func(w http.ResponseWriter, r *http.Request) {
546		h.handleError(w, r, func() error {
547			// TODO: return correct "success" status
548			status := 200
549			start := time.Now()
550
551			defer func() {
552				if e := recover(); e != nil {
553					log.Error("%s:\n%s", e, debug.Stack())
554					impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "Something didn't work quite right."})
555					status = 500
556				}
557
558				log.Info(h.app.ReqLog(r, status, time.Since(start)))
559			}()
560
561			// TODO: do any needed authentication
562
563			err := f(h.app.App(), w, r)
564			if err != nil {
565				if err, ok := err.(impart.HTTPError); ok {
566					status = err.Status
567				} else {
568					status = 500
569				}
570			}
571
572			return err
573		}())
574	}
575}
576
577func (h *Handler) PlainTextAPI(f handlerFunc) http.HandlerFunc {
578	return func(w http.ResponseWriter, r *http.Request) {
579		h.handleTextError(w, r, func() error {
580			// TODO: return correct "success" status
581			status := 200
582			start := time.Now()
583
584			defer func() {
585				if e := recover(); e != nil {
586					log.Error("%s:\n%s", e, debug.Stack())
587					status = http.StatusInternalServerError
588					w.WriteHeader(status)
589					fmt.Fprintf(w, "Something didn't work quite right. The robots have alerted the humans.")
590				}
591
592				log.Info(fmt.Sprintf("\"%s %s\" %d %s \"%s\" \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent(), r.Host))
593			}()
594
595			err := f(h.app.App(), w, r)
596			if err != nil {
597				if err, ok := err.(impart.HTTPError); ok {
598					status = err.Status
599				} else {
600					status = http.StatusInternalServerError
601				}
602			}
603
604			return err
605		}())
606	}
607}
608
609func (h *Handler) OAuth(f handlerFunc) http.HandlerFunc {
610	return func(w http.ResponseWriter, r *http.Request) {
611		h.handleOAuthError(w, r, func() error {
612			// TODO: return correct "success" status
613			status := 200
614			start := time.Now()
615
616			defer func() {
617				if e := recover(); e != nil {
618					log.Error("%s:\n%s", e, debug.Stack())
619					impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "Something didn't work quite right."})
620					status = 500
621				}
622
623				log.Info(h.app.ReqLog(r, status, time.Since(start)))
624			}()
625
626			err := f(h.app.App(), w, r)
627			if err != nil {
628				if err, ok := err.(impart.HTTPError); ok {
629					status = err.Status
630				} else {
631					status = 500
632				}
633			}
634
635			return err
636		}())
637	}
638}
639
640func (h *Handler) AllReader(f handlerFunc) http.HandlerFunc {
641	return func(w http.ResponseWriter, r *http.Request) {
642		h.handleError(w, r, func() error {
643			status := 200
644			start := time.Now()
645
646			defer func() {
647				if e := recover(); e != nil {
648					log.Error("%s:\n%s", e, debug.Stack())
649					impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "Something didn't work quite right."})
650					status = 500
651				}
652
653				log.Info(h.app.ReqLog(r, status, time.Since(start)))
654			}()
655
656			// Allow any origin, as public endpoints are handled in here
657			w.Header().Set("Access-Control-Allow-Origin", "*")
658
659			if h.app.App().cfg.App.Private {
660				// This instance is private, so ensure it's being accessed by a valid user
661				// Check if authenticated with an access token
662				_, apiErr := optionalAPIAuth(h.app.App(), r)
663				if apiErr != nil {
664					if err, ok := apiErr.(impart.HTTPError); ok {
665						status = err.Status
666					} else {
667						status = 500
668					}
669
670					if apiErr == ErrNotLoggedIn {
671						// Fall back to web auth since there was no access token given
672						_, err := webAuth(h.app.App(), r)
673						if err != nil {
674							if err, ok := apiErr.(impart.HTTPError); ok {
675								status = err.Status
676							} else {
677								status = 500
678							}
679							return err
680						}
681					} else {
682						return apiErr
683					}
684				}
685			}
686
687			err := f(h.app.App(), w, r)
688			if err != nil {
689				if err, ok := err.(impart.HTTPError); ok {
690					status = err.Status
691				} else {
692					status = 500
693				}
694			}
695
696			return err
697		}())
698	}
699}
700
701func (h *Handler) Download(f dataHandlerFunc, ul UserLevelFunc) http.HandlerFunc {
702	return func(w http.ResponseWriter, r *http.Request) {
703		h.handleHTTPError(w, r, func() error {
704			var status int
705			start := time.Now()
706			defer func() {
707				if e := recover(); e != nil {
708					log.Error("%s: %s", e, debug.Stack())
709					h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
710					status = 500
711				}
712
713				log.Info(h.app.ReqLog(r, status, time.Since(start)))
714			}()
715
716			data, filename, err := f(h.app.App(), w, r)
717			if err != nil {
718				if err, ok := err.(impart.HTTPError); ok {
719					status = err.Status
720				} else {
721					status = 500
722				}
723				return err
724			}
725
726			ext := ".json"
727			ct := "application/json"
728			if strings.HasSuffix(r.URL.Path, ".csv") {
729				ext = ".csv"
730				ct = "text/csv"
731			} else if strings.HasSuffix(r.URL.Path, ".zip") {
732				ext = ".zip"
733				ct = "application/zip"
734			}
735			w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s%s", filename, ext))
736			w.Header().Set("Content-Type", ct)
737			w.Header().Set("Content-Length", strconv.Itoa(len(data)))
738			fmt.Fprint(w, string(data))
739
740			status = 200
741			return nil
742		}())
743	}
744}
745
746func (h *Handler) Redirect(url string, ul UserLevelFunc) http.HandlerFunc {
747	return func(w http.ResponseWriter, r *http.Request) {
748		h.handleHTTPError(w, r, func() error {
749			start := time.Now()
750
751			var status int
752			if ul(h.app.App().cfg) != UserLevelNoneType {
753				session, err := h.sessionStore.Get(r, cookieName)
754				if err != nil && (ul(h.app.App().cfg) == UserLevelNoneRequiredType || ul(h.app.App().cfg) == UserLevelUserType) {
755					// Cookie is required, but we can ignore this error
756					log.Error("Handler: Unable to get session (for user permission %d); ignoring: %v", ul(h.app.App().cfg), err)
757				}
758
759				_, gotUser := session.Values[cookieUserVal].(*User)
760				if ul(h.app.App().cfg) == UserLevelNoneRequiredType && gotUser {
761					to := correctPageFromLoginAttempt(r)
762					log.Info("Handler: Required NO user, but got one. Redirecting to %s", to)
763					err := impart.HTTPError{http.StatusFound, to}
764					status = err.Status
765					return err
766				} else if ul(h.app.App().cfg) == UserLevelUserType && !gotUser {
767					log.Info("Handler: Required a user, but DIDN'T get one. Sending not logged in.")
768					err := ErrNotLoggedIn
769					status = err.Status
770					return err
771				}
772			}
773
774			status = sendRedirect(w, http.StatusFound, url)
775
776			log.Info(h.app.ReqLog(r, status, time.Since(start)))
777
778			return nil
779		}())
780	}
781}
782
783func (h *Handler) handleHTTPError(w http.ResponseWriter, r *http.Request, err error) {
784	if err == nil {
785		return
786	}
787
788	if err, ok := err.(impart.HTTPError); ok {
789		if err.Status >= 300 && err.Status < 400 {
790			sendRedirect(w, err.Status, err.Message)
791			return
792		} else if err.Status == http.StatusUnauthorized {
793			q := ""
794			if r.URL.RawQuery != "" {
795				q = url.QueryEscape("?" + r.URL.RawQuery)
796			}
797			sendRedirect(w, http.StatusFound, "/login?to="+r.URL.Path+q)
798			return
799		} else if err.Status == http.StatusGone {
800			w.WriteHeader(err.Status)
801			p := &struct {
802				page.StaticPage
803				Content *template.HTML
804			}{
805				StaticPage: pageForReq(h.app.App(), r),
806			}
807			if err.Message != "" {
808				co := template.HTML(err.Message)
809				p.Content = &co
810			}
811			h.errors.Gone.ExecuteTemplate(w, "base", p)
812			return
813		} else if err.Status == http.StatusNotFound {
814			w.WriteHeader(err.Status)
815			if strings.Contains(r.Header.Get("Accept"), "application/activity+json") {
816				// This is a fediverse request; simply return the header
817				return
818			}
819			h.errors.NotFound.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
820			return
821		} else if err.Status == http.StatusInternalServerError {
822			w.WriteHeader(err.Status)
823			log.Info("handleHTTPErorr internal error render")
824			h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
825			return
826		} else if err.Status == http.StatusServiceUnavailable {
827			w.WriteHeader(err.Status)
828			h.errors.UnavailableError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
829			return
830		} else if err.Status == http.StatusAccepted {
831			impart.WriteSuccess(w, "", err.Status)
832			return
833		} else {
834			p := &struct {
835				page.StaticPage
836				Title   string
837				Content template.HTML
838			}{
839				pageForReq(h.app.App(), r),
840				fmt.Sprintf("Uh oh (%d)", err.Status),
841				template.HTML(fmt.Sprintf("<p style=\"text-align: center\" class=\"introduction\">%s</p>", err.Message)),
842			}
843			h.errors.Blank.ExecuteTemplate(w, "base", p)
844			return
845		}
846		impart.WriteError(w, err)
847		return
848	}
849
850	impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "This is an unhelpful error message for a miscellaneous internal error."})
851}
852
853func (h *Handler) handleError(w http.ResponseWriter, r *http.Request, err error) {
854	if err == nil {
855		return
856	}
857
858	if err, ok := err.(impart.HTTPError); ok {
859		if err.Status >= 300 && err.Status < 400 {
860			sendRedirect(w, err.Status, err.Message)
861			return
862		}
863
864		//		if strings.Contains(r.Header.Get("Accept"), "text/html") {
865		impart.WriteError(w, err)
866		//		}
867		return
868	}
869
870	if IsJSON(r) {
871		impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "This is an unhelpful error message for a miscellaneous internal error."})
872		return
873	}
874	h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
875}
876
877func (h *Handler) handleTextError(w http.ResponseWriter, r *http.Request, err error) {
878	if err == nil {
879		return
880	}
881
882	if err, ok := err.(impart.HTTPError); ok {
883		if err.Status >= 300 && err.Status < 400 {
884			sendRedirect(w, err.Status, err.Message)
885			return
886		}
887
888		w.WriteHeader(err.Status)
889		fmt.Fprintf(w, http.StatusText(err.Status))
890		return
891	}
892
893	w.WriteHeader(http.StatusInternalServerError)
894	fmt.Fprintf(w, "This is an unhelpful error message for a miscellaneous internal error.")
895}
896
897func (h *Handler) handleOAuthError(w http.ResponseWriter, r *http.Request, err error) {
898	if err == nil {
899		return
900	}
901
902	if err, ok := err.(impart.HTTPError); ok {
903		if err.Status >= 300 && err.Status < 400 {
904			sendRedirect(w, err.Status, err.Message)
905			return
906		}
907
908		impart.WriteOAuthError(w, err)
909		return
910	}
911
912	impart.WriteOAuthError(w, impart.HTTPError{http.StatusInternalServerError, "This is an unhelpful error message for a miscellaneous internal error."})
913	return
914}
915
916func correctPageFromLoginAttempt(r *http.Request) string {
917	to := r.FormValue("to")
918	if to == "" {
919		to = "/"
920	} else if !strings.HasPrefix(to, "/") {
921		to = "/" + to
922	}
923	return to
924}
925
926func (h *Handler) LogHandlerFunc(f http.HandlerFunc) http.HandlerFunc {
927	return func(w http.ResponseWriter, r *http.Request) {
928		h.handleHTTPError(w, r, func() error {
929			status := 200
930			start := time.Now()
931
932			defer func() {
933				if e := recover(); e != nil {
934					log.Error("Handler.LogHandlerFunc\n\n%s: %s", e, debug.Stack())
935					h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
936					status = 500
937				}
938
939				// TODO: log actual status code returned
940				log.Info(h.app.ReqLog(r, status, time.Since(start)))
941			}()
942
943			if h.app.App().cfg.App.Private {
944				// This instance is private, so ensure it's being accessed by a valid user
945				// Check if authenticated with an access token
946				_, apiErr := optionalAPIAuth(h.app.App(), r)
947				if apiErr != nil {
948					if err, ok := apiErr.(impart.HTTPError); ok {
949						status = err.Status
950					} else {
951						status = 500
952					}
953
954					if apiErr == ErrNotLoggedIn {
955						// Fall back to web auth since there was no access token given
956						_, err := webAuth(h.app.App(), r)
957						if err != nil {
958							if err, ok := apiErr.(impart.HTTPError); ok {
959								status = err.Status
960							} else {
961								status = 500
962							}
963							return err
964						}
965					} else {
966						return apiErr
967					}
968				}
969			}
970
971			f(w, r)
972
973			return nil
974		}())
975	}
976}
977
978func (h *Handler) Gopher(f gopherFunc) gopher.HandlerFunc {
979	return func(w gopher.ResponseWriter, r *gopher.Request) {
980		defer func() {
981			if e := recover(); e != nil {
982				log.Error("%s: %s", e, debug.Stack())
983				w.WriteError("An internal error occurred")
984			}
985			log.Info("gopher: %s", r.Selector)
986		}()
987
988		err := f(h.app.App(), w, r)
989		if err != nil {
990			log.Error("failed: %s", err)
991			w.WriteError("the page failed for some reason (see logs)")
992		}
993	}
994}
995
996func sendRedirect(w http.ResponseWriter, code int, location string) int {
997	w.Header().Set("Location", location)
998	w.WriteHeader(code)
999	return code
1000}
1001
1002func cacheControl(next http.Handler) http.Handler {
1003	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1004		w.Header().Set("Cache-Control", "public, max-age=604800, immutable")
1005		next.ServeHTTP(w, r)
1006	})
1007}
1008