Merge branch 'frost' into pr-eager-content
This commit is contained in:
commit
b4efd75753
59 changed files with 2193 additions and 581 deletions
|
@ -39,20 +39,19 @@ MAX_TITLE_LENGTH = 500
|
|||
MAX_URL_LENGTH = 2048
|
||||
MAX_BODY_LENGTH = 20000
|
||||
|
||||
# Get request value `val` and ensure it is within length constraints
|
||||
# : Returns an either tuple (good_value, error)
|
||||
# : TODO it may make sense to do more sanitisation here
|
||||
def guarded_value(val, min_len, max_len):
|
||||
|
||||
def guarded_value(val, min_len, max_len) -> str:
|
||||
'''
|
||||
Get request value `val` and ensure it is within length constraints
|
||||
Requires a request context and either aborts early or returns a good value
|
||||
'''
|
||||
raw = request.values.get(val, '').strip()
|
||||
raw = raw.replace('\u200e', '')
|
||||
|
||||
if len(raw) < min_len:
|
||||
return (None, ({"error": f"Minimum length for {val} is {min_len}"}, 403))
|
||||
|
||||
if len(raw) > max_len:
|
||||
return (None, ({"error": f"Maximum length for {val} is {max_len}"}, 403))
|
||||
|
||||
return (raw, None)
|
||||
if len(raw) < min_len: abort(400, f"Minimum length for {val} is {min_len}")
|
||||
if len(raw) > max_len: abort(400, f"Maximum length for {val} is {max_len}")
|
||||
# TODO: it may make sense to do more sanitisation here
|
||||
return raw
|
||||
|
||||
@app.post("/toggle_club/<pid>")
|
||||
@auth_required
|
||||
|
@ -131,7 +130,7 @@ def post_id(pid, anything=None, v=None, sub=None):
|
|||
post = get_post(pid, v=v)
|
||||
|
||||
if post.over_18 and not (v and v.over_18) and session.get('over_18', 0) < int(time.time()):
|
||||
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error":"Must be 18+ to view"}, 451
|
||||
if request.headers.get("Authorization") or request.headers.get("xhr"): abort(403, "Must be 18+ to view")
|
||||
return render_template("errors/nsfw.html", v=v)
|
||||
|
||||
if v: defaultsortingcomments = v.defaultsortingcomments
|
||||
|
@ -408,11 +407,8 @@ def edit_post(pid, v):
|
|||
|
||||
if p.author_id != v.id and not (v.admin_level > 1 and v.admin_level > 2): abort(403)
|
||||
|
||||
title, err = guarded_value("title", 1, MAX_TITLE_LENGTH)
|
||||
if err: return err
|
||||
|
||||
body, err = guarded_value("body", 0, MAX_BODY_LENGTH)
|
||||
if err: return err
|
||||
title = guarded_value("title", 1, MAX_TITLE_LENGTH)
|
||||
body = guarded_value("body", 0, MAX_BODY_LENGTH)
|
||||
|
||||
if title != p.title:
|
||||
p.title = title
|
||||
|
@ -434,24 +430,24 @@ def edit_post(pid, v):
|
|||
file.save("video.mp4")
|
||||
with open("video.mp4", 'rb') as f:
|
||||
try: req = requests.request("POST", "https://api.imgur.com/3/upload", headers={'Authorization': f'Client-ID {IMGUR_KEY}'}, files=[('video', f)], timeout=5).json()['data']
|
||||
except requests.Timeout: return {"error": "Video upload timed out, please try again!"}
|
||||
except requests.Timeout: abort(500, "Video upload timed out, please try again!")
|
||||
try: url = req['link']
|
||||
except:
|
||||
error = req['error']
|
||||
if error == 'File exceeds max duration': error += ' (60 seconds)'
|
||||
return {"error": error}, 400
|
||||
abort(400, error)
|
||||
if url.endswith('.'): url += 'mp4'
|
||||
if app.config['MULTIMEDIA_EMBEDDING_ENABLED']:
|
||||
body += f"\n\n"
|
||||
else:
|
||||
body += f'\n\n<a href="{url}">{url}</a>'
|
||||
else: return {"error": "Image/Video files only"}, 400
|
||||
else: abort(400, "Image/Video files only")
|
||||
|
||||
body_html = sanitize(body, edit=True)
|
||||
|
||||
p.body = body
|
||||
|
||||
if len(body_html) > 40000: return {"error":"Submission body_html too long! (max 40k characters)"}, 400
|
||||
if len(body_html) > 40000: abort(400, "Submission body_html too long! (max 40k characters)")
|
||||
|
||||
p.body_html = body_html
|
||||
|
||||
|
@ -678,19 +674,14 @@ def api_is_repost():
|
|||
def submit_post(v, sub=None):
|
||||
|
||||
def error(error):
|
||||
if request.headers.get("Authorization") or request.headers.get("xhr"): return {"error": error}, 403
|
||||
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
|
||||
|
||||
title, err = guarded_value("title", 1, MAX_TITLE_LENGTH)
|
||||
if err: return error(err[0]["error"])
|
||||
|
||||
url, err = guarded_value("url", 0, MAX_URL_LENGTH)
|
||||
if err: return error(err[0]["error"])
|
||||
|
||||
body, err = guarded_value("body", 0, MAX_BODY_LENGTH)
|
||||
if err: return error(err[0]["error"])
|
||||
title = guarded_value("title", 1, MAX_TITLE_LENGTH)
|
||||
url = guarded_value("url", 0, MAX_URL_LENGTH)
|
||||
body = guarded_value("body", 0, MAX_BODY_LENGTH)
|
||||
|
||||
sub = request.values.get("sub")
|
||||
if sub: sub = sub.replace('/h/','').replace('s/','')
|
||||
|
@ -1092,35 +1083,42 @@ def unsave_post(pid, v):
|
|||
@app.post("/pin/<post_id>")
|
||||
@auth_required
|
||||
def api_pin_post(post_id, v):
|
||||
post = get_post(post_id)
|
||||
if v.id != post.author_id: abort(403, "Only the post author's can do that!")
|
||||
post.is_pinned = not post.is_pinned
|
||||
g.db.add(post)
|
||||
|
||||
post = g.db.query(Submission).filter_by(id=post_id).one_or_none()
|
||||
if post:
|
||||
if v.id != post.author_id: return {"error": "Only the post author's can do that!"}
|
||||
post.is_pinned = not post.is_pinned
|
||||
g.db.add(post)
|
||||
|
||||
cache.delete_memoized(User.userpagelisting)
|
||||
|
||||
g.db.commit()
|
||||
if post.is_pinned: return {"message": "Post pinned!"}
|
||||
else: return {"message": "Post unpinned!"}
|
||||
return {"error": "Post not found!"}
|
||||
cache.delete_memoized(User.userpagelisting)
|
||||
|
||||
g.db.commit()
|
||||
if post.is_pinned: return {"message": "Post pinned!"}
|
||||
else: return {"message": "Post unpinned!"}
|
||||
|
||||
@app.get("/submit/title")
|
||||
@limiter.limit("6/minute")
|
||||
@auth_required
|
||||
def get_post_title(v):
|
||||
|
||||
POST_TITLE_TIMEOUT = 5
|
||||
url = request.values.get("url")
|
||||
if not url: abort(400)
|
||||
if not url or '\\' in url: abort(400)
|
||||
url = url.strip()
|
||||
if not url.startswith('http'): abort(400)
|
||||
checking_url = url.lower().split('?')[0].split('%3F')[0]
|
||||
if any((checking_url.endswith(f'.{x}') for x in NO_TITLE_EXTENSIONS)):
|
||||
abort(400)
|
||||
|
||||
try: x = requests.get(url, headers=titleheaders, timeout=5, proxies=proxies)
|
||||
try:
|
||||
x = gevent.with_timeout(POST_TITLE_TIMEOUT, requests.get,
|
||||
url, headers=titleheaders, timeout=POST_TITLE_TIMEOUT,
|
||||
proxies=proxies)
|
||||
except: abort(400)
|
||||
|
||||
content_type = x.headers.get("Content-Type")
|
||||
if not content_type or "text/html" not in content_type: abort(400)
|
||||
|
||||
soup = BeautifulSoup(x.content, 'lxml')
|
||||
match = html_title_regex.search(x.text)
|
||||
if match and match.lastindex >= 1:
|
||||
title = html.unescape(match.group(1))
|
||||
else: abort(400)
|
||||
|
||||
title = soup.find('title')
|
||||
if not title: abort(400)
|
||||
|
||||
return {"url": url, "title": title.string}
|
||||
return {"url": url, "title": title}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue