Compare commits

...

5 Commits

  1. 4
      README.md
  2. 15
      api/http/index.go
  3. 4
      api/http/static/ab0x.go
  4. 6
      api/http/static/b0xfile__error.html.go
  5. 35
      api/http/static/b0xfile__footer.html.go
  6. 6
      api/http/static/b0xfile__index.html.go
  7. 35
      api/http/static/b0xfile__main.html.go
  8. 35
      api/http/static/b0xfile__navigation.html.go
  9. 6
      api/http/static/b0xfile__passworded_paste_verify.html.go
  10. 6
      api/http/static/b0xfile__paste.html.go
  11. 6
      api/http/static/b0xfile__pastelist_list.html.go
  12. 43
      assets/error.html
  13. 11
      assets/footer.html
  14. 174
      assets/index.html
  15. 20
      assets/main.html
  16. 16
      assets/navigation.html
  17. 87
      assets/passworded_paste_verify.html
  18. 103
      assets/paste.html
  19. 59
      assets/pastelist_list.html
  20. 2
      cmd/fastpastebin/fastpastebin.go
  21. 17
      context/context.go
  22. 5
      context/exported.go
  23. 3
      fileb0x.yml
  24. 163
      pastes/api_http.go
  25. 124
      templater/exported.go

@ -3,9 +3,7 @@
# Fast Pastebin
Easy-to-use-and-install pastebin software written in Go. No bells or
whistles, no websockets and even NO JAVASCRIPT!(*)
(*) Except fontawesome, because it's awesome :).
whistles, no websockets and even NO JAVASCRIPT!
# Current functionality.

