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.
 
 
 
 
 

849 lines
24 KiB

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