From fd25809cac6dcb821a2f2921654c8fe1c733af7f Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 03:22:28 -0700 Subject: [PATCH 01/12] rework errors --- files/routes/errors.py | 152 ++++++++++++++++-------------- files/templates/errors/error.html | 22 +++++ 2 files changed, 104 insertions(+), 70 deletions(-) create mode 100644 files/templates/errors/error.html diff --git a/files/routes/errors.py b/files/routes/errors.py index 484b89871..6f63c2e05 100644 --- a/files/routes/errors.py +++ b/files/routes/errors.py @@ -1,19 +1,96 @@ from files.helpers.wrappers import * from flask import * +from werkzeug.exceptions import HTTPException from urllib.parse import quote, urlencode import time from files.__main__ import app, limiter +WERKZEUG_ERROR_DESCRIPTIONS = { + 400: "The browser (or proxy) sent a request that this server could not understand.", + 401: "The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad password), or your browser doesn't understand how to supply the credentials required.", + 403: "You don't have the permission to access the requested resource. It is either read-protected or not readable by the server.", + 404: "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.", + 405: "The method is not allowed for the requested URL.", + 406: "The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.", + 413: "The data value transmitted exceeds the capacity limit.", + 414: "The length of the requested URL exceeds the capacity limit for this server. The request cannot be processed.", + 415: "The server does not support the media type transmitted in the request.", + 418: "This server is a teapot, not a coffee machine", + 429: "This user has exceeded an allotted request count. Try again later.", + 500: "The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.", +} +ERROR_TITLES = { + 400: "Bad Request", + 401: "Unauthorized", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Too Many Pings", + 413: "Max image/audio size is 8 MB (16 MB for paypigs)", + 414: "Max video size is 32 MB (64 MB for paypigs)", + 415: "Unsupported Media Type", + 417: "Image already exists!", + 418: "WEBM videos are not allowed", + 429: "Too Many Requests", + 500: "Internal Server Error", +} + +ERROR_MSGS = { + 400: "That request was bad and you should feel bad.", + 401: "What you're trying to do requires an account. I think. The original error message said something about a castle and I hated that.", + 403: "YOU AREN'T WELCOME HERE GO AWAY", + 404: "Someone typed something wrong and it was probably you, please do better.", + 405: "idk how anyone gets this error but if you see this, remember to follow @carpathianflorist
the original error text here talked about internet gremlins and wtf", + 406: "Max limit is 5 for comments and 50 for posts", + 413: "Max image/audio size is 8 MB (16 MB for paypigs)", + 414: "Max video size is 32 MB (64 MB for paypigs)", + 415: "Please upload only Image, Video, or Audio files!", + 417: "Image already exists!", + 418: "Please convert your video to MP4 and re-upload it!", + 429: "go spam somewhere else nerd", + 500: "Hiiiii it's carp! I think this error means that there's a timeout error. And I think that means something took too long to load so it decided not to work at all. If you keep seeing this on the same page but not other pages, then something is probably wrong with that specific function. It may not be called a function, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily <3", +} + +ERROR_MARSEYS = { + 400: "marseybrainlet", + 401: "marseydead", + 403: "marseytroll", + 404: "marseyconfused", + 405: "marseyretard", + 406: "marseyrage", + 413: "marseyretard", + 414: "marseychonker2", + 415: "marseydetective", + 418: "marseytea", + 429: "marseyrentfree", + 500: "marseycarp3", +} @app.errorhandler(400) -def error_400(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "400 Bad Request"}, 400 - else: return render_template('errors/400.html', err=True), 400 +@app.errorhandler(403) +@app.errorhandler(404) +@app.errorhandler(405) +@app.errorhandler(406) +@app.errorhandler(413) +@app.errorhandler(414) +@app.errorhandler(415) +@app.errorhandler(417) +@app.errorhandler(418) +@app.errorhandler(429) +def error(e): + title = ERROR_TITLES.get(e.code, str(e.code)) + msg = ERROR_MSGS.get(e.code, str(e.code)) + details = e.description + if WERKZEUG_ERROR_DESCRIPTIONS.get(e.code, None) == details: + details = None + if request.headers.get("Authorization") or request.headers.get("xhr"): + return {"error": msg, "code": e.code, "details": details} + img = ERROR_MARSEYS.get(e.code, 'marseyl') + return render_template('errors/error.html', err=True, title=title, msg=msg, details=details, img=img), e.code @app.errorhandler(401) def error_401(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "401 Not Authorized"}, 401 else: path = request.path @@ -23,75 +100,10 @@ def error_401(e): if session.get("history"): return redirect(f"/login?redirect={argval}") else: return redirect(f"/signup?redirect={argval}") -@app.errorhandler(406) -def error_406(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): - return {"error": "Too many pings: max limit is 5 for comments and 50 for posts"}, 406 - else: return render_template('errors/406.html', err=True), 406 - -@app.errorhandler(403) -def error_403(e): - - description = e.description - if description == "You don't have the permission to access the requested resource. It is either read-protected or not readable by the server.": description = '' - - if request.headers.get("Authorization") or request.headers.get("xhr"): - if not description: description = "403 Forbidden" - return {"error": description}, 403 - else: - if not description: description = "YOU AREN'T WELCOME HERE GO AWAY" - return render_template('errors/403.html', description=description, err=True), 403 - - -@app.errorhandler(404) -def error_404(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "404 Not Found"}, 404 - else: return render_template('errors/404.html', err=True), 404 - -@app.errorhandler(405) -def error_405(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "405 Method Not Allowed"}, 405 - else: return render_template('errors/405.html', err=True), 405 - -@app.errorhandler(413) -def error_413(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): - return {"error": "Max image/audio size is 8 MB (16 MB for paypigs)"}, 413 - else: return render_template('errors/413.html', err=True), 413 - -@app.errorhandler(414) -def error_414(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): - return {"error": "Max video size is 32 MB (64 MB for paypigs)"}, 414 - else: return render_template('errors/414.html', err=True), 414 - -@app.errorhandler(415) -def error_415(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "Please upload only Image, Video, or Audio files!"}, 415 - else: return render_template('errors/415.html', err=True), 415 - -@app.errorhandler(417) -def error_417(e): - return {"error": "Image already exists!"}, 417 - -@app.errorhandler(418) -def error_418(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): - return {"error": "WEBM videos are not allowed, please convert your video to MP4 and re-upload it!"}, 418 - else: return render_template('errors/418.html', err=True), 418 - -@app.errorhandler(429) -def error_429(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "429 Too Many Requests"}, 429 - else: return render_template('errors/429.html', err=True), 429 - - @app.errorhandler(500) def error_500(e): g.db.rollback() - - if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "500 Internal Server Error"}, 500 - else: return render_template('errors/500.html', err=True), 500 + return error(e) @app.post("/allow_nsfw") diff --git a/files/templates/errors/error.html b/files/templates/errors/error.html new file mode 100644 index 000000000..147fb0f1e --- /dev/null +++ b/files/templates/errors/error.html @@ -0,0 +1,22 @@ +{% extends "default.html" %} + +{% block title %} +{{code}} {{title}} +{% endblock %} + +{% block pagetype %}error-{{code}}{% endblock %} + +{% block content %} +
+
+
+ {% if img %} + :#{{img}}: + {% endif %} +

+			

{{code}} {{title}}

+

{{msg}}

+
+
+
+{% endblock %} From ac011a77b9472c0274e3c444aae63e6e45c3494c Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 03:28:03 -0700 Subject: [PATCH 02/12] remove unneeded templates, use custom handler for 500, and 401 is "Unauthorized," not "Not Authorized" --- files/routes/errors.py | 7 ++++--- files/templates/errors/400.html | 20 -------------------- files/templates/errors/403.html | 21 --------------------- files/templates/errors/404.html | 21 --------------------- files/templates/errors/405.html | 21 --------------------- files/templates/errors/406.html | 21 --------------------- files/templates/errors/413.html | 20 -------------------- files/templates/errors/414.html | 20 -------------------- files/templates/errors/415.html | 20 -------------------- files/templates/errors/418.html | 20 -------------------- files/templates/errors/429.html | 20 -------------------- 11 files changed, 4 insertions(+), 207 deletions(-) delete mode 100644 files/templates/errors/400.html delete mode 100644 files/templates/errors/403.html delete mode 100644 files/templates/errors/404.html delete mode 100644 files/templates/errors/405.html delete mode 100644 files/templates/errors/406.html delete mode 100644 files/templates/errors/413.html delete mode 100644 files/templates/errors/414.html delete mode 100644 files/templates/errors/415.html delete mode 100644 files/templates/errors/418.html delete mode 100644 files/templates/errors/429.html diff --git a/files/routes/errors.py b/files/routes/errors.py index 6f63c2e05..0cb13116f 100644 --- a/files/routes/errors.py +++ b/files/routes/errors.py @@ -85,13 +85,13 @@ def error(e): if WERKZEUG_ERROR_DESCRIPTIONS.get(e.code, None) == details: details = None if request.headers.get("Authorization") or request.headers.get("xhr"): - return {"error": msg, "code": e.code, "details": details} + return {"error": title, "code": e.code, "description": msg, "details": details} img = ERROR_MARSEYS.get(e.code, 'marseyl') return render_template('errors/error.html', err=True, title=title, msg=msg, details=details, img=img), e.code @app.errorhandler(401) def error_401(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "401 Not Authorized"}, 401 + if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "401 Unauthorized"}, 401 else: path = request.path qs = urlencode(dict(request.values)) @@ -103,7 +103,8 @@ def error_401(e): @app.errorhandler(500) def error_500(e): g.db.rollback() - return error(e) + if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "500 Internal Server Error"}, 500 + else: return render_template('errors/500.html', err=True), 500 @app.post("/allow_nsfw") diff --git a/files/templates/errors/400.html b/files/templates/errors/400.html deleted file mode 100644 index 894d59c7a..000000000 --- a/files/templates/errors/400.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -400 Bad Request -{% endblock %} - -{% block pagetype %}error-400{% endblock %} - -{% block content %} -
-
-
- :#marseybrainlet: -

-			

400 Bad Request

-

That request was bad and you should feel bad.

-
-
-
-{% endblock %} diff --git a/files/templates/errors/403.html b/files/templates/errors/403.html deleted file mode 100644 index ae6535659..000000000 --- a/files/templates/errors/403.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -403 Forbidden -{% endblock %} - -{% block pagetype %}error-403{% endblock %} - -{% block content %} -
-
-
- :#marseytroll: -

-		

403 Forbidden

-

{{description}}

- -
-
-
-{% endblock %} diff --git a/files/templates/errors/404.html b/files/templates/errors/404.html deleted file mode 100644 index a5b7ce074..000000000 --- a/files/templates/errors/404.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -404 Page Not Found -{% endblock %} - -{% block pagetype %}error-404{% endblock %} - -{% block content %} -
-
-
- :#marseyconfused -

-		

404 Page Not Found

-

Someone typed something wrong and it was probably you, please do better.

- -
-
-
-{% endblock %} diff --git a/files/templates/errors/405.html b/files/templates/errors/405.html deleted file mode 100644 index 33215384a..000000000 --- a/files/templates/errors/405.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -405 Method Not Allowed -{% endblock %} - -{% block pagetype %}error-405{% endblock %} - -{% block content %} -
-
-
- :#marseyretard: -

-		

405 Method Not Allowed

-

idk how anyone gets this error but if you see this, remember to follow @carpathianflorist
the original error text here talked about internet gremlins and wtf

