From e074be4e19ca4422e309f1ea9bb2e5b2a9a7e4af Mon Sep 17 00:00:00 2001 From: Wim Brand Date: Tue, 5 Jan 2021 00:00:46 +0100 Subject: [PATCH] Add content policy and add abuse categories --- main.go | 25 +++++- res/include/md/about.md | 41 ++++++--- res/include/md/acknowledgements.md | 10 +-- res/template/home.html | 7 +- .../filesystem/filemanager/FileManager.svelte | 4 +- svelte/src/user_buckets/Bucket.svelte | 88 +++++++++++++++++++ svelte/src/user_buckets/UserBuckets.svelte | 57 ++++-------- webcontroller/ad_click.go | 5 +- webcontroller/admin_panel.go | 9 +- webcontroller/file_preview.go | 6 +- webcontroller/file_viewer.go | 6 +- webcontroller/templates.go | 18 ++-- webcontroller/user_account.go | 6 +- webcontroller/user_settings.go | 3 +- webcontroller/web_controller.go | 14 +-- 15 files changed, 198 insertions(+), 101 deletions(-) create mode 100644 svelte/src/user_buckets/Bucket.svelte diff --git a/main.go b/main.go index 1da7902..b8c4d1e 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,10 @@ package main import ( "flag" + "net" "net/http" + "fornaxian.tech/pixeldrain_server/util" web "fornaxian.tech/pixeldrain_web/init" "github.com/Fornaxian/log" @@ -14,17 +16,32 @@ import ( // be directly embedded by another Go project. And when deployed it will run // independently. func main() { + var err error + var sock = flag.Bool("systemd-socket", false, "Enable/disable systemd socket activation") var listen = flag.String("listen", ":8081", "The address which the API server will listen on") var prefix = flag.String("prefix", "", "Prefix that comes before the API URL") flag.Parse() - r := httprouter.New() + var listener net.Listener - web.Init(r, *prefix, true) + // Serve the API on a socket. If systemd-socket is enabled we'll reuse + // systemd's socket, else we'll create our own to serve on + if *sock { + // Socket activation enabled. Get the provided sockets and serve on them + if listener, err = util.SystemdSocketByName("pd-web.socket"); err != nil { + panic("Socket pd-web.socket not found") + } + } else { + // Socket activation disabled, so we create our own listener to serve on + if listener, err = net.Listen("tcp", *listen); err != nil { + panic(err) + } + } - err := http.ListenAndServe(*listen, r) + var router = httprouter.New() + web.Init(router, *prefix, true) - if err != nil { + if err = http.Serve(listener, router); err != nil { log.Error("Can't listen and serve Pixeldrain Web: %v", err) } } diff --git a/res/include/md/about.md b/res/include/md/about.md index 77c8943..37ddda6 100644 --- a/res/include/md/about.md +++ b/res/include/md/about.md @@ -34,6 +34,33 @@ spare some coins. Possible methods for donating are: * Donate with PayPal +## Content policy + +The following types of content are not allowed to be shared on pixeldrain. They +will be removed when reported. + + * **Copyright violation**: Works which are shared without permission from the + copyright holder. + * **Abuse of minors**: Videos, images or audio fragments depicting abuse or + inappropriate touching of minors will be removed and reported to the National + Center for Missing and Exploited Children when found. + * **Terrorism**: Videos, images or audio fragments which promote and + glorify acts of terrorism. + * **Gore**: Graphic and shocking videos or images depicting severe harm to + humans. + * **Malware and computer viruses**: Software designed to cause harm to computer + systems. + +If you have found content which falls in any of these categories on pixeldrain +please report the download link to me at +[abuse@pixeldrain.com](mailto:abuse@pixeldrain.com) and I will review it. If the +content does not fit into one of the categories your e-mail will be ignored. + +Fornaxian Technologies cannot be held liable for any illegal or copyrighted +material that's uploaded by the users of this application under the Online +Copyright Infringement Liability Limitation Act ยง 512\(c) in the USA and the +Electronic Commerce Directive 2000 Article 14 in the EU. + ## How does pixeldrain store files? Pixeldrain uses a few different techniques to store files cheaply, efficiently @@ -87,16 +114,8 @@ When uploading a file pixeldrain will save a list of file links on your browser's local storage. This data is **only** used for viewing your upload history on the [history page](/history). -## Legality - -I cannot be held liable for any illegal and / or copyrighted material that's -uploaded by the users of this application. Files uploaded to this website are -subjected to local laws. If laws are being broken, and I've been notified of the -fact I'll have to delete the offending content. If you find any files on this -domain that break the law, please contact me at -[abuse@pixeldrain.com](mailto:abuse@pixeldrain.com), and I'll take care of it. - -Please share responsibly. +## Support For other questions you can reach me at -[support@pixeldrain.com](mailto:support@pixeldrain.com) +[support@pixeldrain.com](mailto:support@pixeldrain.com). Abuse reports sent to +this address will not be reviewed, use the abuse address. diff --git a/res/include/md/acknowledgements.md b/res/include/md/acknowledgements.md index 50f9cd2..c8f4480 100644 --- a/res/include/md/acknowledgements.md +++ b/res/include/md/acknowledgements.md @@ -15,12 +15,12 @@ * [BurntSushi/toml](https://github.com/BurntSushi/toml) * [julienschmidt/httprouter](https://github.com/julienschmidt/httprouter) * [gabriel-vasile/mimetype](https://github.com/gabriel-vasile/mimetype) - * [disintegration/imaging](github.com/disintegration/imaging) - * [gorilla/websocket](github.com/gorilla/websocket) - * [shopspring/decimal](github.com/shopspring/decimal) - * [jhillyerd/enmime](github.com/jhillyerd/enmime) + * [disintegration/imaging](https://github.com/disintegration/imaging) + * [gorilla/websocket](https://github.com/gorilla/websocket) + * [shopspring/decimal](https://github.com/shopspring/decimal) + * [jhillyerd/enmime](https://github.com/jhillyerd/enmime) * [russross/blackfriday](https://github.com/russross/blackfriday) - * [microcosm-cc/bluemonday](github.com/microcosm-cc/bluemonday) + * [microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday) ### Web framework diff --git a/res/template/home.html b/res/template/home.html index 661b2ef..e4a0ad0 100644 --- a/res/template/home.html +++ b/res/template/home.html @@ -22,7 +22,7 @@ border-top: 1px solid var(--layer_2_color_border); border-bottom: 1px solid var(--layer_2_color_border); box-sizing: border-box; - margin: 10px 0; + margin: 1.5em 0; padding: 5px; } .big_number { @@ -122,9 +122,8 @@ Upload Text

- By uploading files to pixeldrain you accept that a cookie will - be placed in your web browser. More information on the - about page + By uploading files to pixeldrain you acknowledge and accept our + content policy.

diff --git a/svelte/src/filesystem/filemanager/FileManager.svelte b/svelte/src/filesystem/filemanager/FileManager.svelte index 2de60b7..b39d048 100644 --- a/svelte/src/filesystem/filemanager/FileManager.svelte +++ b/svelte/src/filesystem/filemanager/FileManager.svelte @@ -182,7 +182,9 @@ const toggle_select = () => { {child.name} - {formatDataVolume(child.file_size, 3)} + {#if child.type === "file"} + {formatDataVolume(child.file_size, 3)} + {/if} {/each} diff --git a/svelte/src/user_buckets/Bucket.svelte b/svelte/src/user_buckets/Bucket.svelte new file mode 100644 index 0000000..477e7d8 --- /dev/null +++ b/svelte/src/user_buckets/Bucket.svelte @@ -0,0 +1,88 @@ + + +
+
+ + Bucket icon + {bucket.name} + + +
+
+
+ + + + + + + + +
Name
+ + +
+
+
+
+ + diff --git a/svelte/src/user_buckets/UserBuckets.svelte b/svelte/src/user_buckets/UserBuckets.svelte index 4a6eaff..0ce7fb8 100644 --- a/svelte/src/user_buckets/UserBuckets.svelte +++ b/svelte/src/user_buckets/UserBuckets.svelte @@ -1,5 +1,6 @@ @@ -32,15 +29,23 @@ onMount(get_buckets); {/if}
+ +
+

Persistent buckets

+

+ These buckets don't expire, but have limited storage space and + bandwidth. Their limits can be raised by buying a subscription. +

{#each buckets as bucket} - -
{bucket.name}
- -
-
- Hello! -
+ {/each} +
+

Temporary buckets

+

+ +

@@ -50,34 +55,4 @@ onMount(get_buckets); height: 100px; width: 100px; } -.bucket_header { - display: flex; - flex-direction: row; - text-decoration: none; - color: var(--text_color); - background-color: var(--layer_3_color); - transition: box-shadow 0.5s; - box-shadow: 1px 1px var(--layer_2_shadow) 0 var(--shadow_color); -} -.bucket_header:hover { - box-shadow: 0 0 2px 2px var(--highlight_color), inset 0 0 1px 1px var(--highlight_color); - color: var(--highlight_color); - text-decoration: none; -} -.bucket_title { - flex: 1 1 auto; - align-self: center; - padding: 0.4em; -} -.bucket_expand { - flex: 0 0 auto; -} -.bucket_details { - display: flex; - flex-direction: column; - text-decoration: none; - color: var(--text_color); - background-color: var(--layer_3_color); - transition: box-shadow 0.5s; -} diff --git a/webcontroller/ad_click.go b/webcontroller/ad_click.go index b34f685..bafb4d8 100644 --- a/webcontroller/ad_click.go +++ b/webcontroller/ad_click.go @@ -4,7 +4,6 @@ import ( "net/http" "time" - "fornaxian.tech/pixeldrain_server/api/restapi/apiclient" "fornaxian.tech/pixeldrain_server/util" "github.com/Fornaxian/log" "github.com/julienschmidt/httprouter" @@ -15,11 +14,9 @@ func (wc *WebController) serveAdClick(w http.ResponseWriter, r *http.Request, p w.Header().Set("Referrer-Policy", "origin") http.Redirect(w, r, r.URL.Query().Get("target"), http.StatusTemporaryRedirect) - api := apiclient.New(wc.apiURLInternal) - // The Real IP is used in the API server to determine that the view is not // fake - api.RealIP = util.RemoteAddress(r) + var api = wc.api.RealIP(util.RemoteAddress(r)) // Log a view on the file if err := api.PostFileView(p.ByName("id"), wc.viewTokenOrBust()); err != nil { diff --git a/webcontroller/admin_panel.go b/webcontroller/admin_panel.go index 289f25a..7dcacfa 100644 --- a/webcontroller/admin_panel.go +++ b/webcontroller/admin_panel.go @@ -110,7 +110,14 @@ func (wc *WebController) adminAbuseForm(td *TemplateData, r *http.Request) (f Fo Label: "Type", DefaultValue: "unknown", Type: FieldTypeRadio, - RadioValues: []string{"unknown", "copyright", "terrorism", "child_abuse"}, + RadioValues: []string{ + "unknown", + "copyright", + "child_abuse", + "terrorism", + "gore", + "malware", + }, }, { Name: "reporter", Label: "Reporter", diff --git a/webcontroller/file_preview.go b/webcontroller/file_preview.go index afad788..b2f6081 100644 --- a/webcontroller/file_preview.go +++ b/webcontroller/file_preview.go @@ -6,7 +6,6 @@ import ( "net/http" "strings" - "fornaxian.tech/pixeldrain_server/api/restapi/apiclient" "fornaxian.tech/pixeldrain_server/util" "github.com/Fornaxian/log" "github.com/julienschmidt/httprouter" @@ -21,9 +20,8 @@ func (wc *WebController) serveFilePreview(w http.ResponseWriter, r *http.Request return } - api := apiclient.New(wc.apiURLInternal) - api.APIKey, _ = wc.getAPIKey(r) - api.RealIP = util.RemoteAddress(r) + apiKey, _ := wc.getAPIKey(r) + api := wc.api.Login(apiKey).RealIP(util.RemoteAddress(r)) file, err := api.GetFileInfo(p.ByName("id")) // TODO: Error handling if err != nil { diff --git a/webcontroller/file_viewer.go b/webcontroller/file_viewer.go index 13a99a1..7cc4725 100644 --- a/webcontroller/file_viewer.go +++ b/webcontroller/file_viewer.go @@ -19,7 +19,7 @@ import ( func (wc *WebController) viewTokenOrBust() (t string) { var err error - if t, err = wc.systemPixelAPI.GetMiscViewToken(); err != nil { + if t, err = wc.api.GetMiscViewToken(); err != nil { log.Error("Could not get viewtoken: %s", err) } return t @@ -47,9 +47,7 @@ func adType() int { switch i := rand.Intn(5); i { case 0: return amarulaSolutions - case 1: - return adMaven - case 2, 3, 4: + case 1, 2, 3, 4: return propellerAds default: panic(fmt.Errorf( diff --git a/webcontroller/templates.go b/webcontroller/templates.go index f1587f8..2263e01 100644 --- a/webcontroller/templates.go +++ b/webcontroller/templates.go @@ -29,7 +29,7 @@ type TemplateData struct { Style pixeldrainStyleSheet UserStyle template.CSS APIEndpoint template.URL - PixelAPI *apiclient.PixelAPI + PixelAPI apiclient.PixelAPI Hostname template.HTML // Only used on file viewer page @@ -50,19 +50,19 @@ func (wc *WebController) newTemplateData(w http.ResponseWriter, r *http.Request) Style: userStyle(r), UserStyle: template.CSS(userStyle(r).String()), APIEndpoint: template.URL(wc.apiURLExternal), - PixelAPI: apiclient.New(wc.apiURLInternal), - Hostname: template.HTML(wc.hostname), - URLQuery: r.URL.Query(), - } - // Use the user's IP address for making requests - t.PixelAPI.RealIP = util.RemoteAddress(r) + // Use the user's IP address for making requests + PixelAPI: wc.api.RealIP(util.RemoteAddress(r)), + + Hostname: template.HTML(wc.hostname), + URLQuery: r.URL.Query(), + } // If the user is authenticated we'll indentify him and put the user info // into the templatedata. This is used for putting the username in the menu // and stuff like that if key, err := wc.getAPIKey(r); err == nil { - t.PixelAPI.APIKey = key // Use the user's API key for all requests + t.PixelAPI = t.PixelAPI.Login(key) // Use the user's API key for all requests t.User, err = t.PixelAPI.UserInfo() if err != nil { // This session key doesn't work, or the backend is down, user @@ -71,7 +71,7 @@ func (wc *WebController) newTemplateData(w http.ResponseWriter, r *http.Request) if err.Error() == "authentication_required" || err.Error() == "authentication_failed" { // Disable API authentication - t.PixelAPI.APIKey = "" + t.PixelAPI = wc.api // Remove the authentication cookie log.Debug("Deleting invalid API key") diff --git a/webcontroller/user_account.go b/webcontroller/user_account.go index e0180e1..f6e0d83 100644 --- a/webcontroller/user_account.go +++ b/webcontroller/user_account.go @@ -5,7 +5,6 @@ import ( "net/http" "time" - "fornaxian.tech/pixeldrain_server/api/restapi/apiclient" "github.com/Fornaxian/log" "github.com/julienschmidt/httprouter" ) @@ -16,8 +15,7 @@ func (wc *WebController) serveLogout( p httprouter.Params, ) { if key, err := wc.getAPIKey(r); err == nil { - var api = apiclient.New(wc.apiURLInternal) - api.APIKey = key + var api = wc.api.Login(key) if err = api.UserSessionDestroy(key); err != nil { log.Warn("logout failed for session '%s': %s", key, err) } @@ -150,7 +148,7 @@ func (wc *WebController) loginForm(td *TemplateData, r *http.Request) (f Form) { } if f.ReadInput(r) { - loginResp, err := td.PixelAPI.UserLogin(f.FieldVal("username"), f.FieldVal("password"), false) + loginResp, err := td.PixelAPI.UserLogin(f.FieldVal("username"), f.FieldVal("password")) if err != nil { formAPIError(err, &f) } else { diff --git a/webcontroller/user_settings.go b/webcontroller/user_settings.go index c655a3a..4d3eac6 100644 --- a/webcontroller/user_settings.go +++ b/webcontroller/user_settings.go @@ -170,8 +170,7 @@ func (wc *WebController) serveEmailConfirm( var err error var status string - api := apiclient.New(wc.apiURLInternal) - err = api.UserEmailResetConfirm(r.FormValue("key")) + err = wc.api.UserEmailResetConfirm(r.FormValue("key")) if err != nil && err.Error() == "not_found" { status = "not_found" } else if err != nil { diff --git a/webcontroller/web_controller.go b/webcontroller/web_controller.go index 3e0e66f..296e015 100644 --- a/webcontroller/web_controller.go +++ b/webcontroller/web_controller.go @@ -36,9 +36,10 @@ type WebController struct { httpClient *http.Client - // This API client should only be used for system functions like getting - // view tokens. It has no authentication and no IP forwarding - systemPixelAPI *apiclient.PixelAPI + // API client to use for all requests. If the user is authenticated you + // should call Login() on this object. Calling Login will create a copy and + // not alter the original PixelAPI, but it will use the same HTTP Transport + api apiclient.PixelAPI } // New initializes a new WebController by registering all the request handlers @@ -60,7 +61,7 @@ func New( apiURLExternal: apiURLExternal, sessionCookieDomain: sessionCookieDomain, httpClient: &http.Client{Timeout: time.Minute * 10}, - systemPixelAPI: apiclient.New(apiURLInternal), + api: apiclient.New(apiURLInternal), } wc.templates = NewTemplateManager(resourceDir, apiURLExternal, debugMode) wc.templates.ParseTemplates(false) @@ -218,7 +219,7 @@ func (wc *WebController) serveMarkdown(tpl string, requireAuth bool) httprouter. var inHeader = false blackfriday.New( blackfriday.WithRenderer(renderer), - blackfriday.WithExtensions(blackfriday.CommonExtensions), + blackfriday.WithExtensions(blackfriday.CommonExtensions|blackfriday.AutoHeadingIDs), ).Parse( tplBuf.Bytes(), ).Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus { @@ -337,8 +338,7 @@ func (wc *WebController) getAPIKey(r *http.Request) (key string, err error) { func (wc *WebController) captchaKey() string { // This only runs on the first request if wc.captchaSiteKey == "" { - var api = apiclient.New(wc.apiURLInternal) - capt, err := api.GetRecaptcha() + capt, err := wc.api.GetRecaptcha() if err != nil { log.Error("Error getting recaptcha key: %s", err) return ""