diff --git a/modules/Common.py b/modules/Common.py index 3c17fc1..2a4f1ac 100644 --- a/modules/Common.py +++ b/modules/Common.py @@ -82,7 +82,7 @@ def RandString(n=50): return s -IDColors = {} +IDColors = {" ANONYMOUS ":clr["norm"]} def consoleForm(obj): diff --git a/modules/Email.py b/modules/Email.py new file mode 100644 index 0000000..f5b3e9a --- /dev/null +++ b/modules/Email.py @@ -0,0 +1,163 @@ +import smtplib +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart + +import json + +from modules import Set +from modules.Common import * + +from modules import Render + +verifying = {} + +def Send(reciever, subject, html): + + config = Set.Load() + Email = config.get("email") + + if not Email: + print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" No email credentials!") + + try: port = int(Email.get("port", "")) + except: port = 587 + + message = MIMEMultipart() + message["From"] = Email["visible"] + message["To"] = reciever + message["Subject"] = subject + + message.attach(MIMEText(html, "html")) + + # Send the email + with smtplib.SMTP(Email["server"], port) as server: + + server.starttls() + + server.login(Email["sender"], + Email["password"]) + + server.sendmail(Email["sender"], + reciever, + message.as_string()) + + print('Email "'+subject+'" was sent to', reciever) + +def VerifyPage(server): + + user = Render.validate(server.cookie) + + # Authorization check + if not user: + Render.AccessDenied(server) + return + + config = Set.Load() + + # Generating + html = Render.head(title = "Verify Email", + description = "Verify Email", + config = config + ) + + html = html + Render.Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet")) + + code = server.parsed.get("code", [""])[0] + if not code: + + + + html = html + """ + +
+ +
+ +
+ +
+
+ You have recieved an email with a code.

+ It could be in spam. +

+ +
+ + +
+ +
+ +
+
+ + """ + code = RandString(10) + verifying[code] = user.get("email") + + text = """ +
+ Your Verification Code +
+
"""+code+""" +
+
+ """ + + text = Format(text) + + Send(user.get("email", ""), "Verification Code", text) + + Render.send(server, html, 200) + + else: + + # If we recieved the code. + + if code not in verifying: + print("code not in verifying") + Render.AccessDenied(server) + return + + + if verifying[code] != user.get("email"): + print("email wrong") + Render.AccessDenied(server) + return + + user["email_verified"] = True + + f = Set.Folder() + folder = f+"/accounts" + with open(folder+"/"+user.get("username", "")+".json", "w") as save: + json.dump(user, save, indent=4) + + Render.Redirect(server, "/settings#email") + +def Format(text): + + config = Set.Load() + + favicon = config.get("favicon", "") + if favicon.startswith("/"): + favicon = "https://"+config.get("domain", "")+favicon + + css = open(config.get("css", "default.css"), "r") + html = '' + html = html + '
' + + if favicon: + html = html + '[favicon]' + + html = html + " "+config.get("title", "My Website") + html = html + "

" + html = html + '
' + html = html + text + html = html + '
' + + return html + + diff --git a/modules/Help.py b/modules/Help.py index 3afe605..d7d717a 100644 --- a/modules/Help.py +++ b/modules/Help.py @@ -33,6 +33,7 @@ def Set(): print(clr["tdyl"]+"--tagline"+clr["norm"]+" - Set tagline of the website.") print(clr["tdyl"]+"--domain"+clr["norm"]+" - Let the server know the clearnet domain.") print(clr["tdyl"]+"--tor"+clr["norm"]+" - Let the server know the tor domain.") + print(clr["tdyl"]+"--email"+clr["norm"]+" - Set Email SMTP account for internal automatic messages.") print(clr["tdyl"]+"--port"+clr["norm"]+" - Set port where to run the website.") print(clr["tdyl"]+"--css"+clr["norm"]+" - Set a CSS file.") print(clr["tdyl"]+"--css_edit"+clr["norm"]+" - edit a CSS file.") diff --git a/modules/Render.py b/modules/Render.py index f38c629..8307b14 100644 --- a/modules/Render.py +++ b/modules/Render.py @@ -747,6 +747,8 @@ def AccountPage(server, account): html = html + '
' html = html + '' html = html + ' '+email+'' + if Accounts.get(account, {}).get("email_verified"): + html = html + '' html = html + '
' # Mastodon @@ -921,6 +923,13 @@ def LoginPage(server):

+ +
+ + +
+ +

""" if redirect: @@ -1110,9 +1119,44 @@ def SettingsPage(server): - + """ + if user.get("email"): + + if not user.get("email_verified"): + html = html + """ +
+

Email Settings

+ +
+ Email """+Safe(user.get("email"))+""" is not verified. +

+ """+Button("Verify", "/email_verify", "ok")+""" + +
+
+ + """ + else: + html = html + """ + +
+ +
+ +

Email Settings

+ +
+ Email """+Safe(user.get("email"))+""" is verified! +

+
+ +
+
+ + """ + # Current Logged in Sessions sessions = user.get("sessions", {}) @@ -1173,6 +1217,26 @@ def SettingsPage(server): + +
+ +

Change Password

+ +
+ + + + + + + + + +
+ """ @@ -1809,6 +1873,7 @@ def Login(server): hashed = hashlib.sha512(password.encode("utf-8")).hexdigest() redirect = server.parsed.get("redirect" , [""])[0] + logout = server.parsed.get("logout" , [""])[0] Accounts = accounts() @@ -1825,11 +1890,10 @@ def Login(server): account["sessions"] = {} account["sessions"][server.cookie] = server.headers.get("User-Agent") + f = Set.Folder() folder = f+"/accounts" - with open(folder+"/"+username+".json", "w") as save: - json.dump(account, save, indent=4) - + # Move the cookie arround # When a login happens, we want to make the server know # which articles the person already read and stuff. So @@ -1859,8 +1923,17 @@ def Login(server): with open(f+"/tabs"+article.get("url")+"/metadata.json", "w") as save: json.dump(article, save, indent=4) - + + if logout: + for cookie in list(account["sessions"].keys()): + if cookie != server.cookie: + del account["sessions"][cookie] + + + with open(folder+"/"+username+".json", "w") as save: + json.dump(account, save, indent=4) + if not redirect: Redirect(server, "/settings") else: @@ -1992,6 +2065,11 @@ def UpdateAccount(server): for key in keys: data = server.parsed.get(key, [""])[0] + + # Making sure to reverify email. + if key == "email" and user[key] != data: + user["email_verified"] = False + user[key] = Safe(data) f = Set.Folder() @@ -2001,6 +2079,31 @@ def UpdateAccount(server): Redirect(server, "/settings") +def UpdatePassword(server): + + user = validate(server.cookie) + + old_password = server.parsed.get("password", [""])[0] + new_password = server.parsed.get("new_password", [""])[0] + + old_hashed = hashlib.sha512(old_password.encode("utf-8")).hexdigest() + new_hashed = hashlib.sha512(new_password.encode("utf-8")).hexdigest() + + # Validating the user's password + if user.get("password", "") == old_hashed: + + user["password"] = new_hashed + + f = Set.Folder() + folder = f+"/accounts" + with open(folder+"/"+user.get("username", "")+".json", "w") as save: + json.dump(user, save, indent=4) + + Redirect(server, "/settings") + + AccessDenied(server) + + def UpdatePublicationRights(server): user = validate(server.cookie) diff --git a/modules/Run.py b/modules/Run.py index bc66b1b..d3be2ff 100644 --- a/modules/Run.py +++ b/modules/Run.py @@ -69,7 +69,11 @@ class handler(BaseHTTPRequestHandler): # Failing early to make sure that nobody will # try attacking this part of the server. - commands = ["do_edit", "do_login", "do_register"] + commands = ["do_edit", + "do_login", + "do_register", + "change_password"] + found = False for i in commands: if i in self.path: @@ -106,6 +110,9 @@ class handler(BaseHTTPRequestHandler): elif self.path[1:].startswith("do_login"): Render.Login(self) + elif self.path[1:].startswith("change_password"): + Render.UpdatePassword(self) + elif self.path[1:].startswith("do_register"): Render.Register(self) @@ -164,6 +171,11 @@ class handler(BaseHTTPRequestHandler): elif self.path[1:].startswith("editor"): Render.EditorPage(self) + elif self.path[1:].startswith("email_verify"): + + from modules import Email + Email.VerifyPage(self) + elif self.path[1:].startswith("register"): Render.RegisterPage(self) diff --git a/modules/Set.py b/modules/Set.py index 68f8250..77d1df4 100644 --- a/modules/Set.py +++ b/modules/Set.py @@ -106,6 +106,10 @@ def Set(): print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Didn't specify the editor!") print('Use: $ python3 run.py --set --editor emacs') + if "--email" in sys.argv: + + Email() + if "--port" in sys.argv: try: @@ -281,3 +285,31 @@ def SetFavicon(filename): print("New favicon is set at "+clr["bold"]+Folder()+"/pictures/favicon.png"+clr["norm"]) +def Email(): + + print("Please make sure your email provider supports") + print("SMTP protocol.") + + server = input("SMTP server : ") + port = input("SMTP port : ") + username = input("Username : ") + password = input("Password : ") + sender = input("Sender Address : ") + visible = input("Visible Address: ") + + config = Load() + + email = { + "server":server, + "port":port, + "username":username, + "password":password, + "sender":sender, + "visible":visible + } + + config["email"] = email + + Save(config) + + print("Email credentials saved!")