A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1014 lines
28 KiB

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