Add content policy and add abuse categories

This commit is contained in:
2021-01-05 00:00:46 +01:00
parent 96ca0ed90f
commit e074be4e19
15 changed files with 198 additions and 101 deletions

25
main.go
View File

@@ -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)
}
}

View File

@@ -34,6 +34,33 @@ spare some coins. Possible methods for donating are:
* <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WU49A5NHPAZ9G&source=url">
Donate with PayPal</a>
## 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.

View File

@@ -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

View File

@@ -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 <u>T</u>ext</button>
<br/>
<p>
By uploading files to pixeldrain you accept that a cookie will
be placed in your web browser. More information on the
<a href="/about">about</a> page
By uploading files to pixeldrain you acknowledge and accept our
<a href="/about#content-policy">content policy</a>.
<p>
<div id="instruction_2" class="instruction_highlight">
<div class="limit_width">

View File

@@ -182,7 +182,9 @@ const toggle_select = () => {
{child.name}
</td>
<td class="node_size">
{formatDataVolume(child.file_size, 3)}
{#if child.type === "file"}
{formatDataVolume(child.file_size, 3)}
{/if}
</td>
</a>
{/each}

View File

@@ -0,0 +1,88 @@
<script>
export let bucket
let details_hidden = true
const expand_bucket = () => {
details_hidden = !details_hidden
}
</script>
<div class="bucket">
<div class="bucket_header">
<a href={'/d/' + bucket.id} class="bucket_title">
<img class="bucket_icon" src="/res/img/mime/folder-remote.png" alt="Bucket icon"/>
{bucket.name}
</a>
<button class="bucket_expand" on:click={expand_bucket}>
{#if details_hidden}
<i class="icon">expand_more</i>
{:else}
<i class="icon">expand_less</i>
{/if}
</button>
</div>
<div class="bucket_details" class:hidden={details_hidden}>
<form>
<table class="form">
<tr class="form">
<td>Name</td>
<td><input type="text" value={bucket.name} /></td>
</tr>
<tr class="form">
<td colspan="2">
<button class="button_red">
<i class="icon">delete</i> Delete
</button>
<button class="button_highlight" style="float: right;">
<i class="icon">save</i> Save
</button>
</td>
</tr>
</table>
</form>
</div>
</div>
<style>
.bucket {
text-decoration: none;
background-color: var(--layer_3_color);
transition: box-shadow 0.5s;
box-shadow: 1px 1px var(--layer_3_shadow) 0 var(--shadow_color);
}
.bucket_header {
display: flex;
flex-direction: row;
color: var(--text_color);
}
.bucket_header:hover {
background-color: var(--input_color_dark)
}
.bucket_title {
flex: 1 1 auto;
align-self: center;
display: block;
text-decoration: none;
}
.bucket_icon {
height: 32px;
width: 32px;
margin: 4px;
vertical-align: middle;
}
.bucket_expand {
flex: 0 0 auto;
}
.bucket_details {
display: flex;
padding: 0.4em;
flex-direction: column;
text-decoration: none;
border-top: 1px solid var(--layer_3_color_border);
color: var(--text_color);
}
.hidden {
display: none;
}
</style>

View File

@@ -1,5 +1,6 @@
<script>
import { onMount } from "svelte";
import Bucket from "./Bucket.svelte";
import Spinner from "../util/Spinner.svelte";
import { fs_get_buckets } from "../filesystem/FilesystemAPI.svelte";
@@ -17,10 +18,6 @@ const get_buckets = async () => {
}
};
const expand_bucket = () => {
}
onMount(get_buckets);
</script>
@@ -32,15 +29,23 @@ onMount(get_buckets);
{/if}
<div class="limit_width">
<button style="float: right;">
<i class="icon">create_new_folder</i> New bucket
</button>
<br/>
<h2>Persistent buckets</h2>
<p>
These buckets don't expire, but have limited storage space and
bandwidth. Their limits can be raised by buying a subscription.
</p>
{#each buckets as bucket}
<a class="bucket_header" href={'/d/' + bucket.id}>
<div class="bucket_title">{bucket.name}</div>
<button class="bucket_expand" on:click|preventDefault={expand_bucket}><i class="icon">expand_more</i></button>
</a>
<div class="bucket_details">
Hello!
</div>
<Bucket bucket={bucket}></Bucket>
{/each}
<br/>
<h2>Temporary buckets</h2>
<p>
</p>
</div>
</div>
@@ -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;
}
</style>

View File

@@ -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 {

View File

@@ -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",

View File

@@ -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 {

View File

@@ -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(

View File

@@ -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")

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 ""