@ -27,11 +27,10 @@ package http
import (
// stdlib
"net/http"
"strings"
// local
"github.com/pztrn/fastpastebin/api/http/static"
"github.com/pztrn/fastpastebin/captcha"
"github.com/pztrn/fastpastebin/templater"
// other
"github.com/alecthomas/chroma/lexers"
@ -40,11 +39,6 @@ import (
// Index of this site.
func indexGet(ec echo.Context) error {
htmlRaw, err := static.ReadFile("index.html")
if err != nil {
return ec.String(http.StatusNotFound, "index.html wasn't found!")
}
// Generate list of available languages to highlight.
availableLexers := lexers.Names(false)
@ -53,11 +47,10 @@ func indexGet(ec echo.Context) error {
availableLexersSelectOpts += "<option value='" + availableLexers[i] + "'>" + availableLexers[i] + "</option>"
}
html := strings.Replace(string(htmlRaw), "{lexers}", availableLexersSelectOpts, 1)
// Captcha.
captchaString := captcha.NewCaptcha()
html = strings.Replace(html, "{captchaString}", captchaString, -1)
return ec.HTML(http.StatusOK, html)
htmlData := templater.GetTemplate(ec, "index.html", map[string]string{"lexers": availableLexersSelectOpts, "captchaString": captchaString})
return ec.HTML(http.StatusOK, htmlData)
}

@ -1,5 +1,5 @@
// Code generated by fileb0x at "2018-05-18 22:25:13.180720675 +0500 +05 m=+0.030971779" from config file "fileb0x.yml" DO NOT EDIT.
// modification hash(1679c5529785a2e624071b2679319ecb.608888160ddb195ed89fce5ab290ff92)
// Code generated by fileb0x at "2018-05-26 13:58:30.612342807 +0500 +05 m=+0.013490397" from config file "fileb0x.yml" DO NOT EDIT.
// modification hash(e9e210106a4d77680942064c40d06a3e.522a9cc525fa984df98098d4c3992752)
package static

@ -1,5 +1,5 @@
// Code generaTed by fileb0x at "2018-05-01 18:35:23.702577304 +0500 +05 m=+0.047872409" from config file "fileb0x.yml" DO NOT EDIT.
// modified(2018-05-01 18:35:02 +0500 +05)
// Code generaTed by fileb0x at "2018-05-26 13:29:06.05698436 +0500 +05 m=+0.013970976" from config file "fileb0x.yml" DO NOT EDIT.
// modified(2018-05-26 13:29:04.002355958 +0500 +05)
// original path: assets/error.html
package static
@ -10,7 +10,7 @@ import (
)
// FileErrorHTML is "/error.html"
var FileErrorHTML = []byte("\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x68\x74\x6d\x6c\x3e\x0a\x3c\x68\x74\x6d\x6c\x3e\x0a\x0a\x3c\x68\x65\x61\x64\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3d\x22\x75\x74\x66\x2d\x38\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x6e\x61\x6d\x65\x3d\x22\x76\x69\x65\x77\x70\x6f\x72\x74\x22\x20\x63\x6f\x6e\x74\x65\x6e\x74\x3d\x22\x77\x69\x64\x74\x68\x3d\x64\x65\x76\x69\x63\x65\x2d\x77\x69\x64\x74\x68\x2c\x20\x69\x6e\x69\x74\x69\x61\x6c\x2d\x73\x63\x61\x6c\x65\x3d\x31\x22\x3e\x0a\x20\x20\x20\x20\x3c\x74\x69\x74\x6c\x65\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x20\x2d\x20\x45\x52\x52\x4f\x52\x21\x3c\x2f\x74\x69\x74\x6c\x65\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x62\x75\x6c\x6d\x61\x2d\x30\x2e\x37\x2e\x30\x2e\x6d\x69\x6e\x2e\x63\x73\x73\x22\x3e\x0a\x20\x20\x20\x20\x3c\x73\x63\x72\x69\x70\x74\x20\x64\x65\x66\x65\x72\x20\x73\x72\x63\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x6a\x73\x2f\x66\x6f\x6e\x74\x61\x77\x65\x73\x6f\x6d\x65\x2d\x35\x2e\x30\x2e\x37\x2e\x6a\x73\x22\x3e\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x22\x3e\x0a\x3c\x2f\x68\x65\x61\x64\x3e\x0a\x0a\x3c\x62\x6f\x64\x79\x3e\x0a\x20\x20\x20\x20\x3c\x6e\x61\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x20\x69\x73\x2d\x64\x61\x72\x6b\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x72\x61\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x22\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x70\x61\x73\x74\x65\x73\x2f\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x50\x61\x73\x74\x65\x73\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x75\x72\x67\x65\x72\x20\x62\x75\x72\x67\x65\x72\x22\x20\x64\x61\x74\x61\x2d\x74\x61\x72\x67\x65\x74\x3d\x22\x6e\x61\x76\x62\x61\x72\x49\x74\x65\x6d\x73\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x69\x64\x3d\x22\x6e\x61\x76\x62\x61\x72\x49\x74\x65\x6d\x73\x22\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x6d\x65\x6e\x75\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x73\x74\x61\x72\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x65\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6e\x61\x76\x3e\x0a\x20\x20\x20\x20\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x3e\x7b\x65\x72\x72\x6f\x72\x7d\x3c\x2f\x70\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e\x0a\x3c\x2f\x62\x6f\x64\x79\x3e\x0a\x0a\x3c\x2f\x68\x74\x6d\x6c\x3e")
var FileErrorHTML = []byte("\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x62\x6f\x78\x20\x68\x61\x73\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x2d\x77\x61\x72\x6e\x69\x6e\x67\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x3e\x7b\x65\x72\x72\x6f\x72\x7d\x3c\x2f\x70\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e")
func init() {

@ -0,0 +1,35 @@
// Code generaTed by fileb0x at "2018-05-26 12:55:42.087956217 +0500 +05 m=+0.009637095" from config file "fileb0x.yml" DO NOT EDIT.
// modified(2018-05-26 12:54:22.9990735 +0500 +05)
// original path: assets/footer.html
package static
import (
"os"
)
// FileFooterHTML is "/footer.html"
var FileFooterHTML = []byte("\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6f\x6e\x74\x61\x69\x6e\x65\x72\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x63\x6f\x6e\x74\x65\x6e\x74\x20\x68\x61\x73\x2d\x74\x65\x78\x74\x2d\x63\x65\x6e\x74\x65\x72\x65\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x70\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x72\x6f\x6e\x67\x3e\x46\x61\x73\x74\x20\x70\x61\x73\x74\x65\x20\x62\x69\x6e\x3c\x2f\x73\x74\x72\x6f\x6e\x67\x3e\x20\x76\x65\x72\x73\x69\x6f\x6e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x74\x72\x6f\x6e\x67\x3e\x7b\x76\x65\x72\x73\x69\x6f\x6e\x7d\x3c\x2f\x73\x74\x72\x6f\x6e\x67\x3e\x20\x62\x79\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x68\x74\x74\x70\x73\x3a\x2f\x2f\x70\x7a\x74\x72\x6e\x2e\x6e\x61\x6d\x65\x22\x3e\x53\x74\x61\x6e\x69\x73\x6c\x61\x76\x20\x4e\x2e\x20\x61\x6b\x61\x20\x70\x7a\x74\x72\x6e\x3c\x2f\x61\x3e\x2e\x20\x54\x68\x65\x20\x73\x6f\x75\x72\x63\x65\x20\x63\x6f\x64\x65\x20\x69\x73\x20\x6c\x69\x63\x65\x6e\x73\x65\x64\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x6f\x70\x65\x6e\x73\x6f\x75\x72\x63\x65\x2e\x6f\x72\x67\x2f\x6c\x69\x63\x65\x6e\x73\x65\x73\x2f\x6d\x69\x74\x2d\x6c\x69\x63\x65\x6e\x73\x65\x2e\x70\x68\x70\x22\x3e\x4d\x49\x54\x3c\x2f\x61\x3e\x2e\x20\x47\x65\x74\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x68\x74\x74\x70\x73\x3a\x2f\x2f\x67\x69\x74\x68\x75\x62\x2e\x63\x6f\x6d\x2f\x70\x7a\x74\x72\x6e\x2f\x66\x61\x73\x74\x70\x61\x73\x74\x65\x62\x69\x6e\x22\x3e\x73\x6f\x75\x72\x63\x65\x20\x6f\x72\x20\x62\x69\x6e\x61\x72\x79\x20\x72\x65\x6c\x65\x61\x73\x65\x73\x20\x68\x65\x72\x65\x3c\x2f\x61\x3e\x21\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x70\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x64\x69\x76\x3e")
func init() {
f, err := FS.OpenFile(CTX, "/footer.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
if err != nil {
panic(err)
}
_, err = f.Write(FileFooterHTML)
if err != nil {
panic(err)
}
err = f.Close()
if err != nil {
panic(err)
}
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,35 @@
// Code generaTed by fileb0x at "2018-05-26 12:42:22.452857215 +0500 +05 m=+0.054711585" from config file "fileb0x.yml" DO NOT EDIT.
// modified(2018-05-26 12:41:36.015758918 +0500 +05)
// original path: assets/main.html
package static
import (
"os"
)
// FileMainHTML is "/main.html"
var FileMainHTML = []byte("\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x68\x74\x6d\x6c\x3e\x0a\x3c\x68\x74\x6d\x6c\x3e\x0a\x0a\x3c\x68\x65\x61\x64\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3d\x22\x75\x74\x66\x2d\x38\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x6e\x61\x6d\x65\x3d\x22\x76\x69\x65\x77\x70\x6f\x72\x74\x22\x20\x63\x6f\x6e\x74\x65\x6e\x74\x3d\x22\x77\x69\x64\x74\x68\x3d\x64\x65\x76\x69\x63\x65\x2d\x77\x69\x64\x74\x68\x2c\x20\x69\x6e\x69\x74\x69\x61\x6c\x2d\x73\x63\x61\x6c\x65\x3d\x31\x22\x3e\x0a\x20\x20\x20\x20\x3c\x74\x69\x74\x6c\x65\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x3c\x2f\x74\x69\x74\x6c\x65\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x62\x75\x6c\x6d\x61\x2d\x30\x2e\x37\x2e\x30\x2e\x6d\x69\x6e\x2e\x63\x73\x73\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x62\x75\x6c\x6d\x61\x2d\x74\x6f\x6f\x6c\x74\x69\x70\x2d\x31\x2e\x30\x2e\x34\x2e\x6d\x69\x6e\x2e\x63\x73\x73\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x22\x3e\x0a\x3c\x2f\x68\x65\x61\x64\x3e\x0a\x0a\x3c\x62\x6f\x64\x79\x3e\x0a\x20\x20\x20\x20\x7b\x6e\x61\x76\x69\x67\x61\x74\x69\x6f\x6e\x7d\x20\x7b\x64\x6f\x63\x75\x6d\x65\x6e\x74\x42\x6f\x64\x79\x7d\x0a\x3c\x2f\x62\x6f\x64\x79\x3e\x0a\x3c\x66\x6f\x6f\x74\x65\x72\x20\x63\x6c\x61\x73\x73\x3d\x22\x66\x6f\x6f\x74\x65\x72\x22\x3e\x0a\x20\x20\x20\x20\x7b\x66\x6f\x6f\x74\x65\x72\x7d\x0a\x3c\x2f\x66\x6f\x6f\x74\x65\x72\x3e\x0a\x0a\x3c\x2f\x68\x74\x6d\x6c\x3e")
func init() {
f, err := FS.OpenFile(CTX, "/main.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
if err != nil {
panic(err)
}
_, err = f.Write(FileMainHTML)
if err != nil {
panic(err)
}
err = f.Close()
if err != nil {
panic(err)
}
}

@ -0,0 +1,35 @@
// Code generaTed by fileb0x at "2018-05-26 13:58:30.613412852 +0500 +05 m=+0.014560424" from config file "fileb0x.yml" DO NOT EDIT.
// modified(2018-05-26 13:57:14.88231462 +0500 +05)
// original path: assets/navigation.html
package static
import (
"os"
)
// FileNavigationHTML is "/navigation.html"
var FileNavigationHTML = []byte("\x3c\x6e\x61\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x20\x69\x73\x2d\x64\x61\x72\x6b\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x72\x61\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x22\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x70\x61\x73\x74\x65\x73\x2f\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x50\x61\x73\x74\x65\x73\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x69\x64\x3d\x22\x6e\x61\x76\x62\x61\x72\x49\x74\x65\x6d\x73\x22\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x6d\x65\x6e\x75\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x73\x74\x61\x72\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x65\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x6e\x61\x76\x3e")
func init() {
f, err := FS.OpenFile(CTX, "/navigation.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
if err != nil {
panic(err)
}
_, err = f.Write(FileNavigationHTML)
if err != nil {
panic(err)
}
err = f.Close()
if err != nil {
panic(err)
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,5 +1,5 @@
// Code generaTed by fileb0x at "2018-05-18 22:25:13.191256649 +0500 +05 m=+0.041507879" from config file "fileb0x.yml" DO NOT EDIT.
// modified(2018-05-18 22:17:55.447214662 +0500 +05)
// Code generaTed by fileb0x at "2018-05-26 13:47:21.417964779 +0500 +05 m=+0.019030606" from config file "fileb0x.yml" DO NOT EDIT.
// modified(2018-05-26 13:41:01.75233841 +0500 +05)
// original path: assets/pastelist_list.html
package static
@ -10,7 +10,7 @@ import (
)
// FilePastelistListHTML is "/pastelist_list.html"
var FilePastelistListHTML = []byte("\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x68\x74\x6d\x6c\x3e\x0a\x3c\x68\x74\x6d\x6c\x3e\x0a\x0a\x3c\x68\x65\x61\x64\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x63\x68\x61\x72\x73\x65\x74\x3d\x22\x75\x74\x66\x2d\x38\x22\x3e\x0a\x20\x20\x20\x20\x3c\x6d\x65\x74\x61\x20\x6e\x61\x6d\x65\x3d\x22\x76\x69\x65\x77\x70\x6f\x72\x74\x22\x20\x63\x6f\x6e\x74\x65\x6e\x74\x3d\x22\x77\x69\x64\x74\x68\x3d\x64\x65\x76\x69\x63\x65\x2d\x77\x69\x64\x74\x68\x2c\x20\x69\x6e\x69\x74\x69\x61\x6c\x2d\x73\x63\x61\x6c\x65\x3d\x31\x22\x3e\x0a\x20\x20\x20\x20\x3c\x74\x69\x74\x6c\x65\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x20\x2d\x20\x50\x61\x73\x74\x65\x73\x3c\x2f\x74\x69\x74\x6c\x65\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x62\x75\x6c\x6d\x61\x2d\x30\x2e\x37\x2e\x30\x2e\x6d\x69\x6e\x2e\x63\x73\x73\x22\x3e\x0a\x20\x20\x20\x20\x3c\x73\x63\x72\x69\x70\x74\x20\x64\x65\x66\x65\x72\x20\x73\x72\x63\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x6a\x73\x2f\x66\x6f\x6e\x74\x61\x77\x65\x73\x6f\x6d\x65\x2d\x35\x2e\x30\x2e\x37\x2e\x6a\x73\x22\x3e\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e\x0a\x20\x20\x20\x20\x3c\x6c\x69\x6e\x6b\x20\x72\x65\x6c\x3d\x22\x73\x74\x79\x6c\x65\x73\x68\x65\x65\x74\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x73\x74\x61\x74\x69\x63\x2f\x63\x73\x73\x2f\x73\x74\x79\x6c\x65\x2e\x63\x73\x73\x22\x3e\x0a\x3c\x2f\x68\x65\x61\x64\x3e\x0a\x0a\x3c\x62\x6f\x64\x79\x3e\x0a\x20\x20\x20\x20\x3c\x6e\x61\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x20\x69\x73\x2d\x64\x61\x72\x6b\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x72\x61\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x22\x3e\x46\x61\x73\x74\x20\x50\x61\x73\x74\x65\x20\x42\x69\x6e\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x61\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x69\x74\x65\x6d\x22\x20\x68\x72\x65\x66\x3d\x22\x2f\x70\x61\x73\x74\x65\x73\x2f\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x50\x61\x73\x74\x65\x73\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x61\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x62\x75\x72\x67\x65\x72\x20\x62\x75\x72\x67\x65\x72\x22\x20\x64\x61\x74\x61\x2d\x74\x61\x72\x67\x65\x74\x3d\x22\x6e\x61\x76\x62\x61\x72\x49\x74\x65\x6d\x73\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x73\x70\x61\x6e\x3e\x3c\x2f\x73\x70\x61\x6e\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x69\x64\x3d\x22\x6e\x61\x76\x62\x61\x72\x49\x74\x65\x6d\x73\x22\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x6d\x65\x6e\x75\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x73\x74\x61\x72\x74\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x20\x63\x6c\x61\x73\x73\x3d\x22\x6e\x61\x76\x62\x61\x72\x2d\x65\x6e\x64\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x6e\x61\x76\x3e\x0a\x20\x20\x20\x20\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x73\x74\x65\x73\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e\x0a\x3c\x2f\x62\x6f\x64\x79\x3e\x0a\x0a\x3c\x2f\x68\x74\x6d\x6c\x3e")
var FilePastelistListHTML = []byte("\x3c\x73\x65\x63\x74\x69\x6f\x6e\x20\x63\x6c\x61\x73\x73\x3d\x22\x73\x65\x63\x74\x69\x6f\x6e\x22\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x7d\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x73\x74\x65\x73\x7d\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x3c\x64\x69\x76\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x70\x61\x67\x69\x6e\x61\x74\x69\x6f\x6e\x7d\x0a\x20\x20\x20\x20\x3c\x2f\x64\x69\x76\x3e\x0a\x3c\x2f\x73\x65\x63\x74\x69\x6f\x6e\x3e")
func init() {

@ -1,40 +1,5 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fast Paste Bin - ERROR!</title>
<link rel="stylesheet" href="/static/css/bulma-0.7.0.min.css">
<script defer src="/static/js/fontawesome-5.0.7.js"></script>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<nav class="navbar is-dark">
<div class="navbar-brand">
<a class="navbar-item" href="/">Fast Paste Bin</a>
<a class="navbar-item" href="/pastes/">
Pastes
</a>
<div class="navbar-burger burger" data-target="navbarItems">
<span></span>
<span></span>
<span></span>
</div>
</div>
<div id="navbarItems" class="navbar-menu">
<div class="navbar-start">
</div>
<div class="navbar-end">
</div>
</div>
</nav>
<section class="section">
<section class="section">
<div class="box has-background-warning">
<p>{error}</p>
</section>
</body>
</html>
</div>
</section>

@ -0,0 +1,11 @@
<div class="container">
<div class="content has-text-centered">
<p>
<strong>Fast paste bin</strong> version
<strong>{version}</strong> by
<a href="https://pztrn.name">Stanislav N. aka pztrn</a>. The source code is licensed
<a href="http://opensource.org/licenses/mit-license.php">MIT</a>. Get
<a href="https://github.com/pztrn/fastpastebin">source or binary releases here</a>!
</p>
</div>
</div>

@ -1,120 +1,82 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fast Paste Bin</title>
<link rel="stylesheet" href="/static/css/bulma-0.7.0.min.css">
<link rel="stylesheet" href="/static/css/bulma-tooltip-1.0.4.min.css">
<script defer src="/static/js/fontawesome-5.0.7.js"></script>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<nav class="navbar is-dark">
<div class="navbar-brand">
<a class="navbar-item" href="/">Fast Paste Bin</a>
<a class="navbar-item" href="/pastes/">
Pastes
</a>
<div class="navbar-burger burger" data-target="navbarItems">
<span></span>
<span></span>
<span></span>
</div>
</div>
<div id="navbarItems" class="navbar-menu">
<div class="navbar-start">
</div>
<div class="navbar-end">
<section class="section">
<form action="/paste/" method="POST" autocomplete="off">
<div class="columns">
<div class="column is-8 field">
<div class="control">
<textarea class="textarea" placeholder="Paste contents" name="paste-contents" id="paste-contents"></textarea>
</div>
</div>
</div>
</nav>
<section class="section">
<form action="/paste/" method="POST" autocomplete="off">
<div class="columns">
<div class="column is-8 field">
<div class="column is-4">
<div class="field">
<div class="control">
<textarea class="textarea" placeholder="Paste contents" name="paste-contents" id="paste-contents"></textarea>
<input class="input" type="text" placeholder="Paste title" name="paste-title" id="paste-title">
</div>
</div>
<div class="column is-4">
<div class="field">
<div class="control">
<input class="input" type="text" placeholder="Paste title" name="paste-title" id="paste-title">
</div>
</div>
<div class="field">
<label for="paste-keep-for">Paste should be available for:</label>
<div class="control">
<div class="select">
<select id="paste-keep-for" name="paste-keep-for">
<option value="30M">30 minutes</option>
<option value="1h">1 hour</option>
<option value="3h">3 hours</option>
<option value="6h">6 hours</option>
<option value="12h">12 hours</option>
<option value="1d">1 day</option>
<option value="2d">2 days</option>
<option value="3d">3 days</option>
<option value="4d">4 days</option>
<option value="5d">5 days</option>
<option value="7d">7 days</option>
<option value="14d">14 days</option>
<option value="21d">21 days</option>
<option value="1m">1 monts</option>
<option value="3m">3 months</option>
<option value="6m">6 months</option>
<option value="forever">Forever</option>
</select>
</div>
<div class="field">
<label for="paste-keep-for">Paste should be available for:</label>
<div class="control">
<div class="select">
<select id="paste-keep-for" name="paste-keep-for">
<option value="30M">30 minutes</option>
<option value="1h">1 hour</option>
<option value="3h">3 hours</option>
<option value="6h">6 hours</option>
<option value="12h">12 hours</option>
<option value="1d">1 day</option>
<option value="2d">2 days</option>
<option value="3d">3 days</option>
<option value="4d">4 days</option>
<option value="5d">5 days</option>
<option value="7d">7 days</option>
<option value="14d">14 days</option>
<option value="21d">21 days</option>
<option value="1m">1 monts</option>
<option value="3m">3 months</option>
<option value="6m">6 months</option>
<option value="forever">Forever</option>
</select>
</div>
</div>
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="Should this paste be accessible only with special URL and not shown in pastes list? WARNING: If you'll enter password into 'Password for paste' field this checkbox will be assumed as checked!">
<label class="checkbox">
<input type="checkbox" name="paste-private" id="paste-private"> Private paste with unique URL?
</label>
</div>
<div>OR</div>
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="If you'll enter password here - 'Private
</div>
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="Should this paste be accessible only with special URL and not shown in pastes list? WARNING: If you'll enter password into 'Password for paste' field this checkbox will be assumed as checked!">
<label class="checkbox">
<input type="checkbox" name="paste-private" id="paste-private"> Private paste with unique URL?
</label>
</div>
<div>OR</div>
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="If you'll enter password here - 'Private
paste with unique URL' checkbox will be assumed as checked.">
<label for="paste-password">Password for paste:</label>
<div class="control">
<input class="input" type="text" placeholder="Enter password." name="paste-password" id="paste-password">
</div>
<label for="paste-password">Password for paste:</label>
<div class="control">
<input class="input" type="text" placeholder="Enter password." name="paste-password" id="paste-password">
</div>
<div class="field">
<label for="paste-language">Language:</label>
<div class="control">
<div class="select">
<select id="paste-language" name="paste-language">
{lexers}
</select>
</div>
</div>
<div class="field">
<label for="paste-language">Language:</label>
<div class="control">
<div class="select">
<select id="paste-language" name="paste-language">
{lexers}
</select>
</div>
</div>
<div class="field">
<label for="paste-captcha">Captcha:</label>
<div class="control">
<p>
<img id=image src="/captcha/{captchaString}.png" alt="Captcha image">
</p>
<input class="input" type="text" placeholder="Captcha solution" name="paste-captcha-solution" id="paste-captcha-solution">
<input class="input is-hidden" type="text" name="paste-captcha-id" id="paste-captcha-id" readonly value="{captchaString}">
</div>
</div>
<div class="field">
<label for="paste-captcha">Captcha:</label>
<div class="control">
<p>
<img id=image src="/captcha/{captchaString}.png" alt="Captcha image">
</p>
<input class="input" type="text" placeholder="Captcha solution" name="paste-captcha-solution" id="paste-captcha-solution">
<input class="input is-hidden" type="text" name="paste-captcha-id" id="paste-captcha-id" readonly value="{captchaString}">
</div>
<div class="field">
<div class="control">
<input class="button is-success" type="submit" value="Paste!">
</div>
</div>
<div class="field">
<div class="control">
<input class="button is-success" type="submit" value="Paste!">
</div>
</div>
</div>
</form>
</section>
</body>
</html>
</div>
</form>
</section>

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fast Paste Bin</title>
<link rel="stylesheet" href="/static/css/bulma-0.7.0.min.css">
<link rel="stylesheet" href="/static/css/bulma-tooltip-1.0.4.min.css">
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
{navigation} {documentBody}
</body>
<footer class="footer">
{footer}
</footer>
</html>

@ -0,0 +1,16 @@
<nav class="navbar is-dark">
<div class="navbar-brand">
<a class="navbar-item" href="/">Fast Paste Bin</a>
<a class="navbar-item" href="/pastes/">
Pastes
</a>
</div>
<div id="navbarItems" class="navbar-menu">
<div class="navbar-start">
</div>
<div class="navbar-end">
</div>
</div>
</nav>

@ -1,69 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fast Paste Bin - Paste is protected with password</title>
<link rel="stylesheet" href="/static/css/bulma-0.7.0.min.css">
<script defer src="/static/js/fontawesome-5.0.7.js"></script>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<nav class="navbar is-dark">
<div class="navbar-brand">
<a class="navbar-item" href="/">Fast Paste Bin</a>
<a class="navbar-item" href="/pastes/">
Pastes
</a>
<div class="navbar-burger burger" data-target="navbarItems">
<span></span>
<span></span>
<span></span>
</div>
</div>
<div id="navbarItems" class="navbar-menu">
<div class="navbar-start">
</div>
<div class="navbar-end">
</div>
</div>
</nav>
<section class="section">
<div class="content">
<div class="columns">
<div class="column is-4 is-offset-4">
<form action="/paste/{pasteID}/{pasteTimestamp}/verify" method="POST" autocomplete="off">
<div class="card">
<header class="card-header">
<p class="card-header-title">
This paste is protected with password
</p>
</header>
<div class="card-content">
<div class="content">
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="If you'll enter password here - 'Private paste with unique URL' checkbox will be assumed as checked.">
<label for="paste-password">Password for paste:</label>
<div class="control">
<input class="input" type="text" placeholder="Enter password." name="paste-password" id="paste-password">
</div>
</div>
</div>
<div class="field">
<section class="section">
<div class="content">
<div class="columns">
<div class="column is-4 is-offset-4">
<form action="/paste/{pasteID}/{pasteTimestamp}/verify" method="POST" autocomplete="off">
<div class="card">
<header class="card-header">
<p class="card-header-title">
This paste is protected with password
</p>
</header>
<div class="card-content">
<div class="content">
<div class="field tooltip is-tooltip-bottom is-tooltip-multiline" data-tooltip="If you'll enter password here - 'Private paste with unique URL' checkbox will be assumed as checked.">
<label for="paste-password">Password for paste:</label>
<div class="control">
<input class="button is-success" type="submit" value="Check password!">
<input class="input" type="text" placeholder="Enter password." name="paste-password" id="paste-password">
</div>
</div>
</div>
<div class="field">
<div class="control">
<input class="button is-success" type="submit" value="Check password!">
</div>
</div>
</div>
</form>
</div>
</div>
</form>
</div>
</div>
</section>
</body>
</html>
</div>
</section>

@ -1,71 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fast Paste Bin - Paste #{pasteID}</title>
<link rel="stylesheet" href="/static/css/bulma-0.7.0.min.css">
<script defer src="/static/js/fontawesome-5.0.7.js"></script>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<nav class="navbar is-dark">
<div class="navbar-brand">
<a class="navbar-item" href="/">Fast Paste Bin</a>
<a class="navbar-item" href="/pastes/">
Pastes
</a>
<div class="navbar-burger burger" data-target="navbarItems">
<span></span>
<span></span>
<span></span>
</div>
</div>
<div id="navbarItems" class="navbar-menu">
<div class="navbar-start">
</div>
<div class="navbar-end">
</div>
</div>
</nav>
<section class="section">
<div class="content">
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Language</th>
<th>Pasted on</th>
<th>Will expire on</th>
<th>Paste type</th>
</tr>
</thead>
<tbody>
<tr>
<td>{pasteID}</td>
<td>{pasteTitle}</td>
<td>{pasteLanguage}</td>
<td>{pasteDate}</td>
<td>{pasteExpiration}</td>
<td>{pasteType}</td>
</tr>
<tr>
<td colspan="6">
<a class="button" href="/paste/{pasteID}/{pasteTs}raw">View raw</a>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<div class="paste-data">
{pastedata}
<section class="section">
<div class="content">
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Language</th>
<th>Pasted on</th>
<th>Will expire on</th>
<th>Paste type</th>
</tr>
</thead>
<tbody>
<tr>
<td>{pasteID}</td>
<td>{pasteTitle}</td>
<td>{pasteLanguage}</td>
<td>{pasteDate}</td>
<td>{pasteExpiration}</td>
<td>{pasteType}</td>
</tr>
<tr>
<td colspan="6">
<a class="button" href="/paste/{pasteID}/{pasteTs}raw">View raw</a>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
</section>
<div class="paste-data">
{pastedata}
</div>

@ -1,48 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fast Paste Bin - Pastes</title>
<link rel="stylesheet" href="/static/css/bulma-0.7.0.min.css">
<script defer src="/static/js/fontawesome-5.0.7.js"></script>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<nav class="navbar is-dark">
<div class="navbar-brand">
<a class="navbar-item" href="/">Fast Paste Bin</a>
<a class="navbar-item" href="/pastes/">
Pastes
</a>
<div class="navbar-burger burger" data-target="navbarItems">
<span></span>
<span></span>
<span></span>
</div>
</div>
<div id="navbarItems" class="navbar-menu">
<div class="navbar-start">
</div>
<div class="navbar-end">
</div>
</div>
</nav>
<section class="section">
<div>
{pagination}
</div>
<div>
{pastes}
</div>
<div>
{pagination}
</div>
</section>
</body>
</html>
<section class="section">
<div>
{pagination}
</div>
<div>
{pastes}
</div>
<div>
{pagination}
</div>
</section>

@ -37,6 +37,7 @@ import (
"github.com/pztrn/fastpastebin/database"
"github.com/pztrn/fastpastebin/database/migrations"
"github.com/pztrn/fastpastebin/pastes"
"github.com/pztrn/fastpastebin/templater"
)
func main() {
@ -57,6 +58,7 @@ func main() {
c.Database.Initialize()
migrations.New(c)
migrations.Migrate()
templater.Initialize(c)
api.New(c)
api.InitializeAPI()

@ -114,6 +114,23 @@ func (c *Context) LoadConfiguration() {
// Yay! See what it gets!
c.Logger.Debug().Msgf("Parsed configuration: %+v", c.Config)
// Set log level.
c.Logger.Info().Msgf("Setting logger level: %s", c.Config.Logging.LogLevel)
switch c.Config.Logging.LogLevel {
case "DEBUG":
zerolog.SetGlobalLevel(zerolog.DebugLevel)
case "INFO":
zerolog.SetGlobalLevel(zerolog.InfoLevel)
case "WARN":
zerolog.SetGlobalLevel(zerolog.WarnLevel)
case "ERROR":
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
case "FATAL":
zerolog.SetGlobalLevel(zerolog.FatalLevel)
case "PANIC":
zerolog.SetGlobalLevel(zerolog.PanicLevel)
}
}
// RegisterDatabaseInterface registers database interface for later use.

@ -24,6 +24,11 @@
package context
const (
// Version .
Version = "0.1.1"
)
// New creates new context.
func New() *Context {
return &Context{}

@ -95,7 +95,10 @@ custom:
# end: files
- files:
- "assets/error.html"
- "assets/footer.html"
- "assets/index.html"
- "assets/main.html"
- "assets/navigation.html"
- "assets/pagination_ellipsis.html"
- "assets/pagination_link_current.html"
- "assets/pagination_link.html"

@ -34,9 +34,9 @@ import (
"time"
// local
"github.com/pztrn/fastpastebin/api/http/static"
"github.com/pztrn/fastpastebin/captcha"
"github.com/pztrn/fastpastebin/pagination"
"github.com/pztrn/fastpastebin/templater"
// other
"github.com/alecthomas/chroma"
@ -54,11 +54,6 @@ var (
// GET for "/paste/PASTE_ID" and "/paste/PASTE_ID/TIMESTAMP" (private pastes).
func pasteGET(ec echo.Context) error {
errhtml, err := static.ReadFile("error.html")
if err != nil {
return ec.String(http.StatusNotFound, "error.html wasn't found!")
}
pasteIDRaw := ec.Param("id")
// We already get numbers from string, so we will not check strconv.Atoi()
// error.
@ -69,14 +64,14 @@ func pasteGET(ec echo.Context) error {
paste, err1 := GetByID(pasteID)
if err1 != nil {
c.Logger.Error().Msgf("Failed to get paste #%d: %s", pasteID, err1.Error())
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Paste #"+strconv.Itoa(pasteID)+" not found", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
return ec.HTML(http.StatusBadRequest, errtpl)
}
if paste.IsExpired() {
c.Logger.Error().Msgf("Paste #%d is expired", pasteID)
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Paste #"+strconv.Itoa(pasteID)+" not found", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
return ec.HTML(http.StatusBadRequest, errtpl)
}
// Check if we have a private paste and it's parameters are correct.
@ -85,14 +80,14 @@ func pasteGET(ec echo.Context) error {
tsProvided, err2 := strconv.ParseInt(tsProvidedStr, 10, 64)
if err2 != nil {
c.Logger.Error().Msgf("Invalid timestamp '%s' provided for getting private paste #%d: %s", tsProvidedStr, pasteID, err2.Error())
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Paste #"+strconv.Itoa(pasteID)+" not found", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
return ec.HTML(http.StatusBadRequest, errtpl)
}
pasteTs := paste.CreatedAt.Unix()
if tsProvided != pasteTs {
c.Logger.Error().Msgf("Incorrect timestamp '%v' provided for private paste #%d, waiting for %v", tsProvidedStr, pasteID, strconv.FormatInt(pasteTs, 10))
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Paste #"+strconv.Itoa(pasteID)+" not found", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
return ec.HTML(http.StatusBadRequest, errtpl)
}
}
@ -117,24 +112,20 @@ func pasteGET(ec echo.Context) error {
// If all okay - do nothing :)
}
pasteHTML, err2 := static.ReadFile("paste.html")
if err2 != nil {
return ec.String(http.StatusNotFound, "parse.html wasn't found!")
}
// Format template with paste data.
pasteHTMLAsString := strings.Replace(string(pasteHTML), "{pasteTitle}", paste.Title, 1)
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteID}", strconv.Itoa(paste.ID), -1)
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteDate}", paste.CreatedAt.Format("2006-01-02 @ 15:04:05"), 1)
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteExpiration}", paste.GetExpirationTime().Format("2006-01-02 @ 15:04:05"), 1)
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteLanguage}", paste.Language, 1)
// Format paste data map.
pasteData := make(map[string]string)
pasteData["pasteTitle"] = paste.Title
pasteData["pasteID"] = strconv.Itoa(paste.ID)
pasteData["pasteDate"] = paste.CreatedAt.Format("2006-01-02 @ 15:04:05") + " UTC"
pasteData["pasteExpiration"] = paste.GetExpirationTime().Format("2006-01-02 @ 15:04:05") + " UTC"
pasteData["pasteLanguage"] = paste.Language
if paste.Private {
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteType}", "<span class='has-text-danger'>Private</span>", 1)
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteTs}", strconv.FormatInt(paste.CreatedAt.Unix(), 10)+"/", 1)
pasteData["pasteType"] = "<span class='has-text-danger'>Private</span>"
pasteData["pasteTs"] = strconv.FormatInt(paste.CreatedAt.Unix(), 10) + "/"
} else {
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteType}", "<span class='has-text-success'>Public</span>", 1)
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pasteTs}", "", 1)
pasteData["pasteType"] = "<span class='has-text-success'>Public</span>"
pasteData["pasteTs"] = ""
}
// Highlight.
@ -164,26 +155,16 @@ func pasteGET(ec echo.Context) error {
if err4 != nil {
c.Logger.Error().Msgf("Failed to format paste data: %s", err4.Error())
}
pasteData["pastedata"] = buf.String()
// Escape paste data.
//pasteData := html.EscapeString(buf.String())
pasteHTMLAsString = strings.Replace(pasteHTMLAsString, "{pastedata}", buf.String(), 1)
// Get template and format it.
pasteHTML := templater.GetTemplate(ec, "paste.html", pasteData)
return ec.HTML(http.StatusOK, string(pasteHTMLAsString))
return ec.HTML(http.StatusOK, pasteHTML)
}
// GET for "/paste/PASTE_ID/TIMESTAMP/verify" - a password verify page.
func pastePasswordedVerifyGet(ec echo.Context) error {
verifyHTMLRaw, err := static.ReadFile("passworded_paste_verify.html")
if err != nil {
return ec.String(http.StatusNotFound, "passworded_paste_verify.html wasn't found!")
}
errhtml, err := static.ReadFile("error.html")
if err != nil {
return ec.String(http.StatusNotFound, "error.html wasn't found!")
}
pasteIDRaw := ec.Param("id")
timestampRaw := ec.Param("timestamp")
// We already get numbers from string, so we will not check strconv.Atoi()
@ -194,8 +175,8 @@ func pastePasswordedVerifyGet(ec echo.Context) error {
paste, err1 := GetByID(pasteID)
if err1 != nil {
c.Logger.Error().Msgf("Failed to get paste #%d: %s", pasteID, err1.Error())
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Paste #"+strconv.Itoa(pasteID)+" not found", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
errtpl := templater.GetErrorTemplate(ec, "Paste #"+pasteIDRaw+" not found")
return ec.HTML(http.StatusBadRequest, errtpl)
}
// Check for auth cookie. If present - redirect to paste.
@ -215,19 +196,18 @@ func pastePasswordedVerifyGet(ec echo.Context) error {
c.Logger.Debug().Msg("Invalid cookie, showing auth page")
}
verifyHTML := strings.Replace(string(verifyHTMLRaw), "{pasteID}", strconv.Itoa(pasteID), -1)
verifyHTML = strings.Replace(verifyHTML, "{pasteTimestamp}", timestampRaw, 1)
// HTML data.
htmlData := make(map[string]string)
htmlData["pasteID"] = strconv.Itoa(pasteID)
htmlData["pasteTimestamp"] = timestampRaw
verifyHTML := templater.GetTemplate(ec, "passworded_paste_verify.html", htmlData)
return ec.HTML(http.StatusOK, verifyHTML)
}
// POST for "/paste/PASTE_ID/TIMESTAMP/verify" - a password verify page.
func pastePasswordedVerifyPost(ec echo.Context) error {
errhtml, err := static.ReadFile("error.html")
if err != nil {
return ec.String(http.StatusNotFound, "error.html wasn't found!")
}
pasteIDRaw := ec.Param("id")
timestampRaw := ec.Param("timestamp")
// We already get numbers from string, so we will not check strconv.Atoi()
@ -239,14 +219,15 @@ func pastePasswordedVerifyPost(ec echo.Context) error {
paste, err1 := GetByID(pasteID)
if err1 != nil {
c.Logger.Error().Msgf("Failed to get paste #%d: %s", pasteID, err1.Error())
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Paste #"+strconv.Itoa(pasteID)+" not found", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
return ec.HTML(http.StatusBadRequest, errtpl)
}
params, err2 := ec.FormParams()
if err2 != nil {
c.Logger.Debug().Msg("No form parameters passed")
return ec.HTML(http.StatusBadRequest, string(errhtml))
errtpl := templater.GetErrorTemplate(ec, "Paste #"+strconv.Itoa(pasteID)+" not found")
return ec.HTML(http.StatusBadRequest, errtpl)
}
if paste.VerifyPassword(params["paste-password"][0]) {
@ -261,43 +242,38 @@ func pastePasswordedVerifyPost(ec echo.Context) error {
return ec.Redirect(http.StatusFound, "/paste/"+strconv.Itoa(pasteID)+"/"+timestampRaw)
}
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Invalid password. Please, try again.", 1)
return ec.HTML(http.StatusBadRequest, string(errhtmlAsString))
errtpl := templater.GetErrorTemplate(ec, "Invalid password. Please, try again.")
return ec.HTML(http.StatusBadRequest, string(errtpl))
}
// POST for "/paste/" which will create new paste and redirect to
// "/pastes/CREATED_PASTE_ID".
func pastePOST(ec echo.Context) error {
errhtml, err := static.ReadFile("error.html")
if err != nil {
return ec.String(http.StatusNotFound, "error.html wasn't found!")
}
params, err := ec.FormParams()
if err != nil {
c.Logger.Debug().Msg("No form parameters passed")
return ec.HTML(http.StatusBadRequest, string(errhtml))
errtpl := templater.GetErrorTemplate(ec, "Cannot create empty paste")
return ec.HTML(http.StatusBadRequest, errtpl)
}
c.Logger.Debug().Msgf("Received parameters: %+v", params)
// Do nothing if paste contents is empty.
if len(params["paste-contents"][0]) == 0 {
c.Logger.Debug().Msg("Empty paste submitted, ignoring")
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Empty pastes aren't allowed.", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
errtpl := templater.GetErrorTemplate(ec, "Empty pastes aren't allowed.")
return ec.HTML(http.StatusBadRequest, errtpl)
}
if !strings.ContainsAny(params["paste-keep-for"][0], "Mmhd") {
c.Logger.Debug().Msgf("'Keep paste for' field have invalid value: %s", params["paste-keep-for"][0])
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Invalid 'Paste should be available for' parameter passed. Please do not try to hack us ;).", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
errtpl := templater.GetErrorTemplate(ec, "Invalid 'Paste should be available for' parameter passed. Please do not try to hack us ;).")
return ec.HTML(http.StatusBadRequest, errtpl)
}
// Verify captcha.
if !captcha.Verify(params["paste-captcha-id"][0], params["paste-captcha-solution"][0]) {
c.Logger.Debug().Msgf("Invalid captcha solution for captcha ID '%s': %s", params["paste-captcha-id"][0], params["paste-captcha-solution"][0])
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Invalid captcha solution.", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
errtpl := templater.GetErrorTemplate(ec, "Invalid captcha solution.")
return ec.HTML(http.StatusBadRequest, errtpl)
}
paste := &Paste{
@ -319,8 +295,8 @@ func pastePOST(ec echo.Context) error {
keepFor, err1 := strconv.Atoi(keepForRaw)
if err1 != nil {
c.Logger.Debug().Msgf("Failed to parse 'Keep for' integer: %s", err1.Error())
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Invalid 'Paste should be available for' parameter passed. Please do not try to hack us ;).", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
errtpl := templater.GetErrorTemplate(ec, "Invalid 'Paste should be available for' parameter passed. Please do not try to hack us ;).")
return ec.HTML(http.StatusBadRequest, errtpl)
}
paste.KeepFor = keepFor
@ -342,19 +318,19 @@ func pastePOST(ec echo.Context) error {
paste.Private = false
privateCheckbox, privateCheckboxFound := params["paste-private"]
pastePassword, pastePasswordFound := params["paste-password"]
if privateCheckboxFound && privateCheckbox[0] == "on" || pastePasswordFound && len(pastePassword[0]) != 0 {
if privateCheckboxFound && privateCheckbox[0] == "on" || pastePasswordFound && pastePassword[0] != "" {
paste.Private = true
}
if len(pastePassword) != 0 {
if pastePassword[0] != "" {
paste.CreatePassword(pastePassword[0])
}
id, err2 := Save(paste)
if err2 != nil {
c.Logger.Debug().Msgf("Failed to save paste: %s", err2.Error())
errhtmlAsString := strings.Replace(string(errhtml), "{error}", "Failed to save paste. Please, try again later.", 1)
return ec.HTML(http.StatusBadRequest, errhtmlAsString)
errtpl := templater.GetErrorTemplate(ec, "Failed to save paste. Please, try again later.")
return ec.HTML(http.StatusBadRequest, errtpl)
}
newPasteIDAsString := strconv.FormatInt(id, 10)
@ -415,16 +391,6 @@ func pasteRawGET(ec echo.Context) error {
// GET for "/pastes/", a list of publicly available pastes.
func pastesGET(ec echo.Context) error {
pasteListHTML, err1 := static.ReadFile("pastelist_list.html")
if err1 != nil {
return ec.String(http.StatusNotFound, "pastelist_list.html wasn't found!")
}
pasteElementHTML, err2 := static.ReadFile("pastelist_paste.html")
if err2 != nil {
return ec.String(http.StatusNotFound, "pastelist_paste.html wasn't found!")
}
pageFromParamRaw := ec.Param("page")
var page = 1
if pageFromParamRaw != "" {
@ -443,16 +409,17 @@ func pastesGET(ec echo.Context) error {
// Show "No pastes to show" on any error for now.
if err3 != nil {
c.Logger.Error().Msgf("Failed to get pastes list from database: %s", err3.Error())
pasteListHTMLAsString := strings.Replace(string(pasteListHTML), "{pastes}", pastesString, 1)
return ec.HTML(http.StatusOK, string(pasteListHTMLAsString))
noPastesToShowTpl := templater.GetErrorTemplate(ec, "No pastes to show.")
return ec.HTML(http.StatusOK, noPastesToShowTpl)
}
if len(pastes) > 0 {
pastesString = ""
for i := range pastes {
pasteString := strings.Replace(string(pasteElementHTML), "{pasteID}", strconv.Itoa(pastes[i].ID), 2)
pasteString = strings.Replace(pasteString, "{pasteTitle}", pastes[i].Title, 1)
pasteString = strings.Replace(pasteString, "{pasteDate}", pastes[i].CreatedAt.Format("2006-01-02 @ 15:04:05"), 1)
pasteDataMap := make(map[string]string)
pasteDataMap["pasteID"] = strconv.Itoa(pastes[i].ID)
pasteDataMap["pasteTitle"] = pastes[i].Title
pasteDataMap["pasteDate"] = pastes[i].CreatedAt.Format("2006-01-02 @ 15:04:05") + " UTC"
// Get max 4 lines of each paste.
pasteDataSplitted := strings.Split(pastes[i].Data, "\n")
@ -462,18 +429,20 @@ func pastesGET(ec echo.Context) error {
} else {
pasteData = strings.Join(pasteDataSplitted[0:4], "\n")
}
pasteString = strings.Replace(pasteString, "{pasteData}", pasteData, 1)
pastesString += pasteString
pasteDataMap["pasteData"] = pasteData
pasteTpl := templater.GetRawTemplate(ec, "pastelist_paste.html", pasteDataMap)
pastesString += pasteTpl
}
}
pasteListHTMLAsString := strings.Replace(string(pasteListHTML), "{pastes}", pastesString, 1)
// Pagination.
pages := GetPastesPages()
c.Logger.Debug().Msgf("Total pages: %d, current: %d", pages, page)
paginationHTML := pagination.CreateHTML(page, pages, "/pastes/")
pasteListHTMLAsString = strings.Replace(pasteListHTMLAsString, "{pagination}", paginationHTML, -1)
return ec.HTML(http.StatusOK, string(pasteListHTMLAsString))
pasteListTpl := templater.GetTemplate(ec, "pastelist_list.html", map[string]string{"pastes": pastesString, "pagination": paginationHTML})
return ec.HTML(http.StatusOK, string(pasteListTpl))
}

@ -0,0 +1,124 @@
// Fast Paste Bin - uberfast and easy-to-use pastebin.
//
// Copyright (c) 2018, Stanislav N. aka pztrn and Fast Paste Bin
// developers.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject
// to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.