From f38e020de413ea8ce898bee09f9675d647613af9 Mon Sep 17 00:00:00 2001 From: BlenderDumbass Date: Thu, 28 Nov 2024 05:30:15 +0200 Subject: [PATCH] Publishing from Web UI --- modules/Render.py | 366 +++++++++++++++++++++++++++++++++++++++++++++- modules/Run.py | 62 +++++++- 2 files changed, 421 insertions(+), 7 deletions(-) diff --git a/modules/Render.py b/modules/Render.py index d3538b1..fa9eb5f 100644 --- a/modules/Render.py +++ b/modules/Render.py @@ -3,6 +3,7 @@ import os import json +import time import random import hashlib import urllib.parse @@ -151,6 +152,7 @@ def accounts(): return accounts + def validate(cookie): Accounts = accounts() @@ -188,6 +190,31 @@ def rank(account, Accounts=None): return 1 + rank(Accounts[account].get("invited_by"), Accounts) +def editsIn(account, tab): + + # Determents whether the user + # can edit an article. + + Accounts = accounts() + + # If the user is not registered + # per cannot edit anything. + if account not in Accounts: + return False + + # If the user is the owner of the + # site, per can edit everything. + if rank(account, Accounts) == 0: + return True + + # Not all users can edit in all + # tabs. + user = Accounts[account] + if tab in user.get("editsIn", []): + return True + + return False + def articles(tab): folder = Set.Folder()+"/tabs/"+tab @@ -354,6 +381,8 @@ def MainPage(server): def ListPage(server, tab): + user = validate(server.cookie) + Tabs = tabs() Articles = articles(tab) config = Set.Load() @@ -376,6 +405,9 @@ def ListPage(server, tab): html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet")) + if editsIn(user.get("username", ""), tab): + html = html + Button("New Post", "/editor?tab="+tab, icon="new") + # Scroll thingie if len(Articles) > Buffer: if page > 0: @@ -456,8 +488,19 @@ def ArticlePage(server, url): html = html + '
' html = html + '
' + + # Edit button + + if editsIn(user.get("username"), tab) and moderates(user.get("username"), Articles.get(article, {}).get("author", "")): + + html = html + '


' + html = html +"

"+Articles.get(article, {}).get("title", article)+"