- -
-
-
-{% endblock %} diff --git a/files/templates/errors/406.html b/files/templates/errors/406.html deleted file mode 100644 index 2b36f518c..000000000 --- a/files/templates/errors/406.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -Too many pings -{% endblock %} - -{% block pagetype %}Too many pings{% endblock %} - -{% block content %} -
-
-
- :#marseyrage -

-		

Too many pings

-

Max limit is 5 for comments and 50 for posts

- -
-
-
-{% endblock %} diff --git a/files/templates/errors/413.html b/files/templates/errors/413.html deleted file mode 100644 index 3dae7f9b8..000000000 --- a/files/templates/errors/413.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -Max image/audio size is 8 MB (16 MB for paypigs) -{% endblock %} - -{% block pagetype %}error-413{% endblock %} - -{% block content %} -
-
-
- :#marseyretard: -

-		

Max image/audio size is 8 MB (16 MB for paypigs)

- -
-
-
-{% endblock %} diff --git a/files/templates/errors/414.html b/files/templates/errors/414.html deleted file mode 100644 index e054bf531..000000000 --- a/files/templates/errors/414.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -Max video size is 32 MB (64 MB for paypigs) -{% endblock %} - -{% block pagetype %}error-414{% endblock %} - -{% block content %} -
-
-
- :#marseyretard: -

-		

Max video size is 32 MB (64 MB for paypigs)

- -
-
-
-{% endblock %} diff --git a/files/templates/errors/415.html b/files/templates/errors/415.html deleted file mode 100644 index 0139fd86d..000000000 --- a/files/templates/errors/415.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -415 Unsupported Media Type -{% endblock %} - -{% block pagetype %}error-415{% endblock %} - -{% block content %} -
-
-
- :#marseydetective: -

-		

415 Unsupported Media Type

-

Please upload only Image, Video, or Audio files!

-
-
-
-{% endblock %} diff --git a/files/templates/errors/418.html b/files/templates/errors/418.html deleted file mode 100644 index 15642a384..000000000 --- a/files/templates/errors/418.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -WEBM videos are not allowed -{% endblock %} - -{% block pagetype %}error-418{% endblock %} - -{% block content %} -
-
-
- :#marseytea: -

-		

WEBM videos are not allowed

- -
-
-
-{% endblock %} \ No newline at end of file diff --git a/files/templates/errors/429.html b/files/templates/errors/429.html deleted file mode 100644 index 0860ae1f2..000000000 --- a/files/templates/errors/429.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -429 Too Many Requests -{% endblock %} - -{% block pagetype %}error-429{% endblock %} - -{% block content %} -
-
-
- :#marseyrentfree: -

-		

429 Too Many Requests

-

go spam somewhere else nerd

-
-
-
-{% endblock %} From ec610e99c5362b7c836e4f39950e9a78b7a2f778 Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 03:43:47 -0700 Subject: [PATCH 03/12] add details and tell jinja that variables have kept themselves safe --- files/templates/errors/error.html | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/files/templates/errors/error.html b/files/templates/errors/error.html index 147fb0f1e..a813bafc0 100644 --- a/files/templates/errors/error.html +++ b/files/templates/errors/error.html @@ -10,12 +10,16 @@
- {% if img %} + {% if img -%} :#{{img}}: - {% endif %} + {%- endif %}

 			

{{code}} {{title}}

-

{{msg}}

+

{{msg|safe}}

+ {% if details -%} +
{{details|safe}}
+ {%- endif %} +
From e364ce8043ec125afb23e801f698698fecb6312a Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 04:03:43 -0700 Subject: [PATCH 04/12] add 417 and fix 414 --- files/routes/errors.py | 3 ++- files/templates/errors/error.html | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/files/routes/errors.py b/files/routes/errors.py index 0cb13116f..f14e841fc 100644 --- a/files/routes/errors.py +++ b/files/routes/errors.py @@ -13,8 +13,9 @@ WERKZEUG_ERROR_DESCRIPTIONS = { 405: "The method is not allowed for the requested URL.", 406: "The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.", 413: "The data value transmitted exceeds the capacity limit.", - 414: "The length of the requested URL exceeds the capacity limit for this server. The request cannot be processed.", + 414: "The length of the requested URL exceeds the capacity limit for this server. The request cannot be processed.", 415: "The server does not support the media type transmitted in the request.", + 417: "The server could not meet the requirements of the Expect header", 418: "This server is a teapot, not a coffee machine", 429: "This user has exceeded an allotted request count. Try again later.", 500: "The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.", diff --git a/files/templates/errors/error.html b/files/templates/errors/error.html index a813bafc0..c29d284b9 100644 --- a/files/templates/errors/error.html +++ b/files/templates/errors/error.html @@ -15,9 +15,9 @@ {%- endif %}

 			

{{code}} {{title}}

-

{{msg|safe}}

+

{{msg|safe}}

{% if details -%} -
{{details|safe}}
+
{{details|safe}}
{%- endif %}
Go to frontpage
From 665e6e4b21bbf9005d386c557fef6b3fbe7c0c0b Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 04:30:37 -0700 Subject: [PATCH 05/12] handle 500 and 401 (when using API) with our handlers * 401 had sign up and login buttons on their page, but the page was never visible, we only show this when the API is being used --- files/routes/errors.py | 12 +++++++++--- files/templates/errors/401.html | 24 ------------------------ files/templates/errors/500.html | 31 ------------------------------- 3 files changed, 9 insertions(+), 58 deletions(-) delete mode 100644 files/templates/errors/401.html delete mode 100644 files/templates/errors/500.html diff --git a/files/routes/errors.py b/files/routes/errors.py index f14e841fc..5e59fd708 100644 --- a/files/routes/errors.py +++ b/files/routes/errors.py @@ -53,6 +53,10 @@ ERROR_MSGS = { 500: "Hiiiii it's carp! I think this error means that there's a timeout error. And I think that means something took too long to load so it decided not to work at all. If you keep seeing this on the same page but not other pages, then something is probably wrong with that specific function. It may not be called a function, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily <3", } +ERROR_MSGS_PCM = { + 500: "Hiiiii it's nigger! I think this error means that there's a nigger error. And I think that means something took too long to load so it decided to be a nigger. If you keep seeing this on the same page but not other pages, then something its probably a niggerfaggot. It may not be called a nigger, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily <3" +} + ERROR_MARSEYS = { 400: "marseybrainlet", 401: "marseydead", @@ -88,11 +92,14 @@ def error(e): if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": title, "code": e.code, "description": msg, "details": details} img = ERROR_MARSEYS.get(e.code, 'marseyl') + if e.code == 500 and SITE_NAME == 'PCM': + msg = ERROR_MSGS_PCM.get(e.code, str(e.code)) + img = "wholesome" return render_template('errors/error.html', err=True, title=title, msg=msg, details=details, img=img), e.code @app.errorhandler(401) def error_401(e): - if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "401 Unauthorized"}, 401 + if request.headers.get("Authorization") or request.headers.get("xhr"): return error(e) else: path = request.path qs = urlencode(dict(request.values)) @@ -104,8 +111,7 @@ def error_401(e): @app.errorhandler(500) def error_500(e): g.db.rollback() - if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": "500 Internal Server Error"}, 500 - else: return render_template('errors/500.html', err=True), 500 + return error(e) @app.post("/allow_nsfw") diff --git a/files/templates/errors/401.html b/files/templates/errors/401.html deleted file mode 100644 index 6403b0553..000000000 --- a/files/templates/errors/401.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -401 Not Authorized -{% endblock %} - -{% block pagetype %}error-401{% endblock %} - -{% block content %} -
-
-
- - :#marseydead: -

-
-			

401 Not Authorized

-

What you're trying to do requires an account. I think. The original error message said something about a castle and I hated that.

- - -
-
-
-{% endblock %} \ No newline at end of file diff --git a/files/templates/errors/500.html b/files/templates/errors/500.html deleted file mode 100644 index ce0e69e84..000000000 --- a/files/templates/errors/500.html +++ /dev/null @@ -1,31 +0,0 @@ -{% extends "default.html" %} - -{% block title %} -500 Internal Server Error -{% endblock %} - -{% block pagetype %}error-500{% endblock %} - -{% block content %} -
-
-
- {% if SITE_NAME == 'PCM' %} - wholesome - {% else %} - :#marseycarp3: - {% endif %} -

-			

500 Internal Server Error

-

- {% if SITE_NAME == 'PCM' %} - Hiiiii it's nigger! I think this error means that there's a nigger error. And I think that means something took too long to load so it decided to be a nigger. If you keep seeing this on the same page but not other pages, then something its probably a niggerfaggot. It may not be called a nigger, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily <3 - {% else %} - Hiiiii it's carp! I think this error means that there's a timeout error. And I think that means something took too long to load so it decided not to work at all. If you keep seeing this on the same page but not other pages, then something is probably wrong with that specific function. It may not be called a function, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily <3 - {% endif %} -

- -
-
-
-{% endblock %} From 64cd11377ed9c9e9cfe1c486f8f300e6d89c64cb Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 04:49:22 -0700 Subject: [PATCH 06/12] fix 500 werkzeug description, add note, and remove unused import --- files/routes/errors.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/files/routes/errors.py b/files/routes/errors.py index 5e59fd708..c6db0855b 100644 --- a/files/routes/errors.py +++ b/files/routes/errors.py @@ -1,10 +1,14 @@ from files.helpers.wrappers import * from flask import * -from werkzeug.exceptions import HTTPException from urllib.parse import quote, urlencode import time from files.__main__ import app, limiter +# If you're adding an error, go here: +# https://github.com/pallets/werkzeug/blob/main/src/werkzeug/exceptions.py +# and copy the description for the error code you're adding so that the +# default error message doesn't show up on the message. Please be exact. + WERKZEUG_ERROR_DESCRIPTIONS = { 400: "The browser (or proxy) sent a request that this server could not understand.", 401: "The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad password), or your browser doesn't understand how to supply the credentials required.", @@ -18,7 +22,7 @@ WERKZEUG_ERROR_DESCRIPTIONS = { 417: "The server could not meet the requirements of the Expect header", 418: "This server is a teapot, not a coffee machine", 429: "This user has exceeded an allotted request count. Try again later.", - 500: "The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.", + 500: "The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.", } ERROR_TITLES = { From c0acb1722b97cd1f52547035e216b0c958cf084d Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 06:01:39 -0700 Subject: [PATCH 07/12] abort a bunch of stuff where we manually returned JSON --- files/assets/js/bootstrap.js | 1 + files/routes/admin.py | 22 +++++------ files/routes/asset_submissions.py | 41 ++++++++------------ files/routes/awards.py | 32 +++++++--------- files/routes/casino.py | 40 ++++++++++---------- files/routes/comments.py | 47 +++++++++++------------ files/routes/discord.py | 7 +--- files/routes/hats.py | 20 +++++----- files/routes/lottery.py | 2 +- files/routes/polls.py | 8 ++-- files/routes/posts.py | 23 +++++------ files/routes/reporting.py | 15 ++++---- files/routes/search.py | 6 +-- files/routes/settings.py | 28 +++++++------- files/routes/subs.py | 8 ++-- files/routes/users.py | 63 +++++++++++++++---------------- files/routes/votes.py | 4 +- 17 files changed, 171 insertions(+), 196 deletions(-) diff --git a/files/assets/js/bootstrap.js b/files/assets/js/bootstrap.js index 8b4ca23e5..95913ffef 100644 --- a/files/assets/js/bootstrap.js +++ b/files/assets/js/bootstrap.js @@ -218,6 +218,7 @@ function post_toast(t, url, button1, button2, classname, extra_actions) { } else { document.getElementById('toast-post-error-text').innerText = "Error, please try again later." if (data && data["error"]) document.getElementById('toast-post-error-text').innerText = data["error"]; + if (data && data["details"]) document.getElementById('toast-post-error-text').innerText = data["details"]; bootstrap.Toast.getOrCreateInstance(document.getElementById('toast-post-error')).show(); } if (!isShopConfirm) diff --git a/files/routes/admin.py b/files/routes/admin.py index 401d0bd89..f430f34c7 100644 --- a/files/routes/admin.py +++ b/files/routes/admin.py @@ -196,7 +196,7 @@ def remove_admin(v, username): @admin_level_required(PERMS['POST_BETS_DISTRIBUTE']) def distribute(v, option_id): autojanny = get_account(AUTOJANNY_ID) - if autojanny.coins == 0: return {"error": "@AutoJanny has 0 coins"}, 400 + if autojanny.coins == 0: abort(400, "@AutoJanny has 0 coins") try: option_id = int(option_id) except: abort(400) @@ -303,7 +303,7 @@ def revert_actions(v, username): def club_allow(v, username): u = get_user(username, v=v) - if u.admin_level >= v.admin_level: return {"error": "noob"}, 400 + if u.admin_level >= v.admin_level: abort(403, 'noob') u.club_allowed = True g.db.add(u) @@ -329,7 +329,7 @@ def club_allow(v, username): def club_ban(v, username): u = get_user(username, v=v) - if u.admin_level >= v.admin_level: return {"error": "noob"}, 400 + if u.admin_level >= v.admin_level: abort(403, 'noob') u.club_allowed = False @@ -490,7 +490,7 @@ def purge_cache(v): g.db.add(ma) if response == "": return {"message": "Cache purged!"} - return {"error": "Failed to purge cache."}, 400 + abort(400, 'Failed to purge cache.') @app.post("/admin/under_attack") @@ -507,7 +507,7 @@ def under_attack(v): response = str(requests.patch(f'https://api.cloudflare.com/client/v4/zones/{CF_ZONE}/settings/security_level', headers=CF_HEADERS, data='{"value":"high"}', timeout=5)) if response == "": return {"message": "Under attack mode disabled!"} - return {"error": "Failed to disable under attack mode."}, 400 + abort(400, "Failed to disable under attack mode.") else: ma = ModAction( kind="enable_under_attack", @@ -517,7 +517,7 @@ def under_attack(v): response = str(requests.patch(f'https://api.cloudflare.com/client/v4/zones/{CF_ZONE}/settings/security_level', headers=CF_HEADERS, data='{"value":"under_attack"}', timeout=5)) if response == "": return {"message": "Under attack mode enabled!"} - return {"error": "Failed to enable under attack mode."}, 400 + abort(400, "Failed to enable under attack mode.") @app.get("/admin/badge_grant") @admin_level_required(PERMS['USER_BADGES']) @@ -1158,7 +1158,7 @@ def approve_post(post_id, v): post = get_post(post_id) if post.author.id == v.id and post.author.agendaposter and AGENDAPOSTER_PHRASE not in post.body.lower() and post.sub != 'chudrama': - return {"error": "You can't bypass the chud award!"}, 400 + abort(400, "You can't bypass the chud award!") if post.is_banned: ma=ModAction( @@ -1223,7 +1223,7 @@ def sticky_post(post_id, v): if v.admin_level >= PERMS['BYPASS_PIN_LIMIT']: post.stickied = v.username post.stickied_utc = int(time.time()) + 3600 - else: return {"error": f"Can't exceed {PIN_LIMIT} pinned posts limit!"}, 403 + else: abort(403, f"Can't exceed {PIN_LIMIT} pinned posts limit!") else: post.stickied = v.username g.db.add(post) @@ -1246,7 +1246,7 @@ def unsticky_post(post_id, v): post = get_post(post_id) if post.stickied: - if post.stickied.endswith('(pin award)'): return {"error": "Can't unpin award pins!"}, 403 + if post.stickied.endswith('(pin award)'): abort(403, "Can't unpin award pins!") post.stickied = None post.stickied_utc = None @@ -1296,7 +1296,7 @@ def unsticky_comment(cid, v): comment = get_comment(cid, v=v) if comment.stickied: - if comment.stickied.endswith("(pin award)"): return {"error": "Can't unpin award pins!"}, 403 + if comment.stickied.endswith("(pin award)"): abort(403, "Can't unpin award pins!") comment.stickied = None g.db.add(comment) @@ -1344,7 +1344,7 @@ def approve_comment(c_id, v): if not comment: abort(404) if comment.author.id == v.id and comment.author.agendaposter and AGENDAPOSTER_PHRASE not in comment.body.lower() and comment.post.sub != 'chudrama': - return {"error": "You can't bypass the chud award!"}, 400 + abort(400, "You can't bypass the chud award!") if comment.is_banned: ma=ModAction( diff --git a/files/routes/asset_submissions.py b/files/routes/asset_submissions.py index 26d0ae625..8556724b8 100644 --- a/files/routes/asset_submissions.py +++ b/files/routes/asset_submissions.py @@ -96,27 +96,27 @@ if SITE not in ('pcmemes.net', 'watchpeopledie.co'): @admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_MARSEYS']) def approve_marsey(v, name): if AEVANN_ID and v.id not in (AEVANN_ID, CARP_ID, SNAKES_ID): - return {"error": "Only Carp can approve marseys!"}, 403 + abort(403, "Only Carp can approve marseys!") name = name.lower().strip() marsey = g.db.query(Marsey).filter_by(name=name).one_or_none() if not marsey: - return {"error": f"This marsey '{name}' doesn't exist!"}, 404 + abort(404, f"This marsey '{name}' doesn't exist!") tags = request.values.get('tags').lower().strip() if not tags: - return {"error": "You need to include tags!"}, 400 + abort(400, "You need to include tags!") new_name = request.values.get('name').lower().strip() if not new_name: - return {"error": "You need to include name!"}, 400 + abort(400, "You need to include name!") if not marsey_regex.fullmatch(new_name): - return {"error": "Invalid name!"}, 400 + abort(400, "Invalid name!") if not tags_regex.fullmatch(tags): - return {"error": "Invalid tags!"}, 400 + abort(400, "Invalid tags!") marsey.name = new_name @@ -168,10 +168,10 @@ if SITE not in ('pcmemes.net', 'watchpeopledie.co'): marsey = g.db.query(Marsey).filter_by(name=name).one_or_none() if not marsey: - return {"error": f"This marsey '{name}' doesn't exist!"}, 404 + abort(404, f"This marsey '{name}' doesn't exist!") if v.id not in (marsey.submitter_id, AEVANN_ID, CARP_ID): - return {"error": "Only Carp can remove marseys!"}, 403 + abort(403, "Only Carp can remove marseys!") if v.id != marsey.submitter_id: msg = f"@{v.username} has rejected a marsey you submitted: `'{marsey.name}'`" @@ -257,27 +257,20 @@ if SITE not in ('pcmemes.net', 'watchpeopledie.co'): @admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_HATS']) def approve_hat(v, name): if AEVANN_ID and v.id not in (AEVANN_ID, CARP_ID, SNAKES_ID): - return {"error": "Only Carp can approve hats!"}, 403 + abort(403, "Only Carp can approve hats!"}, 403 name = name.strip() hat = g.db.query(HatDef).filter_by(name=name).one_or_none() - if not hat: - return {"error": f"This hat '{name}' doesn't exist!"}, 404 + if not hat: abort(404, f"This hat '{name}' doesn't exist!") description = request.values.get('description').strip() - if not description: - return {"error": "You need to include description!"}, 400 + if not description: abort(400, "You need to include a description!") new_name = request.values.get('name').strip() - if not new_name: - return {"error": "You need to include name!"}, 400 - - if not hat_regex.fullmatch(new_name): - return {"error": "Invalid name!"}, 400 - - if not description_regex.fullmatch(description): - return {"error": "Invalid description!"}, 400 + if not new_name: abort(400, "You need to include a name!") + if not hat_regex.fullmatch(new_name): abort(400, "Invalid name!") + if not description_regex.fullmatch(description): abort(400, "Invalid description!") hat.price = int(request.values.get('price')) hat.name = new_name @@ -332,11 +325,9 @@ if SITE not in ('pcmemes.net', 'watchpeopledie.co'): name = name.strip() hat = g.db.query(HatDef).filter_by(name=name).one_or_none() - if not hat: - return {"error": f"This hat '{name}' doesn't exist!"}, 404 - + if not hat: abort(404, f"This hat '{name}' doesn't exist!") if v.id not in (hat.submitter_id, AEVANN_ID, CARP_ID): - return {"error": "Only Carp can remove hats!"}, 403 + abort(403, 'Only Carp can remove hats!') if v.id != hat.submitter_id: msg = f"@{v.username} has rejected a hat you submitted: `'{hat.name}'`" diff --git a/files/routes/awards.py b/files/routes/awards.py index faecb388d..a7bdce2c7 100644 --- a/files/routes/awards.py +++ b/files/routes/awards.py @@ -49,10 +49,10 @@ def buy(v, award): if award == 'benefactor' and not request.values.get("mb"): - return {"error": "You can only buy the Benefactor award with marseybux."}, 403 + abort(403, "You can only buy the Benefactor award with marseybux.") if award == 'ghost' and v.admin_level < PERMS['BUY_GHOST_AWARD']: - return {"error": "Only admins can buy this award."}, 403 + abort(403, "Only admins can buy this award") AWARDS = deepcopy(AWARDS2) @@ -67,15 +67,15 @@ def buy(v, award): if request.values.get("mb"): if award == "grass": - return {"error": "You can't buy the grass award with marseybux."}, 403 + abort(403, "You can't buy the grass award with marseybux.") charged = v.charge_account('procoins', price) if not charged: - return {"error": "Not enough marseybux."}, 400 + abort(400, "Not enough marseybux.") else: charged = v.charge_account('coins', price) if not charged: - return {"error": "Not enough coins."}, 400 + abort(400, "Not enough coins.") v.coins_spent += price if v.coins_spent >= 1000000: @@ -129,8 +129,6 @@ def buy(v, award): @is_not_permabanned @feature_required('BADGES') def award_thing(v, thing_type, id): - - if thing_type == 'post': thing = get_post(id) else: thing = get_comment(id) @@ -142,8 +140,7 @@ def award_thing(v, thing_type, id): if v.house: AWARDS[v.house] = HOUSE_AWARDS[v.house] - if kind not in AWARDS: - return {"error": "This award doesn't exist."}, 404 + if kind not in AWARDS: abort(404, "This award doesn't exist") award = g.db.query(AwardRelationship).filter( AwardRelationship.kind == kind, @@ -152,8 +149,7 @@ def award_thing(v, thing_type, id): AwardRelationship.comment_id == None ).first() - if not award: - return {"error": "You don't have that award."}, 404 + if not award: abort(404, "You don't have that award") if thing_type == 'post': award.submission_id = thing.id else: award.comment_id = thing.id @@ -167,13 +163,13 @@ def award_thing(v, thing_type, id): if author.shadowbanned: abort(404) if SITE == 'rdrama.net' and author.id in (PIZZASHILL_ID, CARP_ID): - return {"error": "This user is immune to awards."}, 403 + abort(403, "This user is immune to awards.") if kind == "benefactor" and author.id == v.id: - return {"error": "You can't use this award on yourself."}, 400 + abort(400, "You can't use this award on yourself.") if kind == 'marsify' and author.marsify == 1: - return {"error": "User is already permanently marsified!"}, 403 + abort(403, "User is already permanently marsified!") if v.id != author.id: safe_username = "👻" if thing.ghost else f"@{author.username}" @@ -254,7 +250,7 @@ def award_thing(v, thing_type, id): g.db.add(thing) elif kind == "agendaposter" and not (author.agendaposter and author.agendaposter == 0): if author.marseyawarded: - return {"error": "This user is the under the effect of a conflicting award: Marsey award."}, 404 + abort(400, "This user is under the effect of a conflicting award: Marsey award.") if author.agendaposter and time.time() < author.agendaposter: author.agendaposter += 86400 else: author.agendaposter = int(time.time()) + 86400 @@ -284,13 +280,13 @@ def award_thing(v, thing_type, id): badge_grant(user=author, badge_id=98) elif kind == "pizzashill": if author.bird: - return {"error": "This user is the under the effect of a conflicting award: Bird Site award."}, 404 + abort(400, "This user is under the effect of a conflicting award: Bird Site award.") if author.longpost: author.longpost += 86400 else: author.longpost = int(time.time()) + 86400 badge_grant(user=author, badge_id=97) elif kind == "bird": if author.longpost: - return {"error": "This user is the under the effect of a conflicting award: Pizzashill award."}, 404 + abort(400, "This user is under the effect of a conflicting award: Pizzashill award.") if author.bird: author.bird += 86400 else: author.bird = int(time.time()) + 86400 badge_grant(user=author, badge_id=95) @@ -317,7 +313,7 @@ def award_thing(v, thing_type, id): else: author.progressivestack = int(time.time()) + 21600 badge_grant(user=author, badge_id=94) elif kind == "benefactor": - if author.patron: return {"error": "This user is already a paypig!"}, 400 + if author.patron: abort(400, "This user is already a paypig!") author.patron = 1 if author.patron_utc: author.patron_utc += 2629746 else: author.patron_utc = int(time.time()) + 2629746 diff --git a/files/routes/casino.py b/files/routes/casino.py index 5208f6615..c88194361 100644 --- a/files/routes/casino.py +++ b/files/routes/casino.py @@ -55,8 +55,8 @@ def casino_game_page(v, game): @auth_required @feature_required('GAMBLING') def casino_game_feed(v, game): - if v.rehab: - return {"error": "You are under Rehab award effect!"}, 400 + if v.rehab: + abort(403, "You are under Rehab award effect!") elif game not in CASINO_GAME_KINDS: abort(404) @@ -83,27 +83,27 @@ def lottershe(v): @feature_required('GAMBLING') def pull_slots(v): if v.rehab: - return {"error": "You are under Rehab award effect!"}, 400 + abort(403, "You are under Rehab award effect!") try: wager = int(request.values.get("wager")) except: - return {"error": "Invalid wager."}, 400 + abort(400, "Invalid wager.") try: currency = request.values.get("currency") except: - return {"error": "Invalid currency (expected 'coin' or 'marseybux')."}, 400 + abort(400, "Invalid currency (expected 'coin' or 'marseybux').") if (currency == "coin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins): - return {"error": f"Not enough {currency} to make that bet."}, 400 + abort(400, f"Not enough {currency} to make that bet") success, game_state = casino_slot_pull(v, wager, currency) if success: return {"game_state": game_state, "gambler": {"coins": v.coins, "procoins": v.procoins}} else: - return {"error": f"Wager must be more than 5 {currency}."}, 400 + abort(400, f"Wager must be more than 5 {currency}") # 21 @@ -113,7 +113,7 @@ def pull_slots(v): @feature_required('GAMBLING') def blackjack_deal_to_player(v): if v.rehab: - return {"error": "You are under Rehab award effect!"}, 400 + abort(403, "You are under Rehab award effect!") try: wager = int(request.values.get("wager")) @@ -124,7 +124,7 @@ def blackjack_deal_to_player(v): return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} except Exception as e: - return {"error": str(e)}, 400 + abort(400, str(e)) @app.post("/casino/twentyone/hit") @@ -133,14 +133,14 @@ def blackjack_deal_to_player(v): @feature_required('GAMBLING') def blackjack_player_hit(v): if v.rehab: - return {"error": "You are under Rehab award effect!"}, 400 + abort(403, "You are under Rehab award effect!") try: state = dispatch_action(v, BlackjackAction.HIT) feed = get_game_feed('blackjack') return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} except: - return {"error": "Unable to hit."}, 400 + abort(400, "Unable to hit.") @app.post("/casino/twentyone/stay") @@ -149,14 +149,14 @@ def blackjack_player_hit(v): @feature_required('GAMBLING') def blackjack_player_stay(v): if v.rehab: - return {"error": "You are under Rehab award effect!"}, 400 + abort(403, "You are under Rehab award effect!") try: state = dispatch_action(v, BlackjackAction.STAY) feed = get_game_feed('blackjack') return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} except: - return {"error": "Unable to stay."}, 400 + abort(400, "Unable to stay.") @app.post("/casino/twentyone/double-down") @@ -165,14 +165,14 @@ def blackjack_player_stay(v): @feature_required('GAMBLING') def blackjack_player_doubled_down(v): if v.rehab: - return {"error": "You are under Rehab award effect!"}, 400 + abort(403, "You are under Rehab award effect!") try: state = dispatch_action(v, BlackjackAction.DOUBLE_DOWN) feed = get_game_feed('blackjack') return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} except: - return {"error": "Unable to double down."}, 400 + abort(400, "Unable to double down.") @app.post("/casino/twentyone/buy-insurance") @@ -181,14 +181,14 @@ def blackjack_player_doubled_down(v): @feature_required('GAMBLING') def blackjack_player_bought_insurance(v): if v.rehab: - return {"error": "You are under Rehab award effect!"}, 400 + abort(403, "You are under Rehab award effect!") try: state = dispatch_action(v, BlackjackAction.BUY_INSURANCE) feed = get_game_feed('blackjack') return {"success": True, "state": state, "feed": feed, "gambler": {"coins": v.coins, "procoins": v.procoins}} except: - return {"error": "Unable to buy insurance."}, 400 + abort(403, "Unable to buy insurance.") # Roulette @app.get("/casino/roulette/bets") @@ -197,7 +197,7 @@ def blackjack_player_bought_insurance(v): @feature_required('GAMBLING') def roulette_get_bets(v): if v.rehab: - return {"error": "You are under Rehab award effect!"}, 400 + abort(403, "You are under Rehab award effect!") bets = get_roulette_bets() @@ -210,7 +210,7 @@ def roulette_get_bets(v): @feature_required('GAMBLING') def roulette_player_placed_bet(v): if v.rehab: - return {"error": "You are under Rehab award effect!"}, 400 + abort(403, "You are under Rehab award effect!") try: bet = request.values.get("bet") @@ -227,4 +227,4 @@ def roulette_player_placed_bet(v): return {"success": True, "bets": bets, "gambler": {"coins": v.coins, "procoins": v.procoins}} except: - return {"error": "Unable to place a bet."}, 400 + abort(400, "Unable to place a bet.") diff --git a/files/routes/comments.py b/files/routes/comments.py index 74f5d1043..a6b060ca1 100644 --- a/files/routes/comments.py +++ b/files/routes/comments.py @@ -56,7 +56,7 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None, sub=None): post = get_post(pid, v=v) if post.over_18 and not (v and v.over_18) and not session.get('over_18', 0) >= int(time.time()): - if request.headers.get("Authorization"): return {"error": 'This content is not suitable for some users and situations.'}, 403 + if request.headers.get("Authorization"): abort(403, "This content is not suitable for some users and situations.") else: return render_template("errors/nsfw.html", v=v) try: context = min(int(request.values.get("context", 0)), 8) @@ -126,17 +126,17 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None, sub=None): @limiter.limit("1/second;20/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}') @auth_required def comment(v): - if v.is_suspended: return {"error": "You can't perform this action while banned."}, 403 + if v.is_suspended: abort(403, "You can't perform this action while banned.") parent_submission = request.values.get("submission").strip() parent_fullname = request.values.get("parent_fullname").strip() parent_post = get_post(parent_submission, v=v) sub = parent_post.sub - if sub and v.exiled_from(sub): return {"error": f"You're exiled from /h/{sub}"}, 403 + if sub and v.exiled_from(sub): abort(403, f"You're exiled from /h/{sub}") if sub in ('furry','vampire','racist','femboy') and not v.client and not v.house.lower().startswith(sub): - return {"error": f"You need to be a member of House {sub.capitalize()} to comment in /h/{sub}"}, 400 + abort(403, f"You need to be a member of House {sub.capitalize()} to comment in /h/{sub}") if parent_post.club and not (v and (v.paid_dues or v.id == parent_post.author_id)): abort(403) @@ -157,18 +157,17 @@ def comment(v): if not parent.can_see(v): abort(404) if parent.deleted_utc != 0: abort(404) - if level > COMMENT_MAX_DEPTH: - return {"error": f"Max comment level is {COMMENT_MAX_DEPTH}"}, 400 + if level > COMMENT_MAX_DEPTH: abort(400, f"Max comment level is {COMMENT_MAX_DEPTH}") body = sanitize_raw_body(request.values.get("body", ""), False) if parent_post.id not in ADMIGGERS: if v.longpost and (len(body) < 280 or ' [](' in body or body.startswith('[](')): - return {"error":"You have to type more than 280 characters!"}, 403 + abort(403, "You have to type more than 280 characters!") elif v.bird and len(body) > 140: - return {"error":"You have to type less than 140 characters!"}, 403 + abort(403, "You have to type less than 140 characters!") - if not body and not request.files.get('file'): return {"error":"You need to actually write something!"}, 400 + if not body and not request.files.get('file'): abort(400, "You need to actually write something!") options = [] for i in poll_regex.finditer(body): @@ -187,7 +186,7 @@ def comment(v): oldname = f'/images/{time.time()}'.replace('.','') + '.webp' file.save(oldname) image = process_image(oldname, patron=v.patron) - if image == "": return {"error":"Image upload failed"}, 400 + if image == "": abort(400, "Image upload failed") if v.admin_level >= PERMS['SITE_SETTINGS_SIDEBARS_BANNERS_BADGES'] and level == 1: if parent_post.id == SIDEBAR_THREAD: li = sorted(os.listdir(f'files/assets/images/{SITE_NAME}/sidebar'), @@ -210,7 +209,7 @@ def comment(v): name = badge_def["name"] existing = g.db.query(BadgeDef).filter_by(name=name).one_or_none() - if existing: return {"error": "A badge with this name already exists!"}, 403 + if existing: abort(403, "A badge with this name already exists!") badge = BadgeDef(name=name, description=badge_def["description"]) g.db.add(badge) @@ -221,7 +220,7 @@ def comment(v): requests.post(f'https://api.cloudflare.com/client/v4/zones/{CF_ZONE}/purge_cache', headers=CF_HEADERS, data=f'{{"files": ["https://{SITE}/assets/images/badges/{badge.id}.webp"]}}', timeout=5) except Exception as e: - return {"error": str(e)}, 400 + abort(400, str(e)) body += f"\n\n![]({image})" elif file.content_type.startswith('video/'): body += f"\n\n{process_video(file)}" @@ -257,7 +256,7 @@ def comment(v): if existing: return {"error": f"You already made that comment: /comment/{existing.id}"}, 409 if parent.author.any_block_exists(v) and v.admin_level < PERMS['POST_COMMENT_MODERATION']: - return {"error": "You can't reply to users who have blocked you or users that you have blocked."}, 403 + abort(403, "You can't reply to users who have blocked you or users that you have blocked.") is_bot = v.id != BBBB_ID and (bool(request.headers.get("Authorization")) or (SITE == 'pcmemes.net' and v.id == SNAPPY_ID)) @@ -297,7 +296,7 @@ def comment(v): g.db.add(ma) g.db.commit() - return {"error": "Too much spam!"}, 403 + abort(403, "Too much spam!") if len(body_html) > COMMENT_BODY_HTML_LENGTH_LIMIT: abort(400) @@ -442,7 +441,7 @@ def comment(v): g.db.add(c) if v.marseyawarded and parent_post.id not in ADMIGGERS and marseyaward_body_regex.search(body_html): - return {"error":"You can only type marseys!"}, 403 + abort(403, "You can only type marseys!") check_for_treasure(body, c) @@ -469,7 +468,7 @@ def edit_comment(cid, v): c = get_comment(cid, v=v) if time.time() - c.created_utc > 7*24*60*60 and not (c.post and c.post.private): - return {"error":"You can't edit comments older than 1 week!"}, 403 + abort(403, "You can't edit comments older than 1 week!") if c.author_id != v.id: abort(403) if not c.post: abort(403) @@ -477,13 +476,13 @@ def edit_comment(cid, v): body = sanitize_raw_body(request.values.get("body", ""), False) if len(body) < 1 and not (request.files.get("file") and request.headers.get("cf-ipcountry") != "T1"): - return {"error":"You have to actually type something!"}, 400 + abort(400, "You have to actually type something!") if body != c.body or request.files.get("file") and request.headers.get("cf-ipcountry") != "T1": if v.longpost and (len(body) < 280 or ' [](' in body or body.startswith('[](')): - return {"error":"You have to type more than 280 characters!"}, 403 + abort(403, "You have to type more than 280 characters!") elif v.bird and len(body) > 140: - return {"error":"You have to type less than 140 characters!"}, 403 + abort(403, "You have to type less than 140 characters!") for i in poll_regex.finditer(body): body = body.replace(i.group(0), "") @@ -535,7 +534,7 @@ def edit_comment(cid, v): g.db.add(comment) g.db.commit() - return {"error": "Too much spam!"}, 403 + abort(403, "Too much spam!") body += process_files() body = body.strip()[:COMMENT_BODY_LENGTH_LIMIT] # process_files potentially adds characters to the post @@ -553,7 +552,7 @@ def edit_comment(cid, v): if len(body_html) > COMMENT_BODY_HTML_LENGTH_LIMIT: abort(400) if v.marseyawarded and marseyaward_body_regex.search(body_html): - return {"error":"You can only type marseys!"}, 403 + abort(403, "You can only type marseys!") c.body = body c.body_html = body_html @@ -568,7 +567,7 @@ def edit_comment(cid, v): g.db.add(notif) if v.agendaposter and not v.marseyawarded and AGENDAPOSTER_PHRASE not in c.body.lower() and c.post.sub != 'chudrama': - return {"error": f'You have to include "{AGENDAPOSTER_PHRASE}" in your comment!'}, 403 + abort(403, f'You have to include "{AGENDAPOSTER_PHRASE}" in your comment!') if int(time.time()) - c.created_utc > 60 * 3: c.edited_utc = int(time.time()) @@ -745,7 +744,6 @@ def diff_words(answer, guess): @limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}') @auth_required def handle_wordle_action(cid, v): - comment = get_comment(cid) if v.id != comment.author_id: @@ -757,8 +755,7 @@ def handle_wordle_action(cid, v): try: guess = request.values.get("thing").strip().lower() except: abort(400) - if len(guess) != 5: - return {"error": "Not a valid guess!"}, 400 + if len(guess) != 5: abort(400, "Not a valid guess!") if status == "active": guesses += "".join(cg + WORDLE_COLOR_MAPPINGS[diff] for cg, diff in zip(guess, diff_words(answer, guess))) diff --git a/files/routes/discord.py b/files/routes/discord.py index 2d2f31cc7..fff30625e 100644 --- a/files/routes/discord.py +++ b/files/routes/discord.py @@ -8,15 +8,10 @@ import requests @app.get("/discord") @is_not_permabanned def join_discord(v): - - if v.shadowbanned: return {"error": "Internal server error"}, 400 - + if v.shadowbanned: return {"error": "Internal Server Error"}, 500 now=int(time.time()) - state=generate_hash(f"{now}+{v.id}+discord") - state=f"{now}.{state}" - return redirect(f"https://discord.com/api/oauth2/authorize?client_id={DISCORD_CLIENT_ID}&redirect_uri=https%3A%2F%2F{SITE}%2Fdiscord_redirect&response_type=code&scope=identify%20guilds.join&state={state}") diff --git a/files/routes/hats.py b/files/routes/hats.py index 4eff02150..a78d445e2 100644 --- a/files/routes/hats.py +++ b/files/routes/hats.py @@ -36,25 +36,23 @@ def hats(v): @feature_required('HATS') def buy_hat(v, hat_id): try: hat_id = int(hat_id) - except: return {"error": "Hat not found!"}, 400 + except: abort(404, "Hat not found!") hat = g.db.query(HatDef).filter_by(submitter_id=None, id=hat_id).one_or_none() - if not hat: return {"error": "Hat not found!"}, 400 + if not hat: abort(404, "Hat not found!") existing = g.db.query(Hat).filter_by(user_id=v.id, hat_id=hat.id).one_or_none() - if existing: return {"error": "You already own this hat!"}, 400 + if existing: abort(400, "You already own this hat!") if request.values.get("mb"): charged = v.charge_account('procoins', hat.price) - if not charged: - return {"error": "Not enough marseybux."}, 400 + if not charged: abort(400, "Not enough marseybux.") hat.author.procoins += hat.price * 0.1 currency = "marseybux" else: charged = v.charge_account('coins', hat.price) - if not charged: - return {"error": "Not enough coins."}, 400 + if not charged: abort(400, "Not enough coins.") v.coins_spent_on_hats += hat.price hat.author.coins += hat.price * 0.1 @@ -86,10 +84,10 @@ def buy_hat(v, hat_id): @feature_required('HATS') def equip_hat(v, hat_id): try: hat_id = int(hat_id) - except: return {"error": "Hat not found!"}, 400 + except: abort(404, "Hat not found!") hat = g.db.query(Hat).filter_by(hat_id=hat_id, user_id=v.id).one_or_none() - if not hat: return {"error": "You don't own this hat!"}, 400 + if not hat: abort(403, "You don't own this hat!") hat.equipped = True g.db.add(hat) @@ -101,10 +99,10 @@ def equip_hat(v, hat_id): @feature_required('HATS') def unequip_hat(v, hat_id): try: hat_id = int(hat_id) - except: return {"error": "Hat not found!"}, 400 + except: abort(404, "Hat not found!") hat = g.db.query(Hat).filter_by(hat_id=hat_id, user_id=v.id).one_or_none() - if not hat: return {"error": "You don't own this hat!"}, 400 + if not hat: abort(403, "You don't own this hat!") hat.equipped = False g.db.add(hat) diff --git a/files/routes/lottery.py b/files/routes/lottery.py index 01544317b..930b56205 100644 --- a/files/routes/lottery.py +++ b/files/routes/lottery.py @@ -29,7 +29,7 @@ def lottery_start(v): @feature_required('GAMBLING') def lottery_buy(v): try: quantity = int(request.values.get("quantity")) - except: return {"error": "Invalid ticket quantity."}, 400 + except: abort(400, "Invalid ticket quantity.") success, message = purchase_lottery_tickets(v, quantity) lottery, participants = get_active_lottery_stats() diff --git a/files/routes/polls.py b/files/routes/polls.py index b75cfc68b..71e6ab5f2 100644 --- a/files/routes/polls.py +++ b/files/routes/polls.py @@ -19,10 +19,10 @@ def vote_option(option_id, v): sub = option.post.sub if sub in ('furry','vampire','racist','femboy') and not v.house.lower().startswith(sub): - return {"error": f"You need to be a member of House {sub.capitalize()} to vote on polls in /h/{sub}"}, 400 + abort(403, f"You need to be a member of House {sub.capitalize()} to vote on polls in /h/{sub}") if option.exclusive == 2: - if v.coins < POLL_BET_COINS: return {"error": f"You don't have {POLL_BET_COINS} coins!"}, 400 + if v.coins < POLL_BET_COINS: abort(400, f"You don't have {POLL_BET_COINS} coins!") v.coins -= POLL_BET_COINS g.db.add(v) autojanny = get_account(AUTOJANNY_ID) @@ -35,7 +35,7 @@ def vote_option(option_id, v): SubmissionOptionVote.submission_id==option.submission_id, SubmissionOption.exclusive==option.exclusive).one_or_none() if vote: - if option.exclusive == 2: return {"error": "You already voted on this bet!"}, 400 + if option.exclusive == 2: abort(400, "You already voted on this bet!") g.db.delete(vote) existing = g.db.query(SubmissionOptionVote).filter_by(option_id=option_id, user_id=v.id).one_or_none() @@ -85,7 +85,7 @@ def vote_option_comment(option_id, v): sub = option.comment.post.sub if sub in ('furry','vampire','racist','femboy') and not v.house.lower().startswith(sub): - return {"error": f"You need to be a member of House {sub.capitalize()} to vote on polls in /h/{sub}"}, 400 + abort(403, f"You need to be a member of House {sub.capitalize()} to vote on polls in /h/{sub}") if option.exclusive: vote = g.db.query(CommentOptionVote).join(CommentOption).filter( diff --git a/files/routes/posts.py b/files/routes/posts.py index 63470bcb4..13251f2a8 100644 --- a/files/routes/posts.py +++ b/files/routes/posts.py @@ -414,26 +414,26 @@ def edit_post(pid, v): # Disable edits on things older than 1wk unless it's a draft or editor is a jannie if (time.time() - p.created_utc > 7*24*60*60 and not p.private and not v.admin_level >= PERMS['POST_EDITING']): - return {"error": "You can't edit posts older than 1 week!"}, 403 + abort(403, "You can't edit posts older than 1 week!") title = sanitize_raw_title(request.values.get("title", "")) body = sanitize_raw_body(request.values.get("body", ""), True) if v.id == p.author_id: if v.longpost and (len(body) < 280 or ' [](' in body or body.startswith('[](')): - return {"error":"You have to type more than 280 characters!"}, 403 + abort(403, "You have to type more than 280 characters!") elif v.bird and len(body) > 140: - return {"error":"You have to type less than 140 characters!"}, 403 + abort(403, "You have to type less than 140 characters!") if not title: - return {"error": "Please enter a better title."}, 400 + abort(400, "Please enter a better title.") if title != p.title: torture = (v.agendaposter and not v.marseyawarded and p.sub != 'chudrama' and v.id == p.author_id) title_html = filter_emojis_only(title, golden=False, torture=torture) if v.id == p.author_id and v.marseyawarded and not marseyaward_title_regex.fullmatch(title_html): - return {"error":"You can only type marseys!"}, 403 + abort(403, "You can only type marseys!") p.title = title p.title_html = title_html @@ -466,7 +466,7 @@ def edit_post(pid, v): body_html = sanitize(body, golden=False, limit_pings=100, showmore=False, torture=torture) if v.id == p.author_id and v.marseyawarded and marseyaward_body_regex.search(body_html): - return {"error":"You can only type marseys!"}, 403 + abort(403, "You can only type marseys!") p.body = body @@ -477,12 +477,13 @@ def edit_post(pid, v): g.db.add(v) send_repeatable_notification(CARP_ID, p.permalink) - if len(body_html) > POST_BODY_HTML_LENGTH_LIMIT: return {"error":f"Submission body_html too long! (max {POST_BODY_HTML_LENGTH_LIMIT} characters)"}, 400 + if len(body_html) > POST_BODY_HTML_LENGTH_LIMIT: + abort(400, f"Submission body_html too long! (max {POST_BODY_HTML_LENGTH_LIMIT} characters)") p.body_html = body_html if v.id == p.author_id and v.agendaposter and not v.marseyawarded and AGENDAPOSTER_PHRASE not in f'{p.body}{p.title}'.lower() and p.sub != 'chudrama': - return {"error": f'You have to include "{AGENDAPOSTER_PHRASE}" in your post!'}, 403 + abort(403, f'You have to include "{AGENDAPOSTER_PHRASE}" in your post!') if not p.private and not p.ghost: @@ -702,7 +703,7 @@ def submit_post(v, sub=None): body = sanitize_raw_body(request.values.get("body", ""), True) def error(error): - if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": error}, 400 + if request.headers.get("Authorization") or request.headers.get("xhr"): abort(400, error) SUBS = [x[0] for x in g.db.query(Sub.name).order_by(Sub.name).all()] return render_template("submit.html", SUBS=SUBS, v=v, error=error, title=title, url=url, body=body), 400 @@ -1204,7 +1205,7 @@ def pin_post(post_id, v): post = get_post(post_id) if post: - if v.id != post.author_id: return {"error": "Only the post author's can do that!"}, 400 + if v.id != post.author_id: abort(400, "Only the post author's can do that!") post.is_pinned = not post.is_pinned g.db.add(post) @@ -1212,7 +1213,7 @@ def pin_post(post_id, v): if post.is_pinned: return {"message": "Post pinned!"} else: return {"message": "Post unpinned!"} - return {"error": "Post not found!"}, 400 + return abort(404, "Post not found!") extensions = ( diff --git a/files/routes/reporting.py b/files/routes/reporting.py index 45c000498..da9e83a6f 100644 --- a/files/routes/reporting.py +++ b/files/routes/reporting.py @@ -21,14 +21,13 @@ def flag_post(pid, v): if not v.is_banned: v.ban_reason = 'Blackjack' send_repeatable_notification(CARP_ID, f"reports on {post.permalink}") - if v.is_muted: - return {"error": "You are forbidden from making reports."}, 400 + if v.is_muted: abort(400, "You are forbidden from making reports.") reason = reason[:100] reason = filter_emojis_only(reason) - if len(reason) > 350: return {"error": "Too long."}, 400 + if len(reason) > 350: abort(400, "Too long.") if reason.startswith('!') and (v.admin_level >= PERMS['POST_COMMENT_MODERATION'] or post.sub and v.mods(post.sub)): post.flair = reason[1:] @@ -58,16 +57,16 @@ def flag_post(pid, v): sub_to = g.db.get(Sub, sub_to) sub_to = sub_to.name if sub_to else None - if sub_from == sub_to: {"error": f"Post is already in /h/{sub_to}"}, 400 + if sub_from == sub_to: abort(400, f"Post is already in /h/{sub_to}") if post.author.exiled_from(sub_to): - return {"error": f"User is exiled from this {HOLE_NAME}!"}, 400 + abort(400, f"User is exiled from this {HOLE_NAME}!") if sub_to in ('furry','vampire','racist','femboy') and not v.client and not post.author.house.lower().startswith(sub_to): if v.id == post.author_id: - return {"error": f"You need to be a member of House {sub.capitalize()} to post in /h/{sub}"}, 403 + abort(403, f"You need to be a member of House {sub_to.capitalize()} to post in /h/{sub_to}") else: - return {"error": f"@{post.author.username} needs to be a member of House {sub.capitalize()} for their post to be moved to /h/{sub}"}, 400 + abort(403, f"@{post.author.username} needs to be a member of House {sub_to.capitalize()} for their post to be moved to /h/{sub_to}") post.sub = sub_to g.db.add(post) @@ -135,7 +134,7 @@ def flag_comment(cid, v): reason = filter_emojis_only(reason) - if len(reason) > 350: return {"error": "Too long."}, 400 + if len(reason) > 350: abort(400, "Too long.") flag = CommentFlag(comment_id=comment.id, user_id=v.id, reason=reason) diff --git a/files/routes/search.py b/files/routes/search.py index 24257bbaf..b9336e099 100644 --- a/files/routes/search.py +++ b/files/routes/search.py @@ -75,7 +75,7 @@ def searchposts(v): author = get_user(criteria['author'], v=v, include_shadowbanned=False) if author.is_private and author.id != v.id and v.admin_level < PERMS['VIEW_PRIVATE_PROFILES'] and not v.eye: if request.headers.get("Authorization"): - return {"error": f"@{author.username}'s profile is private; You can't use the 'author' syntax on them"}, 400 + abort(403, f"@{author.username}'s profile is private; You can't use the 'author' syntax on them") return render_template("search.html", v=v, query=query, @@ -193,7 +193,7 @@ def searchcomments(v): if 'post' in criteria: try: post = int(criteria['post']) - except: return {"error": f"Post with id {post} does not exist."}, 400 + except: abort(404, f"Post with id {post} does not exist."}) comments = comments.filter(Comment.parent_submission == post) @@ -202,7 +202,7 @@ def searchcomments(v): author = get_user(criteria['author'], v=v, include_shadowbanned=False) if author.is_private and author.id != v.id and v.admin_level < PERMS['VIEW_PRIVATE_PROFILES'] and not v.eye: if request.headers.get("Authorization"): - return {"error": f"@{author.username}'s profile is private; You can't use the 'author' syntax on them"}, 400 + abort(403, f"@{author.username}'s profile is private; You can't use the 'author' syntax on them") return render_template("search_comments.html", v=v, query=query, total=0, page=page, comments=[], sort=sort, t=t, next_exists=False, error=f"@{author.username}'s profile is private; You can't use the 'author' syntax on them.") diff --git a/files/routes/settings.py b/files/routes/settings.py index b764a3cee..f1781d483 100644 --- a/files/routes/settings.py +++ b/files/routes/settings.py @@ -263,7 +263,7 @@ def settings_profile_post(v): if theme: if theme in {"4chan","classic","classic_dark","coffee","dark","dramblr","light","midnight","transparent","tron","win98"}: if theme == "transparent" and not v.background: - return {"error": "You need to set a background to use the transparent theme!"}, 400 + abort(400, "You need to set a background to use the transparent theme!") v.theme = theme if theme == "win98": v.themecolor = "30409f" updated = True @@ -294,7 +294,7 @@ def settings_profile_post(v): return {"message": "Your settings have been updated."} else: - return {"error": "You didn't change anything."}, 400 + abort(400, "You didn't change anything.") @app.post("/settings/filters") @@ -348,23 +348,23 @@ def themecolor(v): @auth_required def gumroad(v): if not (v.email and v.is_activated): - return {"error": f"You must have a verified email to verify {patron} status and claim your rewards!"}, 400 + abort(400, f"You must have a verified email to verify {patron} status and claim your rewards!") data = {'access_token': GUMROAD_TOKEN, 'email': v.email} response = requests.get('https://api.gumroad.com/v2/sales', data=data, timeout=5).json()["sales"] - if len(response) == 0: return {"error": "Email not found"}, 404 + if len(response) == 0: abort(404, "Email not found") response = [x for x in response if x['variants_and_quantity']] response = response[0] tier = tiers[response["variants_and_quantity"]] - if v.patron == tier: return {"error": f"{patron} rewards already claimed"}, 400 + if v.patron == tier: abort(400, f"{patron} rewards already claimed") procoins = procoins_li[tier] - procoins_li[v.patron] - if procoins < 0: return {"error": f"{patron} rewards already claimed"}, 400 + if procoins < 0: abort(400, f"{patron} rewards already claimed") existing = g.db.query(User.id).filter(User.email == v.email, User.is_activated == True, User.patron >= tier).first() - if existing: return {"error": f"{patron} rewards already claimed on another account"}, 400 + if existing: abort(400, f"{patron} rewards already claimed on another account") v.patron = tier if v.discord_id: add_role(v, f"{tier}") @@ -513,7 +513,7 @@ def settings_log_out_others(v): @limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}') @auth_required def settings_images_profile(v): - if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403 + if request.headers.get("cf-ipcountry") == "T1": abort(403, "Image uploads are not allowed through TOR.") file = request.files["profile"] @@ -549,9 +549,7 @@ def settings_images_profile(v): @auth_required @feature_required('USERS_PROFILE_BANNER') def settings_images_banner(v): - - - if request.headers.get("cf-ipcountry") == "T1": return {"error":"Image uploads are not allowed through TOR."}, 403 + if request.headers.get("cf-ipcountry") == "T1": abort(403, "Image uploads are not allowed through TOR.") file = request.files["banner"] @@ -586,12 +584,12 @@ def settings_css_get(v): @limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}') @auth_required def settings_css(v): - if v.agendaposter: return {"error": "Agendapostered users can't edit css!"}, 400 + if v.agendaposter: abort(400, "Agendapostered users can't edit CSS!") css = request.values.get("css").strip().replace('\\', '').strip()[:4000] if '/transfer_bux") @@ -307,9 +306,9 @@ def transfer_bux(v, username): amount = int(amount) if amount.isdigit() else None reason = request.values.get("reason", "").strip() - if not amount or amount < 0: return {"error": "Invalid amount of marseybux."}, 400 - if v.procoins < amount: return {"error": "You don't have enough marseybux"}, 400 - if amount < 100: return {"error": "You have to gift at least 100 marseybux."}, 400 + if not amount or amount < 0: abort(400, "Invalid amount of marseybux.") + if v.procoins < amount: abort(400, "You don't have enough marseybux") + if amount < 100: abort(400, "You have to gift at least 100 marseybux.") v.procoins -= amount @@ -331,7 +330,7 @@ def transfer_bux(v, username): g.db.add(v) return {"message": f"{amount} marseybux have been transferred to @{receiver.username}"}, 200 - return {"message": "You can't transfer marseybux to yourself!"}, 400 + abort(400, "You can't transfer marseybux to yourslef!") @app.get("/leaderboard") @@ -500,16 +499,16 @@ def message2(v, username): user = get_user(username, v=v, include_blocks=True, include_shadowbanned=False) if hasattr(user, 'is_blocking') and user.is_blocking: - return {"error": "You're blocking this user."}, 403 + abort(403, "You're blocking this user.") if v.admin_level <= PERMS['MESSAGE_BLOCKED_USERS'] and hasattr(user, 'is_blocked') and user.is_blocked: - return {"error": "This user is blocking you."}, 403 + abort(403, "This user is blocking you.") message = request.values.get("message", "").strip()[:10000].strip() - if not message: return {"error": "Message is empty!"}, 400 + if not message: abort(400, "Message is empty!") - if 'linkedin.com' in message: return {"error": "This domain 'linkedin.com' is banned."}, 403 + if 'linkedin.com' in message: abort(403, "This domain 'linkedin.com' is banned.") body_html = sanitize(message) @@ -519,7 +518,7 @@ def message2(v, username): Comment.body_html == body_html, ).first() - if existing: return {"error": "Message already exists."}, 403 + if existing: abort(403, "Message already exists.") c = Comment(author_id=v.id, parent_submission=None, @@ -573,18 +572,18 @@ def messagereply(v): body = request.values.get("body", "").strip().replace('‎','') body = body.replace('\r\n', '\n')[:COMMENT_BODY_LENGTH_LIMIT] - if not body and not request.files.get("file"): return {"error": "Message is empty!"}, 400 + if not body and not request.files.get("file"): abort(400, "Message is empty!") - if 'linkedin.com' in body: return {"error": "this domain 'linkedin.com' is banned"}, 400 + if 'linkedin.com' in body: abort(400, "this domain 'linkedin.com' is banned") id = int(request.values.get("parent_id")) parent = get_comment(id, v=v) user_id = parent.author.id if v.is_suspended_permanently and parent.sentto != 2: - return {"error": "You are permabanned and may not reply to messages."}, 400 + abort(400, "You are permabanned and may not reply to messages.") elif v.is_muted and parent.sentto == 2: - return {"error": "You are forbidden from replying to modmail."}, 400 + abort(400, "You are forbidden from replying to modmail.") if parent.sentto == 2: user_id = None elif v.id == user_id: user_id = parent.sentto @@ -791,14 +790,14 @@ def u_username(username, v=None): if u.is_private and (not v or (v.id != u.id and v.admin_level < PERMS['VIEW_PRIVATE_PROFILES'] and not v.eye)): if request.headers.get("Authorization") or request.headers.get("xhr") or request.path.endswith(".json"): - return {"error": "This userpage is private"}, 403 + abort(403, "This userpage is private") return render_template("userpage_private.html", u=u, v=v) if v and hasattr(u, 'is_blocking') and u.is_blocking: if request.headers.get("Authorization") or request.headers.get("xhr") or request.path.endswith(".json"): - return {"error": f"You are blocking @{u.username}."}, 403 + abort(403, f"You are blocking @{u.username}.") return render_template("userpage_blocking.html", u=u, v=v) @@ -877,12 +876,12 @@ def u_username_comments(username, v=None): if u.is_private and (not v or (v.id != u.id and v.admin_level < PERMS['VIEW_PRIVATE_PROFILES'] and not v.eye)): if request.headers.get("Authorization") or request.headers.get("xhr") or request.path.endswith(".json"): - return {"error": "This userpage is private"}, 403 + abort(403, "This userpage is private") return render_template("userpage_private.html", u=u, v=v) if v and hasattr(u, 'is_blocking') and u.is_blocking: if request.headers.get("Authorization") or request.headers.get("xhr") or request.path.endswith(".json"): - return {"error": f"You are blocking @{u.username}."}, 403 + abort(403, f"You are blocking @{u.username}.") return render_template("userpage_blocking.html", u=u, v=v) try: page = max(int(request.values.get("page", "1")), 1) @@ -933,9 +932,9 @@ def u_username_info(username, v=None): user=get_user(username, v=v, include_blocks=True, include_shadowbanned=False) if hasattr(user, 'is_blocking') and user.is_blocking: - return {"error": "You're blocking this user."}, 401 + abort(401, "You're blocking this user.") elif hasattr(user, 'is_blocked') and user.is_blocked: - return {"error": "This user is blocking you."}, 403 + abort(403, "This user is blocking you.") return user.json @@ -946,9 +945,9 @@ def u_user_id_info(id, v=None): user=get_account(id, v=v, include_blocks=True, include_shadowbanned=False) if hasattr(user, 'is_blocking') and user.is_blocking: - return {"error": "You're blocking this user."}, 401 + abort(403, "You're blocking this user.") elif hasattr(user, 'is_blocked') and user.is_blocked: - return {"error": "This user is blocking you."}, 403 + abort(403, "This user is blocking you.") return user.json @@ -961,10 +960,10 @@ def follow_user(username, v): target = get_user(username, v=v, include_shadowbanned=False) if target.id==v.id: - return {"error": "You can't follow yourself!"}, 400 + abort(400, "You can't follow yourself!") if target.is_nofollow: - return {"error": "This user has disallowed other users from following them!"}, 403 + abort(403, "This user has disallowed other users from following them!") if g.db.query(Follow).filter_by(user_id=v.id, target_id=target.id).one_or_none(): return {"message": f"@{target.username} has been followed!"} @@ -993,7 +992,7 @@ def unfollow_user(username, v): if target.fish: if not v.shadowbanned: send_notification(target.id, f"@{v.username} has tried to unfollow you and failed because of your fish award!") - return {"error": "You can't unfollow this user!"}, 400 + abort(400, "You can't unfollow this user!") follow = g.db.query(Follow).filter_by(user_id=v.id, target_id=target.id).one_or_none() @@ -1223,15 +1222,15 @@ kofi_tiers={ @auth_required def settings_kofi(v): if not (v.email and v.is_activated): - return {"error": f"You must have a verified email to verify {patron} status and claim your rewards!"}, 400 + abort(400, f"You must have a verified email to verify {patron} status and claim your rewards!") transaction = g.db.query(Transaction).filter_by(email=v.email).order_by(Transaction.created_utc.desc()).first() if not transaction: - return {"error": "Email not found"}, 404 + abort(404, "Email not found") if transaction.claimed: - return {"error": f"{patron} rewards already claimed"}, 400 + abort(400, f"{patron} rewards already claimed") tier = kofi_tiers[transaction.amount] diff --git a/files/routes/votes.py b/files/routes/votes.py index f98204a9d..3d4a897b8 100644 --- a/files/routes/votes.py +++ b/files/routes/votes.py @@ -49,7 +49,7 @@ def vote_info_get(v, link): @is_not_permabanned def vote_post(post_id, new, v): - if new == "-1" and DISABLE_DOWNVOTES: return {"error": "forbidden."}, 403 + if new == "-1" and DISABLE_DOWNVOTES: abort(403) if new not in ["-1", "0", "1"]: abort(400) @@ -126,7 +126,7 @@ def vote_post(post_id, new, v): @is_not_permabanned def vote_comment(comment_id, new, v): - if new == "-1" and DISABLE_DOWNVOTES: return {"error": "forbidden."}, 403 + if new == "-1" and DISABLE_DOWNVOTES: abort(403, "forbidden.") if new not in ["-1", "0", "1"]: abort(400) From 39e49a508f2bfeb1f25c4d766ae4c05b821bd67f Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 07:51:14 -0700 Subject: [PATCH 08/12] add 409 to errors * also move check for AUTOJANNY_ID to before has_blocked --- files/routes/errors.py | 5 +++++ files/routes/reporting.py | 6 ++---- files/routes/settings.py | 17 ++++------------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/files/routes/errors.py b/files/routes/errors.py index c6db0855b..216371d66 100644 --- a/files/routes/errors.py +++ b/files/routes/errors.py @@ -16,6 +16,7 @@ WERKZEUG_ERROR_DESCRIPTIONS = { 404: "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.", 405: "The method is not allowed for the requested URL.", 406: "The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.", + 409: "A conflict happened while processing the request. The resource might have been modified while the request was being processed.", 413: "The data value transmitted exceeds the capacity limit.", 414: "The length of the requested URL exceeds the capacity limit for this server. The request cannot be processed.", 415: "The server does not support the media type transmitted in the request.", @@ -32,6 +33,7 @@ ERROR_TITLES = { 404: "Not Found", 405: "Method Not Allowed", 406: "Too Many Pings", + 409: "Conflict", 413: "Max image/audio size is 8 MB (16 MB for paypigs)", 414: "Max video size is 32 MB (64 MB for paypigs)", 415: "Unsupported Media Type", @@ -48,6 +50,7 @@ ERROR_MSGS = { 404: "Someone typed something wrong and it was probably you, please do better.", 405: "idk how anyone gets this error but if you see this, remember to follow @carpathianflorist
the original error text here talked about internet gremlins and wtf", 406: "Max limit is 5 for comments and 50 for posts", + 409: "There's a conflict between what you're trying to do and what you or someone else has done and because of that you can't do what you're trying to do, sorry not sorry", 413: "Max image/audio size is 8 MB (16 MB for paypigs)", 414: "Max video size is 32 MB (64 MB for paypigs)", 415: "Please upload only Image, Video, or Audio files!", @@ -68,6 +71,7 @@ ERROR_MARSEYS = { 404: "marseyconfused", 405: "marseyretard", 406: "marseyrage", + 409: "marseynoyou", 413: "marseyretard", 414: "marseychonker2", 415: "marseydetective", @@ -81,6 +85,7 @@ ERROR_MARSEYS = { @app.errorhandler(404) @app.errorhandler(405) @app.errorhandler(406) +@app.errorhandler(409) @app.errorhandler(413) @app.errorhandler(414) @app.errorhandler(415) diff --git a/files/routes/reporting.py b/files/routes/reporting.py index da9e83a6f..dc7d2baa3 100644 --- a/files/routes/reporting.py +++ b/files/routes/reporting.py @@ -102,8 +102,7 @@ def flag_post(pid, v): return {"message": f"Post moved to /h/{post.sub}"} else: existing = g.db.query(Flag.post_id).filter_by(user_id=v.id, post_id=post.id).one_or_none() - if existing: - return {"error": "You already reported this post!"}, 409 + if existing: abort(409, "You already reported this post!") flag = Flag(post_id=post.id, user_id=v.id, reason=reason) g.db.add(flag) @@ -120,8 +119,7 @@ def flag_comment(cid, v): comment = get_comment(cid) existing = g.db.query(CommentFlag.comment_id).filter_by(user_id=v.id, comment_id=comment.id).one_or_none() - if existing: - return {"error": "You already reported this comment!"}, 409 + if existing: abort(409, "You already reported this comment!") reason = request.values.get("reason", "").strip() diff --git a/files/routes/settings.py b/files/routes/settings.py index f1781d483..1ff8619a9 100644 --- a/files/routes/settings.py +++ b/files/routes/settings.py @@ -630,14 +630,9 @@ def settings_block_user(v): send_notification(user.id, f"@{v.username} has tried to block you and failed because of your unblockable status!") abort(403, "This user is unblockable.") - if user.id == v.id: - return {"error": "You can't block yourself."}, 409 - - if v.has_blocked(user): - return {"error": f"You have already blocked @{user.username}."}, 409 - - if user.id == AUTOJANNY_ID: - return {"error": "You can't block this user."}, 409 + if user.id == v.id: abort(400, "You can't block yourself") + if user.id == AUTOJANNY_ID: abort(403, "You can't block this user") + if v.has_blocked(user): abort(409, f"You have already blocked @{user.username}") new_block = UserBlock(user_id=v.id, target_id=user.id, @@ -658,13 +653,9 @@ def settings_block_user(v): @limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{SITE}-{session.get("lo_user")}') @auth_required def settings_unblock_user(v): - user = get_user(request.values.get("username")) - x = v.has_blocked(user) - - if not x: abort(409) - + if not x: abort(409, "You can't unblock someone you haven't blocked") g.db.delete(x) if not v.shadowbanned and user.admin_level >= PERMS['USER_BLOCKS_VISIBLE']: From 2004278791a41f55338c1be7ee947c9b955360e4 Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 07:58:52 -0700 Subject: [PATCH 09/12] syntax error fix --- files/routes/search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/routes/search.py b/files/routes/search.py index b9336e099..addfb9c37 100644 --- a/files/routes/search.py +++ b/files/routes/search.py @@ -193,7 +193,7 @@ def searchcomments(v): if 'post' in criteria: try: post = int(criteria['post']) - except: abort(404, f"Post with id {post} does not exist."}) + except: abort(404, f"Post with id {post} does not exist.") comments = comments.filter(Comment.parent_submission == post) From eebef17e8d9c2460a842ba4efdda6c4d74c1e444 Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 08:00:24 -0700 Subject: [PATCH 10/12] another dumb syntax error --- files/routes/asset_submissions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/routes/asset_submissions.py b/files/routes/asset_submissions.py index 8556724b8..f10b8e457 100644 --- a/files/routes/asset_submissions.py +++ b/files/routes/asset_submissions.py @@ -257,7 +257,7 @@ if SITE not in ('pcmemes.net', 'watchpeopledie.co'): @admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_HATS']) def approve_hat(v, name): if AEVANN_ID and v.id not in (AEVANN_ID, CARP_ID, SNAKES_ID): - abort(403, "Only Carp can approve hats!"}, 403 + abort(403, "Only Carp can approve hats!") name = name.strip() From 3a5c90e6d3ce119706aca0479a97f9ddf0b3f7c8 Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 08:02:30 -0700 Subject: [PATCH 11/12] change 409 msg --- files/routes/errors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/routes/errors.py b/files/routes/errors.py index 216371d66..3b07b5501 100644 --- a/files/routes/errors.py +++ b/files/routes/errors.py @@ -50,7 +50,7 @@ ERROR_MSGS = { 404: "Someone typed something wrong and it was probably you, please do better.", 405: "idk how anyone gets this error but if you see this, remember to follow @carpathianflorist
the original error text here talked about internet gremlins and wtf", 406: "Max limit is 5 for comments and 50 for posts", - 409: "There's a conflict between what you're trying to do and what you or someone else has done and because of that you can't do what you're trying to do, sorry not sorry", + 409: "There's a conflict between what you're trying to do and what you or someone else has done and because of that you can't do what you're trying to do. So maybe like... don't try and do that? Sorry not sorry", 413: "Max image/audio size is 8 MB (16 MB for paypigs)", 414: "Max video size is 32 MB (64 MB for paypigs)", 415: "Please upload only Image, Video, or Audio files!", From 63215b8888728aba389975a1b50876f297440dec Mon Sep 17 00:00:00 2001 From: justcool393 Date: Tue, 11 Oct 2022 08:26:20 -0700 Subject: [PATCH 12/12] move messages to constant file and also remove special logic for PCM, instead opting to modify the constant instead --- files/helpers/const.py | 69 ++++++++++++++++++++++++++++++++++++ files/routes/errors.py | 80 +++--------------------------------------- 2 files changed, 73 insertions(+), 76 deletions(-) diff --git a/files/helpers/const.py b/files/helpers/const.py index 59b72bd7d..79975c89b 100644 --- a/files/helpers/const.py +++ b/files/helpers/const.py @@ -257,6 +257,73 @@ FEATURES = { 'PATRON_ICONS': False, } +WERKZEUG_ERROR_DESCRIPTIONS = { + 400: "The browser (or proxy) sent a request that this server could not understand.", + 401: "The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad password), or your browser doesn't understand how to supply the credentials required.", + 403: "You don't have the permission to access the requested resource. It is either read-protected or not readable by the server.", + 404: "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.", + 405: "The method is not allowed for the requested URL.", + 406: "The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.", + 409: "A conflict happened while processing the request. The resource might have been modified while the request was being processed.", + 413: "The data value transmitted exceeds the capacity limit.", + 414: "The length of the requested URL exceeds the capacity limit for this server. The request cannot be processed.", + 415: "The server does not support the media type transmitted in the request.", + 417: "The server could not meet the requirements of the Expect header", + 418: "This server is a teapot, not a coffee machine", + 429: "This user has exceeded an allotted request count. Try again later.", + 500: "The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.", +} + +ERROR_TITLES = { + 400: "Bad Request", + 401: "Unauthorized", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Too Many Pings", + 409: "Conflict", + 413: "Max image/audio size is 8 MB (16 MB for paypigs)", + 414: "Max video size is 32 MB (64 MB for paypigs)", + 415: "Unsupported Media Type", + 417: "Image already exists!", + 418: "WEBM videos are not allowed", + 429: "Too Many Requests", + 500: "Internal Server Error", +} + +ERROR_MSGS = { + 400: "That request was bad and you should feel bad.", + 401: "What you're trying to do requires an account. I think. The original error message said something about a castle and I hated that.", + 403: "YOU AREN'T WELCOME HERE GO AWAY", + 404: "Someone typed something wrong and it was probably you, please do better.", + 405: "idk how anyone gets this error but if you see this, remember to follow @carpathianflorist
the original error text here talked about internet gremlins and wtf", + 406: "Max limit is 5 for comments and 50 for posts", + 409: "There's a conflict between what you're trying to do and what you or someone else has done and because of that you can't do what you're trying to do. So maybe like... don't try and do that? Sorry not sorry", + 413: "Max image/audio size is 8 MB (16 MB for paypigs)", + 414: "Max video size is 32 MB (64 MB for paypigs)", + 415: "Please upload only Image, Video, or Audio files!", + 417: "Image already exists!", + 418: "Please convert your video to MP4 and re-upload it!", + 429: "go spam somewhere else nerd", + 500: "Hiiiii it's carp! I think this error means that there's a timeout error. And I think that means something took too long to load so it decided not to work at all. If you keep seeing this on the same page but not other pages, then something is probably wrong with that specific function. It may not be called a function, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily <3", +} + +ERROR_MARSEYS = { + 400: "marseybrainlet", + 401: "marseydead", + 403: "marseytroll", + 404: "marseyconfused", + 405: "marseyretard", + 406: "marseyrage", + 409: "marseynoyou", + 413: "marseyretard", + 414: "marseychonker2", + 415: "marseydetective", + 418: "marseytea", + 429: "marseyrentfree", + 500: "marseycarp3", +} + EMOJI_MARSEYS = True EMOJI_SRCS = ['files/assets/emojis.json'] @@ -389,6 +456,8 @@ elif SITE == 'pcmemes.net': PIN_LIMIT = 10 FEATURES['REPOST_DETECTION'] = False FEATURES['GAMBLING'] = False + ERROR_MSGS[500] = "Hiiiii it's nigger! I think this error means that there's a nigger error. And I think that means something took too long to load so it decided to be a nigger. If you keep seeing this on the same page but not other pages, then something its probably a niggerfaggot. It may not be called a nigger, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily <3" + ERROR_MARSEYS[500] = "wholesome" POST_RATE_LIMIT = '1/second;4/minute;20/hour;100/day' HOLE_COST = 2000 diff --git a/files/routes/errors.py b/files/routes/errors.py index 3b07b5501..4712fad93 100644 --- a/files/routes/errors.py +++ b/files/routes/errors.py @@ -6,79 +6,9 @@ from files.__main__ import app, limiter # If you're adding an error, go here: # https://github.com/pallets/werkzeug/blob/main/src/werkzeug/exceptions.py -# and copy the description for the error code you're adding so that the -# default error message doesn't show up on the message. Please be exact. - -WERKZEUG_ERROR_DESCRIPTIONS = { - 400: "The browser (or proxy) sent a request that this server could not understand.", - 401: "The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad password), or your browser doesn't understand how to supply the credentials required.", - 403: "You don't have the permission to access the requested resource. It is either read-protected or not readable by the server.", - 404: "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.", - 405: "The method is not allowed for the requested URL.", - 406: "The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.", - 409: "A conflict happened while processing the request. The resource might have been modified while the request was being processed.", - 413: "The data value transmitted exceeds the capacity limit.", - 414: "The length of the requested URL exceeds the capacity limit for this server. The request cannot be processed.", - 415: "The server does not support the media type transmitted in the request.", - 417: "The server could not meet the requirements of the Expect header", - 418: "This server is a teapot, not a coffee machine", - 429: "This user has exceeded an allotted request count. Try again later.", - 500: "The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.", -} - -ERROR_TITLES = { - 400: "Bad Request", - 401: "Unauthorized", - 403: "Forbidden", - 404: "Not Found", - 405: "Method Not Allowed", - 406: "Too Many Pings", - 409: "Conflict", - 413: "Max image/audio size is 8 MB (16 MB for paypigs)", - 414: "Max video size is 32 MB (64 MB for paypigs)", - 415: "Unsupported Media Type", - 417: "Image already exists!", - 418: "WEBM videos are not allowed", - 429: "Too Many Requests", - 500: "Internal Server Error", -} - -ERROR_MSGS = { - 400: "That request was bad and you should feel bad.", - 401: "What you're trying to do requires an account. I think. The original error message said something about a castle and I hated that.", - 403: "YOU AREN'T WELCOME HERE GO AWAY", - 404: "Someone typed something wrong and it was probably you, please do better.", - 405: "idk how anyone gets this error but if you see this, remember to follow @carpathianflorist
the original error text here talked about internet gremlins and wtf", - 406: "Max limit is 5 for comments and 50 for posts", - 409: "There's a conflict between what you're trying to do and what you or someone else has done and because of that you can't do what you're trying to do. So maybe like... don't try and do that? Sorry not sorry", - 413: "Max image/audio size is 8 MB (16 MB for paypigs)", - 414: "Max video size is 32 MB (64 MB for paypigs)", - 415: "Please upload only Image, Video, or Audio files!", - 417: "Image already exists!", - 418: "Please convert your video to MP4 and re-upload it!", - 429: "go spam somewhere else nerd", - 500: "Hiiiii it's carp! I think this error means that there's a timeout error. And I think that means something took too long to load so it decided not to work at all. If you keep seeing this on the same page but not other pages, then something is probably wrong with that specific function. It may not be called a function, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily <3", -} - -ERROR_MSGS_PCM = { - 500: "Hiiiii it's nigger! I think this error means that there's a nigger error. And I think that means something took too long to load so it decided to be a nigger. If you keep seeing this on the same page but not other pages, then something its probably a niggerfaggot. It may not be called a nigger, but that sounds right to me. Anyway, ping me and I'll whine to someone smarter to fix it. Don't bother them. Thanks ily <3" -} - -ERROR_MARSEYS = { - 400: "marseybrainlet", - 401: "marseydead", - 403: "marseytroll", - 404: "marseyconfused", - 405: "marseyretard", - 406: "marseyrage", - 409: "marseynoyou", - 413: "marseyretard", - 414: "marseychonker2", - 415: "marseydetective", - 418: "marseytea", - 429: "marseyrentfree", - 500: "marseycarp3", -} +# and copy the description for the error code you're adding and add it to +# the constant WERKZEUG_ERROR_DESCRIPTIONS so that the default error message +# doesn't show up on the message. Be exact or it won't work properly. @app.errorhandler(400) @app.errorhandler(403) @@ -96,14 +26,12 @@ def error(e): title = ERROR_TITLES.get(e.code, str(e.code)) msg = ERROR_MSGS.get(e.code, str(e.code)) details = e.description + if WERKZEUG_ERROR_DESCRIPTIONS.get(e.code, None) == details: details = None if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": title, "code": e.code, "description": msg, "details": details} img = ERROR_MARSEYS.get(e.code, 'marseyl') - if e.code == 500 and SITE_NAME == 'PCM': - msg = ERROR_MSGS_PCM.get(e.code, str(e.code)) - img = "wholesome" return render_template('errors/error.html', err=True, title=title, msg=msg, details=details, img=img), e.code @app.errorhandler(401)