" + + # Page author author = Articles.get(article, {}).get("author", "") if author: @@ -578,6 +621,8 @@ def ArticlePage(server, url): def AccountPage(server, account): + user = validate(server.cookie) + config = Set.Load() Accounts = accounts() Tabs = tabs() @@ -641,8 +686,6 @@ def AccountPage(server, account): # It could be mastodon url and not handle. - # https://social.trom.tf/@morsmortium - try: if "/" in mastodon: mastodon = mastodon.replace("https://", "").replace("http://", "") @@ -692,6 +735,46 @@ def AccountPage(server, account): html = html + '
' + # Validating this account + + validates = [] + if user.get("username", "") != account and moderates(user.get("username", ""), account): + for tab in Tabs: + if editsIn(user.get("username", ""), tab): + validates.append(tab) + if validates: + html = html + '
' + html = html + "You can grant publication rights to this account.
" + + html = html + '
' + + html = html + '' + + for tab in validates: + checked = "" + if editsIn(account, tab): + checked = 'checked=""' + html = html + '
' + html = html + '' + Tabs[tab].get("title", tab) + html = html + '
' + + + html = html + """ + +
+ + + +
+ + """ + html = html + '
' + + + # Posts by this account html = html + '
' @@ -920,7 +1003,7 @@ def SettingsPage(server): - +
@@ -1033,6 +1116,112 @@ def SettingsPage(server): send(server, html, 200) +def EditorPage(server): + + user = validate(server.cookie) + + # Authorization check + if not user: + AccessDenied(server) + return + + config = Set.Load() + + Tabs = tabs() + f = Set.Folder() + + # Generating + html = head(title = "Editor", + description = "Editor", + config = config + ) + + html = html + Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet")) + + tab = server.parsed.get("tab", [""])[0] + name = server.parsed.get("name", [""])[0] + + if tab in Tabs: + Articles = articles(tab) + else: + Articles = {} + + url = tab+"/"+name + article = Articles.get(name, {}) + + if article: + try: + text = open(f+"/tabs/"+tab+"/"+name+"/text.md") + text = text.read() + except: + text = "" + else: + text = "" + + html = html + """ + +
+ +
+ + + + """ + if tab in Tabs: + html = html + ' ' + html = html + Tabs.get(tab, {}).get("title", tab) + html = html + '
' + html = html + '' + + else: + html = html + """ + + + """ + + if name: + html = html + '' + + html = html + """ + + + + + + + + + + + """ + for l in Licenses: + html = html + '' + html = html + """ + + + + + + +
+
+ +
+ +
+ + + +
+ + """ + + html = html + LoginButton(server) + + send(server, html, 200) ### @@ -1623,7 +1812,82 @@ def UpdateAccount(server): json.dump(user, save, indent=4) Redirect(server, "/settings") + +def UpdatePublicationRights(server): + + user = validate(server.cookie) + + # Authorization check + if not user: + AccessDenied(server) + return + + Accounts = accounts() + + account = server.parsed.get("account", [""])[0] + + if account not in Accounts: + NotFound(server) + return + + if not moderates(user.get("username", ""), account): + AccessDenied(server) + return + + Tabs = tabs() + Account = Accounts[account] + if "editsIn" not in Account: + Account["editsIn"] = [] + + granted = [] + revoked = [] + for tab in Tabs: + + if not editsIn(user.get("username", ""), tab): + AccessDenied(server) + return + + tabOn = server.parsed.get(tab, [""])[0] + if tabOn and tab not in Account["editsIn"]: + Account["editsIn"].append(tab) + granted.append(tab) + + elif not tabOn and tab in Account["editsIn"]: + Account["editsIn"].remove(tab) + revoked.append(tab) + + f = Set.Folder() + folder = f+"/accounts" + with open(folder+"/"+account+".json", "w") as save: + json.dump(Account, save, indent=4) + + Redirect(server, "/account/"+account) + + # Notification + + text = "@"+user.get("username", "")+" " + if granted: + text = text + "granted you publication rights in: " + for n, i in enumerate(granted): + text = text + i + if n != len(granted)-1: + text = text + ", " + + if revoked: + text = text + " and " + + if revoked: + text = text + "revoked your publication rights in: " + for n, i in enumerate(revoked): + text = text + i + if n != len(revoked)-1: + text = text + ", " + + text = text + ".
" + + Notify(account, "/account/"+account, text) + def DoComment(server): user = validate(server.cookie) @@ -1744,6 +2008,102 @@ def DoComment(server): url+placeRedirect+str(number), "@"+Safe(username)+" mentioned you:

"+article.get("title", "")+"

"+Safe(text[:200])+"") +def Publish(server): + + user = validate(server.cookie) + + # Authorization check + if not user: + AccessDenied(server) + return + + config = Set.Load() + Tabs = tabs() + + f = Set.Folder() + + # Getting data to create + tab = server.parsed.get("tab", [""])[0] + name = server.parsed.get("name", [""])[0] + text = server.parsed.get("text", [""])[0] + title = server.parsed.get("title", [""])[0] + description = server.parsed.get("description", [""])[0] + thumbnail = server.parsed.get("thumbnail", [""])[0] + License = server.parsed.get("license", [""])[0] + recording = server.parsed.get("recording", [""])[0] + + # If this tab doesn't exist, this is an error. + if tab not in Tabs: + AccessDenied(server) + return + + # Checking if the user has rights to post in here. + if not editsIn(user.get("username"), tab): + AccessDenied(server) + return + + Articles = articles(tab) + + if not name: + name = Simplify(title) + + # Reading the file + + if name in Articles: + metadata = Articles[name] + + else: + metadata = { + "title":"", + "timestamp":time.time(), + "description":"", + "author":user.get("username", ""), + "thumbnail":"", + "license":"", + "views":{ + "amount":0, + "viewers":[], + "dates":{} + }, + "recording":"", + "comments":{ + "comments":[], + "requests":[] + } + } + + # Checking if the user can edit the posts of the + # author of this article. + + if not moderates(user.get("username"), metadata.get("author", "")): + AccessDenied(server) + return + + metadata["title"] = title + metadata["description"] = description + metadata["license"] = License + metadata["recording"] = recording + metadata["thumbnail"] = thumbnail + + # Save the changes + + try: + os.makedirs(f+"/tabs/"+tab+"/"+name) + except:pass + + with open(f+"/tabs/"+tab+"/"+name+"/metadata.json", "w") as save: + json.dump(metadata, save, indent=4) + + with open(f+"/tabs/"+tab+"/"+name+"/text.md", "w") as save: + + # Enabling HTML embedding only for the owner + if rank(user.get("username", "")) == 0: + save.write(text) + else: + save.write(Safe(text)) + + Redirect(server, "/"+tab+"/"+name) + def DeleteComment(server): diff --git a/modules/Run.py b/modules/Run.py index b6d7a64..9beb367 100644 --- a/modules/Run.py +++ b/modules/Run.py @@ -28,7 +28,6 @@ class handler(BaseHTTPRequestHandler): q = clr["tbbl"] b = clr["tbbl"] - #print(dir(self)) try: if self.newview: @@ -57,15 +56,64 @@ class handler(BaseHTTPRequestHandler): logfile = open(filename, "ab") logfile.write((toprint+"\n").encode('utf-8')) logfile.close() - + + def do_POST(self): + + self.path = self.path.replace("/..", "/") + self.path = self.path.replace("%27", "'") + + self.cookie = self.headers.get("Cookie") + self.newview = False + if self.cookie: self.cookie = self.cookie[-50:] + + # Failing early to make sure that nobody will + # try attacking this part of the server. + + commands = ["do_edit"] + found = False + for i in commands: + if i in self.path: + found = True + break + if not found: + Render.AccessDenied(self) + return + + + # Not allowing more than 1 MB for the sake of + # network. A full book of Sheiny The Hacker + # which was posted on blenderdumbass.org is + # only about 160 KB. 1MB should be plenty. + + length = int(self.headers["Content-Length"]) + if length < 1000000: + try: + text = self.rfile.read(length).decode("utf-8") + except: + Render.AccessDenied(self) + return + + else: + Render.AccessDenied(self) + return + + #parsed_url = urllib.parse.urlparse(text) + self.parsed = urllib.parse.parse_qs(text) + + if self.path[1:].startswith("do_edit"): + Render.Publish(self) + + else: + Render.NotFound(self) + def do_GET(self): self.path = self.path.replace("/..", "/") self.path = self.path.replace("%27", "'") parsed_url = urllib.parse.urlparse(self.path) - self.parsed = urllib.parse.parse_qs(parsed_url.query) + self.cookie = self.headers.get("Cookie") if self.cookie: self.cookie = self.cookie[-50:] @@ -100,9 +148,12 @@ class handler(BaseHTTPRequestHandler): elif self.path[1:].startswith("login"): Render.LoginPage(self) + elif self.path[1:].startswith("editor"): + Render.EditorPage(self) + elif self.path[1:].startswith("register"): Render.RegisterPage(self) - + elif self.path[1:].startswith("settings"): Render.SettingsPage(self) @@ -115,6 +166,9 @@ class handler(BaseHTTPRequestHandler): elif self.path[1:].startswith("update_account"): Render.UpdateAccount(self) + elif self.path[1:].startswith("grant_publication_rights"): + Render.UpdatePublicationRights(self) + elif self.path[1:].startswith("create_invite"): Render.CreateInvite(self)