diff --git a/api.py b/api.py new file mode 100644 index 0000000000000000000000000000000000000000..e9d521ad371dc46f74fcdebaa03dd9e7ae6e18c1 --- /dev/null +++ b/api.py @@ -0,0 +1,77 @@ +from flask import jsonify, render_template +import sqlite3 + +def apiGet(request, url_scheme): + try: + userID = sAPI.dumps(s.loads(request.cookies.get('userID'))) + except: + userID = "" + + return render_template("apiDocs.html", apikey=userID, domain=request.headers['Host'], url_scheme=url_scheme) + +def apiPost(request, domain): + username = "error" + try: + username = sAPI.loads(request.form['apikey']) + except: + pass + + try: + short = request.form['short'] + except: + return jsonify( + status="1", + message="short link missing" + ) + + try: + longURL = request.form['long'] + except: + return jsonify( + status="2", + message="link to short is missing" + ) + + domain_ = short.split("/")[0] + if not domain_ in domain: + return jsonify( + status="3", + message="domain for short link is not in allowed domain list" + ) + + with sqlite3.connect('db/urls.db') as conn: #Check if another user already used the short link + cursor = conn.cursor() + res = cursor.execute('SELECT LONG_URL FROM WEB_URL WHERE SHORT_URL=?', [short]) + try: + short2 = res.fetchone() + already_used = False + if short2 is not None: + already_used = True + except: + pass + + if not already_used: #If short link wasn't used before, insert the link in the Database. + res = cursor.execute( + 'INSERT INTO WEB_URL (LONG_URL, SHORT_URL, USERNAME) VALUES (?, ?, ?)', + [longURL, short, username] + ) + + try: + request.form['qr'] + qr64 = "data:image/jpeg;base64," + makeQR(url_scheme + "://" + short) + + return jsonify( + status="0", + message="ok", + qr=qr64 + ) + except: + return jsonify( + status="0", + message="ok" + ) + else: + return jsonify( + status="4", + message="short url already in use" + ) \ No newline at end of file diff --git a/deletelink.py b/deletelink.py new file mode 100644 index 0000000000000000000000000000000000000000..9ee137a2f4861e1d19d4bd37b627e0efead91d68 --- /dev/null +++ b/deletelink.py @@ -0,0 +1,19 @@ +import sqlite3 +from flask import redirect, abort + +def deleteLink(request, s): + try: + userID = s.loads(request.cookies.get('userID')) #get the userid from the cookie + loginbar = "Hello " + s.loads(request.cookies.get('username')) + ' (<a href="/user/logout" style="color:white">logout</a>)' # generate the login form + loginbar = loginbar + "" #to hide the unused variable message + except: + return redirect("/user/login") # if user is not logged in redirect him to the login page + linkToDelete = request.args.get('link') #get the link, which the user want's to delete from the parameter in the url. + + with sqlite3.connect('db/urls.db') as conn: + cursor = conn.cursor() + try: + cursor.execute('DELETE FROM WEB_URL WHERE SHORT_URL=? AND USERNAME=?', [linkToDelete, userID]) #Delete the entrie + return redirect('/user/links') #redirect the user back to the table. + except: + abort(500) \ No newline at end of file diff --git a/githubcallback.py b/githubcallback.py new file mode 100644 index 0000000000000000000000000000000000000000..429107da8a1755ffa540b91ce5872796df4c84e5 --- /dev/null +++ b/githubcallback.py @@ -0,0 +1,22 @@ +from flask import make_response, redirect +from requests import post, get +import json #used for github oauth + +def githubCallback(request, GITHUB_CLIENT_SECRET, GITHUB_CLIENT_ID, s): + try: + code = request.args.get("code") + url = "https://github.com/login/oauth/access_token" #The baseurl + params = {'client_id': GITHUB_CLIENT_ID, 'client_secret': GITHUB_CLIENT_SECRET, 'code': code} #As paramtere we send the client id and the client secret which we get from github when registering an application and the user code from before + oauth_token = post(url,params).text.split("access_token=")[1].split("&")[0] #Send a post request with the parameters from + + headers = {'Authorization': 'token ' + oauth_token,} #Useragent doesn't matters, but is set here + githubResponse = get("https://api.github.com/user", headers=headers).text + userID = str(json.loads(githubResponse)['id']) + username = str(json.loads(githubResponse)['login']) + + resp = make_response(redirect('/')) #redirect the user at the end back to the main page + resp.set_cookie('userID', s.dumps("github_" + userID)) #set the cookies with username and userid + resp.set_cookie('username', s.dumps(username)) + return resp + except: + print("Authentication failed") \ No newline at end of file diff --git a/googlecallback.py b/googlecallback.py new file mode 100644 index 0000000000000000000000000000000000000000..0af7947865defebd958f203e89f8ec01fe2c52e3 --- /dev/null +++ b/googlecallback.py @@ -0,0 +1,20 @@ +from flask import make_response, redirect +from requests import post, get + +def googleCallback(request, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, url_scheme, domain, s): + try: + code = request.args.get("code") + url = "https://www.googleapis.com/oauth2/v4/token" #The baseurl + headers = {'Content-Type': 'application/x-www-form-urlencoded',} + params = {'client_id': GOOGLE_CLIENT_ID, 'client_secret': GOOGLE_CLIENT_SECRET, 'code': code, "grant_type": "authorization_code", "redirect_uri": url_scheme + "://" + domain[0] + "/user/google-callback"} #As paramtere we send the client id and the client secret which we get from github when registering an application and the user code from before + access_token = post(url,params, headers=headers).text.split('access_token": "')[1].split('"')[0] + authorization_header = {"Authorization": "OAuth %s" % access_token} + r = get("https://www.googleapis.com/oauth2/v2/userinfo", headers=authorization_header) + userID = r.text.split('"id": "')[1].split('"')[0] + name = r.text.split('"name": "')[1].split('"')[0] + resp = make_response(redirect('/')) #redirect the user at the end back to the main page + resp.set_cookie('userID', s.dumps("google_" + userID)) #set the cookies with username and userid + resp.set_cookie('username', s.dumps(name)) + return resp + except: + print("Authentication failed") \ No newline at end of file diff --git a/grecaptcha_verify.py b/grecaptcha_verify.py new file mode 100644 index 0000000000000000000000000000000000000000..55acc158a056c59de1043d633a6cf279fc4ea969 --- /dev/null +++ b/grecaptcha_verify.py @@ -0,0 +1,12 @@ +from requests import post + +def grecaptcha_verify(request, skipCaptcha, recaptchaPrivateKey): #This function is used to verify the google recaptcha code, that is send to the server after submitting a new link + if(skipCaptcha): return True #If recaptcha is disabled alwas return at this point true, which means response is verified + captcha_rs = request.form.get('g-recaptcha-response') + url = "https://www.google.com/recaptcha/api/siteverify" #The baseurl + headers = {'User-Agent': 'DebuguearApi-Browser',} #Useragent doesn't matters, but is set here + params = {'secret': recaptchaPrivateKey, 'response': captcha_rs} #As paramtere we send to google our private Key and the key from the user + verify_rs = post(url,params, headers=headers) #Send a post request with the parameters from before to googlde + verify_rs = verify_rs.json() + response = verify_rs.get("success", False) #Verify that the response includes the success, if so return True, if not return False + return response \ No newline at end of file diff --git a/home.py b/home.py new file mode 100644 index 0000000000000000000000000000000000000000..411b5b4195510fef64b39702155dfd593d59930f --- /dev/null +++ b/home.py @@ -0,0 +1,11 @@ +from flask import render_template, abort +def home(request, builddate, version, domain_prepared, recaptchaPublicKey, showDomainSelect, cookieNotice, domain_to_index, s): + try: + loginbar = "Hello " + s.loads(request.cookies.get('username')) + ' (<a href="/user/links" style="color:white">your links</a>, <a href="/user/logout" style="color:white">logout</a>)' + except: + loginbar = '<a href="#" onClick="showLogin()" style="color:white">login</a>' + + try: + return render_template('home.html', builddate=builddate, version=version, domain=domain_prepared, recaptchaPublicKey=recaptchaPublicKey, showDomainSelect=showDomainSelect, loginbar=loginbar, cookieNotice=cookieNotice ,domain_prefilled=domain_to_index[request.headers["host"]]) #return the default site to create a new shorten link + except: + abort(500) \ No newline at end of file diff --git a/login.py b/login.py new file mode 100644 index 0000000000000000000000000000000000000000..299dbe62155e7ee9dbf33044bd6223c2ffec3db3 --- /dev/null +++ b/login.py @@ -0,0 +1,7 @@ +from flask import redirect, render_template + +def login(request, GITHUB_CLIENT_ID, cookieNotice, GOOGLE_CLIENT_ID, url_scheme, domain): + service = request.args.get("service") + if(service == "github"): return redirect("https://github.com/login/oauth/authorize/?client_id=" + GITHUB_CLIENT_ID + "&scope=user") #redirect the user to the github login page and ask for access to user data (name, email, ...) + if(service == "google"): return redirect("https://accounts.google.com/o/oauth2/v2/auth?client_id=" + GOOGLE_CLIENT_ID + "&scope=profile%20email%20openid&response_type=code&access_type=offline&include_granted_scopes=true&redirect_uri=" + url_scheme + "://" + domain[0] + "/user/google-callback") + return render_template("login.html", cookieNotice=cookieNotice) \ No newline at end of file diff --git a/main.py b/main.py index c6e760c4116f6cff6483aa3fe1d58bf94dc3822f..f9158e2afb256acaeef7495bfa64e8d3e0944f09 100644 --- a/main.py +++ b/main.py @@ -3,16 +3,21 @@ from waitress import serve #Used as webserver (Production) from flask import Flask, request, render_template, redirect, abort, Markup, session, make_response, jsonify #Used to prepare the dynamic pages import sqlite3 #Used to store the Data import os #Used for getting the enviorement variables -import qrcode #Used to generate the QR -import base64 #Used to encode the generated QR as base64, to directly insert it into the HTML -from requests import post, get #Used to validate recaptcha / oauth -from io import BytesIO #Needed for base64 encoding of the image -from PIL import Image #Needed for QR generation -import json #used for github oauth -from html import escape #This is used to escape characters, if they are send in the url from itsdangerous import URLSafeSerializer #used for signing the cookies import random #used for signing the cookies import string #used for signing the cookies +from table_check import table_check #import the table check file +from makeqr import makeQR #Qr code generation tool +from grecaptcha_verify import grecaptcha_verify #Tool to verify the google recaptcha response +from newurl import newurl #Script to add the new urls to the database +from redirectShortenURL import redirectShortenURL #Script to redirect the user to the long site +from login import login #thandles the login +from home import home #returns the main page +from googlecallback import googleCallback #Script to handle the google oauth login +from githubcallback import githubCallback #Script to handle the Github oauth login +from userprofile import userProfile #This script handles the overview of the own links +from deletelink import deleteLink #Script to delete Links of Users, if they want. +from api import apiGet,apiPost #Scripts to handle the API app = Flask(__name__) @@ -106,8 +111,6 @@ for domains in domain: #Make from every domnain a entry for the select box later domain_to_index[domains] = str(index) index = index + 1 -print(domain_to_index) - if(index > 1): showDomainSelect=True #Show only domain select, if there are more than one available else: @@ -115,193 +118,35 @@ else: domain_prepared = domain[0] -def table_check(): #This function is used on start to make a new Database if not already exists. - create_table_data = """ - CREATE TABLE WEB_URL( - LONG_URL TEXT NOT NULL, - SHORT_URL TEXT NOT NULL, - USERNAME TEXT - ); - """ - create_table_analytics = """ - CREATE TABLE ANALYTICS( - SHORT_URL TEXT NOT NULL, - CALLS INT DEFAULT 1 - ) - """ - with sqlite3.connect('db/urls.db') as conn: - cursor = conn.cursor() - try: #Try making the database structure, if fails Database was already created. - cursor.execute(create_table_data) - except sqlite3.OperationalError: - pass - try: #Try making the database structure, if fails Database was already created. - cursor.execute(create_table_analytics) - except sqlite3.OperationalError: - pass - - -def makeQR(text): #This function is used to create a QR code and encode it base64, if you make a new shortlink - qr = qrcode.QRCode( #QR generation variables - version=1, - error_correction=qrcode.constants.ERROR_CORRECT_L, - box_size=10, - border=1, - ) - qr.add_data(text) #The URL is in the text variable - qr.make(fit=True) #Generate the QR - - img = qr.make_image(fill_color="black", back_color="white") #Encode the WR as base 64 - with BytesIO() as buffer: - img.save(buffer, 'jpeg') - return base64.b64encode(buffer.getvalue()).decode() - - -def grecaptcha_verify(request): #This function is used to verify the google recaptcha code, that is send to the server after submitting a new link - if(skipCaptcha): return True #If recaptcha is disabled alwas return at this point true, which means response is verified - captcha_rs = request.form.get('g-recaptcha-response') - url = "https://www.google.com/recaptcha/api/siteverify" #The baseurl - headers = {'User-Agent': 'DebuguearApi-Browser',} #Useragent doesn't matters, but is set here - params = {'secret': recaptchaPrivateKey, 'response': captcha_rs} #As paramtere we send to google our private Key and the key from the user - verify_rs = post(url,params, headers=headers) #Send a post request with the parameters from before to googlde - verify_rs = verify_rs.json() - response = verify_rs.get("success", False) #Verify that the response includes the success, if so return True, if not return False - return response - - - @app.route('/', methods=['GET']) def home_get(): - try: - loginbar = "Hello " + s.loads(request.cookies.get('username')) + ' (<a href="/user/links" style="color:white">your links</a>, <a href="/user/logout" style="color:white">logout</a>)' - except: - loginbar = '<a href="#" onClick="showLogin()" style="color:white">login</a>' - - try: - return render_template('home.html', builddate=builddate, version=version, domain=domain_prepared, recaptchaPublicKey=recaptchaPublicKey, showDomainSelect=showDomainSelect, loginbar=loginbar, cookieNotice=cookieNotice ,domain_prefilled=domain_to_index[request.headers["host"]]) #return the default site to create a new shorten link - except: - abort(500) + return home(request, builddate, version, domain_prepared, recaptchaPublicKey, showDomainSelect, cookieNotice, domain_to_index, s) @app.route('/', methods=['POST']) #This function is used to create a new url def home_post(): - - try: - userID = s.loads(request.cookies.get('userID')) - loginbar = "Hello " + s.loads(request.cookies.get('username')) + ' (<a href="/user/links" style="color:white">your links</a>, <a href="/user/logout" style="color:white">logout</a>)' - except: - userID = "null" - loginbar = '<a href="/user/login" style="color:white">login</a>' - if not grecaptcha_verify(request): - return render_template('home.html', builddate=builddate, version=version, domain=domain_prepared, snackbar="There was an error validating, that you are a human, please try again.", long_url_prefilled=request.form.get('url'), short_url_prefilled=request.form.get('short').lower(), domain_prefilled=domain_to_index[request.form.get('domain')], recaptchaPublicKey=recaptchaPublicKey, showDomainSelect=showDomainSelect, loginbar=loginbar, cookieNotice=cookieNotice) #return the user the prefilled form with an error message, because no url to short was provided - - if (request.form.get('url').replace(" ", "") == ""): - return render_template('home.html', builddate=builddate, version=version, domain=domain_prepared, snackbar="Please enter a url to short, before submitting this form", long_url_prefilled=request.form.get('url'), short_url_prefilled=request.form.get('short').lower(), domain_prefilled=domain_to_index[request.form.get('domain')], recaptchaPublicKey=recaptchaPublicKey, showDomainSelect=showDomainSelect, loginbar=loginbar, cookieNotice=cookieNotice) #return the user the prefilled form with an error message, because no url to short was provided - if (request.form.get('short').replace(" ", "") == ""): - return render_template('home.html', builddate=builddate, version=version, domain=domain_prepared, snackbar="Please enter a short name, before submitting this form", long_url_prefilled=request.form.get('url'), short_url_prefilled=request.form.get('short').lower(), domain_prefilled=domain_to_index[request.form.get('domain')], recaptchaPublicKey=recaptchaPublicKey, showDomainSelect=showDomainSelect, loginbar=loginbar, cookieNotice=cookieNotice) #return the user the prefilled form with an error message, because no short link was provided - shorturl = (request.form.get('domain') + "/" + request.form.get('short')).lower() - - url = request.form.get('url') - with sqlite3.connect('db/urls.db') as conn: #Check if another user already used the short link - cursor = conn.cursor() - res = cursor.execute('SELECT LONG_URL FROM WEB_URL WHERE SHORT_URL=?', [shorturl]) - try: - short = res.fetchone() - already_used = False - if short is not None: - already_used = True - except: - pass - - if not already_used: #If short link wasn't used before, insert the link in the Database. - res = cursor.execute( - 'INSERT INTO WEB_URL (LONG_URL, SHORT_URL, USERNAME) VALUES (?, ?, ?)', - [url, shorturl, userID] - ) - return render_template('home.html', short_url=shorturl, recaptchaPublicKey=recaptchaPublicKey, builddate=builddate, version=version, domain=domain_prepared, qrcode=makeQR(url_scheme + "://" + shorturl), loginbar=loginbar, cookieNotice=cookieNotice) #return the shorten link to the user - else: - return render_template('home.html', builddate=builddate, version=version, recaptchaPublicKey=recaptchaPublicKey, domain=domain_prepared, snackbar="URL already used, please try another one", long_url_prefilled=request.form.get('url'), short_url_prefilled=request.form.get('short').lower(), domain_prefilled=domain_to_index[request.form.get('domain')], showDomainSelect=showDomainSelect, loginbar=loginbar, cookieNotice=cookieNotice) #return the user the prefilled form with an error message, because the url was already used - + return newurl(request, skipCaptcha, recaptchaPrivateKey, recaptchaPublicKey, builddate, version, domain_prepared, domain_to_index, showDomainSelect, cookieNotice, s, url_scheme) + @app.route('/favicon.ico') #Redirect to the static url of the favicon def favicon(): return redirect("/static/favicon.ico") @app.route('/<short_url>') def redirect_short_url(short_url): - host = request.headers['Host'] - url = "" - with sqlite3.connect('db/urls.db') as conn: #Get the original URL from the database - cursor = conn.cursor() - res = cursor.execute('SELECT LONG_URL FROM WEB_URL WHERE SHORT_URL=?', [host + "/" + short_url.lower()]) - try: - short = res.fetchone() - if short is not None: #If a long url is found - url = short[0] - error_404 = False - else: - error_404 = True #If no url is found throw a 404. If you throw a error in a try / catch block it will be catched by this, so set a variable to true and throw the error later - except Exception as e: #If there happens an error, print the exception to the console and throw a 500 error - print(e) #Print a debug Message to the console - abort(500) #Throw a 500 error. This means internal Server error. - if not error_404: #If there was no 404 error before, redirect the user. If not throw a 404 error - res = cursor.execute('SELECT CALLS FROM ANALYTICS WHERE SHORT_URL=?', [host + "/" + short_url.lower()]) - try: - calls = res.fetchone()[0] + 1 - cursor.execute('UPDATE ANALYTICS SET CALLS = ? WHERE SHORT_URL=?', (str(calls), host + "/" + short_url.lower())) - except: - print(Exception) - cursor.execute('INSERT INTO ANALYTICS (SHORT_URL) VALUES ("' + host + "/" + short_url.lower() + "\")") - - return redirect(url) - else: - abort(404) + return redirectShortenURL(request, short_url) @app.route('/user/login') -def login(): - service = request.args.get("service") - if(service == "github"): return redirect("https://github.com/login/oauth/authorize/?client_id=" + GITHUB_CLIENT_ID + "&scope=user") #redirect the user to the github login page and ask for access to user data (name, email, ...) - if(service == "google"): return redirect("https://accounts.google.com/o/oauth2/v2/auth?client_id=" + GOOGLE_CLIENT_ID + "&scope=profile%20email%20openid&response_type=code&access_type=offline&include_granted_scopes=true&redirect_uri=" + url_scheme + "://" + domain[0] + "/user/google-callback") - return render_template("login.html", cookieNotice=cookieNotice) +def loginPage(): + return login(request, GITHUB_CLIENT_ID, cookieNotice, GOOGLE_CLIENT_ID, url_scheme, domain) @app.route("/user/google-callback") def authorizeGoogle(): - try: - code = request.args.get("code") - url = "https://www.googleapis.com/oauth2/v4/token" #The baseurl - headers = {'Content-Type': 'application/x-www-form-urlencoded',} - params = {'client_id': GOOGLE_CLIENT_ID, 'client_secret': GOOGLE_CLIENT_SECRET, 'code': code, "grant_type": "authorization_code", "redirect_uri": url_scheme + "://" + domain[0] + "/user/google-callback"} #As paramtere we send the client id and the client secret which we get from github when registering an application and the user code from before - access_token = post(url,params, headers=headers).text.split('access_token": "')[1].split('"')[0] - authorization_header = {"Authorization": "OAuth %s" % access_token} - r = get("https://www.googleapis.com/oauth2/v2/userinfo", headers=authorization_header) - userID = r.text.split('"id": "')[1].split('"')[0] - name = r.text.split('"name": "')[1].split('"')[0] - resp = make_response(redirect('/')) #redirect the user at the end back to the main page - resp.set_cookie('userID', s.dumps("google_" + userID)) #set the cookies with username and userid - resp.set_cookie('username', s.dumps(name)) - return resp - except: - return "Authentication failed" + return googleCallback(request, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, url_scheme, domain, s) @app.route('/user/github-callback') #Github redirects to this link after the user authenticated. Then we use the Token we get from github and request via the github api the username and the userid def authorizeGithub(): - try: - code = request.args.get("code") - url = "https://github.com/login/oauth/access_token" #The baseurl - params = {'client_id': GITHUB_CLIENT_ID, 'client_secret': GITHUB_CLIENT_SECRET, 'code': code} #As paramtere we send the client id and the client secret which we get from github when registering an application and the user code from before - oauth_token = post(url,params).text.split("access_token=")[1].split("&")[0] #Send a post request with the parameters from - - headers = {'Authorization': 'token ' + oauth_token,} #Useragent doesn't matters, but is set here - githubResponse = get("https://api.github.com/user", headers=headers).text - userID = str(json.loads(githubResponse)['id']) - username = str(json.loads(githubResponse)['login']) - - resp = make_response(redirect('/')) #redirect the user at the end back to the main page - resp.set_cookie('userID', s.dumps("github_" + userID)) #set the cookies with username and userid - resp.set_cookie('username', s.dumps(username)) - return resp - except: - return "Authentication failed" + return githubCallback(request, GITHUB_CLIENT_SECRET, GITHUB_CLIENT_ID, s) @app.route('/user/logout') def logout(): @@ -312,48 +157,11 @@ def logout(): @app.route('/user/links')#This function gives the user the posibility to see and delete his links def ownLinks(): - try: - userID = s.loads(request.cookies.get('userID')) #Get the userid from the cookie - loginbar = "Hello " + s.loads(request.cookies.get('username')) + ' (<a href="/user/logout" style="color:white">logout</a>)' #This is the loginbar - except: - return redirect("/user/login") #If user is not logged in redirect him to the login page - - with sqlite3.connect('db/urls.db') as conn: - cursor = conn.cursor() - res = cursor.execute('SELECT LONG_URL, SHORT_URL FROM WEB_URL WHERE USERNAME=?', [userID]) #Get all entries from the database, that are created by this user - response = '<table id="t01">\n<tr>\n<th>Long URL</th>\n<th>Short URL</th>\n<th>Views</th>\n<th>Action</th>\n</tr>\n' #This is the layout of the table - try: - entriesList = res.fetchall() - for entries in entriesList: #for every entrie in the database add a line to the table - cursor2 = conn.cursor() - try: - calls = str(cursor2.execute('SELECT CALLS FROM ANALYTICS WHERE SHORT_URL=?', [entries[1]]).fetchone()[0]) - except: - calls = "0" - response = response + "<tr>\n<td>" + entries[0] + "</td>\n<td>" + entries[1] + '</td>\n<td>' + calls + '</td>\n<td><a id="red" href="/user/delete?link=' + escape(entries[1]) + '">delete</a> <a href="#" id="dialog-link" onclick="buttonListener(\'' + entries[1] + '\', this)">QR</a></tr>\n' - response = response + "</table>" #Close the table - if(len(entriesList) == 0): response = 'you have no shorten links.' #If user has no shorten links make this message - except: - abort(500) #Shouldn't happen, 500 means internal server error - return render_template('editEntries.html', content=response, loginbar=loginbar, cookieNotice=cookieNotice) #Put the table and the login div inside the template and server it to the user - + return userProfile(request, cookieNotice, s) @app.route('/user/delete') #This function is called if a user deletes an entrie def delete(): - try: - userID = s.loads(request.cookies.get('userID')) #get the userid from the cookie - loginbar = "Hello " + s.loads(request.cookies.get('username')) + ' (<a href="/user/logout" style="color:white">logout</a>)' # generate the login form - except: - return redirect("/user/login") # if user is not logged in redirect him to the login page - linkToDelete = request.args.get('link') #get the link, which the user want's to delete from the parameter in the url. - - with sqlite3.connect('db/urls.db') as conn: - cursor = conn.cursor() - try: - cursor.execute('DELETE FROM WEB_URL WHERE SHORT_URL=? AND USERNAME=?', [linkToDelete, userID]) #Delete the entrie - return redirect('/user/links') #redirect the user back to the table. - except: - abort(500) + return deleteLink(request, s) @app.route('/user/makeqr') def makeQrCode(): @@ -362,80 +170,12 @@ def makeQrCode(): @app.route('/user/api', methods=['POST']) def api(): - username = "error" - try: - username = sAPI.loads(request.form['apikey']) - except: - pass - - try: - short = request.form['short'] - except: - return jsonify( - status="1", - message="short link missing" - ) - - try: - longURL = request.form['long'] - except: - return jsonify( - status="2", - message="link to short is missing" - ) - - domain_ = short.split("/")[0] - if not domain_ in domain: - return jsonify( - status="3", - message="domain for short link is not in allowed domain list" - ) - - with sqlite3.connect('db/urls.db') as conn: #Check if another user already used the short link - cursor = conn.cursor() - res = cursor.execute('SELECT LONG_URL FROM WEB_URL WHERE SHORT_URL=?', [short]) - try: - short2 = res.fetchone() - already_used = False - if short2 is not None: - already_used = True - except: - pass - - if not already_used: #If short link wasn't used before, insert the link in the Database. - res = cursor.execute( - 'INSERT INTO WEB_URL (LONG_URL, SHORT_URL, USERNAME) VALUES (?, ?, ?)', - [longURL, short, username] - ) - - try: - request.form['qr'] - qr64 = "data:image/jpeg;base64," + makeQR(url_scheme + "://" + short) - - return jsonify( - status="0", - message="ok", - qr=qr64 - ) - except: - return jsonify( - status="0", - message="ok" - ) - else: - return jsonify( - status="4", - message="short url already in use" - ) + return apiPost(request, domain) + @app.route('/user/api', methods=['GET']) def apiDocs(): - try: - userID = sAPI.dumps(s.loads(request.cookies.get('userID'))) - except: - userID = "" - - return render_template("apiDocs.html", apikey=userID, domain=request.headers['Host'], url_scheme=url_scheme) + return apiGet(request, url_scheme) if __name__ == '__main__': diff --git a/makeqr.py b/makeqr.py new file mode 100644 index 0000000000000000000000000000000000000000..3ce8b53d730d7da5198e24cc2e5cbf097af9bac3 --- /dev/null +++ b/makeqr.py @@ -0,0 +1,19 @@ +import qrcode #Used to generate the QR +import base64 #Used to encode the generated QR as base64, to directly insert it into the HTML +from io import BytesIO #Needed for base64 encoding of the image +from PIL import Image #Needed for QR generation + +def makeQR(text): #This function is used to create a QR code and encode it base64, if you make a new shortlink + qr = qrcode.QRCode( #QR generation variables + version=1, + error_correction=qrcode.constants.ERROR_CORRECT_L, + box_size=10, + border=1, + ) + qr.add_data(text) #The URL is in the text variable + qr.make(fit=True) #Generate the QR + + img = qr.make_image(fill_color="black", back_color="white") #Encode the WR as base 64 + with BytesIO() as buffer: + img.save(buffer, 'jpeg') + return base64.b64encode(buffer.getvalue()).decode() \ No newline at end of file diff --git a/newurl.py b/newurl.py new file mode 100644 index 0000000000000000000000000000000000000000..0f0d07ba61490805bbe8dc7b90badaea36ef8387 --- /dev/null +++ b/newurl.py @@ -0,0 +1,41 @@ +from grecaptcha_verify import grecaptcha_verify +from flask import render_template +import sqlite3 +from makeqr import makeQR + +def newurl(request, skipCaptcha, recaptchaPrivateKey, recaptchaPublicKey, builddate, version, domain_prepared, domain_to_index, showDomainSelect, cookieNotice, s, url_scheme): + try: + userID = s.loads(request.cookies.get('userID')) + loginbar = "Hello " + s.loads(request.cookies.get('username')) + ' (<a href="/user/links" style="color:white">your links</a>, <a href="/user/logout" style="color:white">logout</a>)' + except: + userID = "null" + loginbar = '<a href="/user/login" style="color:white">login</a>' + if not grecaptcha_verify(request, skipCaptcha, recaptchaPrivateKey): + return render_template('home.html', builddate=builddate, version=version, domain=domain_prepared, snackbar="There was an error validating, that you are a human, please try again.", long_url_prefilled=request.form.get('url'), short_url_prefilled=request.form.get('short').lower(), domain_prefilled=domain_to_index[request.form.get('domain')], recaptchaPublicKey=recaptchaPublicKey, showDomainSelect=showDomainSelect, loginbar=loginbar, cookieNotice=cookieNotice) #return the user the prefilled form with an error message, because no url to short was provided + + if (request.form.get('url').replace(" ", "") == ""): + return render_template('home.html', builddate=builddate, version=version, domain=domain_prepared, snackbar="Please enter a url to short, before submitting this form", long_url_prefilled=request.form.get('url'), short_url_prefilled=request.form.get('short').lower(), domain_prefilled=domain_to_index[request.form.get('domain')], recaptchaPublicKey=recaptchaPublicKey, showDomainSelect=showDomainSelect, loginbar=loginbar, cookieNotice=cookieNotice) #return the user the prefilled form with an error message, because no url to short was provided + if (request.form.get('short').replace(" ", "") == ""): + return render_template('home.html', builddate=builddate, version=version, domain=domain_prepared, snackbar="Please enter a short name, before submitting this form", long_url_prefilled=request.form.get('url'), short_url_prefilled=request.form.get('short').lower(), domain_prefilled=domain_to_index[request.form.get('domain')], recaptchaPublicKey=recaptchaPublicKey, showDomainSelect=showDomainSelect, loginbar=loginbar, cookieNotice=cookieNotice) #return the user the prefilled form with an error message, because no short link was provided + shorturl = (request.form.get('domain') + "/" + request.form.get('short')).lower() + + url = request.form.get('url') + with sqlite3.connect('db/urls.db') as conn: #Check if another user already used the short link + cursor = conn.cursor() + res = cursor.execute('SELECT LONG_URL FROM WEB_URL WHERE SHORT_URL=?', [shorturl]) + try: + short = res.fetchone() + already_used = False + if short is not None: + already_used = True + except: + pass + + if not already_used: #If short link wasn't used before, insert the link in the Database. + res = cursor.execute( + 'INSERT INTO WEB_URL (LONG_URL, SHORT_URL, USERNAME) VALUES (?, ?, ?)', + [url, shorturl, userID] + ) + return render_template('home.html', short_url=shorturl, recaptchaPublicKey=recaptchaPublicKey, builddate=builddate, version=version, domain=domain_prepared, qrcode=makeQR(url_scheme + "://" + shorturl), loginbar=loginbar, cookieNotice=cookieNotice) #return the shorten link to the user + else: + return render_template('home.html', builddate=builddate, version=version, recaptchaPublicKey=recaptchaPublicKey, domain=domain_prepared, snackbar="URL already used, please try another one", long_url_prefilled=request.form.get('url'), short_url_prefilled=request.form.get('short').lower(), domain_prefilled=domain_to_index[request.form.get('domain')], showDomainSelect=showDomainSelect, loginbar=loginbar, cookieNotice=cookieNotice) #return the user the prefilled form with an error message, because the url was already used diff --git a/redirectShortenURL.py b/redirectShortenURL.py new file mode 100644 index 0000000000000000000000000000000000000000..99f1eaef2164fd8a9773d27b803930549b10af91 --- /dev/null +++ b/redirectShortenURL.py @@ -0,0 +1,31 @@ +from flask import redirect, abort +import sqlite3 + +def redirectShortenURL(request, short_url): + host = request.headers['Host'] + url = "" + with sqlite3.connect('db/urls.db') as conn: #Get the original URL from the database + cursor = conn.cursor() + res = cursor.execute('SELECT LONG_URL FROM WEB_URL WHERE SHORT_URL=?', [host + "/" + short_url.lower()]) + try: + short = res.fetchone() + if short is not None: #If a long url is found + url = short[0] + error_404 = False + else: + error_404 = True #If no url is found throw a 404. If you throw a error in a try / catch block it will be catched by this, so set a variable to true and throw the error later + except Exception as e: #If there happens an error, print the exception to the console and throw a 500 error + print(e) #Print a debug Message to the console + abort(500) + if not error_404: #If there was no 404 error before, redirect the user. If not throw a 404 error + res = cursor.execute('SELECT CALLS FROM ANALYTICS WHERE SHORT_URL=?', [host + "/" + short_url.lower()]) + try: + calls = res.fetchone()[0] + 1 + cursor.execute('UPDATE ANALYTICS SET CALLS = ? WHERE SHORT_URL=?', (str(calls), host + "/" + short_url.lower())) + except: + print(Exception) + cursor.execute('INSERT INTO ANALYTICS (SHORT_URL) VALUES ("' + host + "/" + short_url.lower() + "\")") + + return redirect(url) + else: + abort(404) diff --git a/table_check.py b/table_check.py new file mode 100644 index 0000000000000000000000000000000000000000..f502d9761bd3b582cc9f2d92d4ed4a7721e41ac0 --- /dev/null +++ b/table_check.py @@ -0,0 +1,26 @@ +import sqlite3 + +def table_check(): #This function is used on start to make a new Database if not already exists. + create_table_data = """ + CREATE TABLE WEB_URL( + LONG_URL TEXT NOT NULL, + SHORT_URL TEXT NOT NULL, + USERNAME TEXT + ); + """ + create_table_analytics = """ + CREATE TABLE ANALYTICS( + SHORT_URL TEXT NOT NULL, + CALLS INT DEFAULT 1 + ) + """ + with sqlite3.connect('db/urls.db') as conn: + cursor = conn.cursor() + try: #Try making the database structure, if fails Database was already created. + cursor.execute(create_table_data) + except sqlite3.OperationalError: + pass + try: #Try making the database structure, if fails Database was already created. + cursor.execute(create_table_analytics) + except sqlite3.OperationalError: + pass \ No newline at end of file diff --git a/userprofile.py b/userprofile.py new file mode 100644 index 0000000000000000000000000000000000000000..2224612ce8e5d278034711e45f02373753cd505e --- /dev/null +++ b/userprofile.py @@ -0,0 +1,30 @@ +import sqlite3 +from flask import redirect, abort, render_template +from html import escape #This is used to escape characters, if they are send in the url + +def userProfile(request, cookieNotice, s): + try: + userID = s.loads(request.cookies.get('userID')) #Get the userid from the cookie + loginbar = "Hello " + s.loads(request.cookies.get('username')) + ' (<a href="/user/logout" style="color:white">logout</a>)' #This is the loginbar + except: + return redirect("/user/login") #If user is not logged in redirect him to the login page + + with sqlite3.connect('db/urls.db') as conn: + cursor = conn.cursor() + res = cursor.execute('SELECT LONG_URL, SHORT_URL FROM WEB_URL WHERE USERNAME=?', [userID]) #Get all entries from the database, that are created by this user + response = '<table id="t01">\n<tr>\n<th>Long URL</th>\n<th>Short URL</th>\n<th>Views</th>\n<th>Action</th>\n</tr>\n' #This is the layout of the table + try: + entriesList = res.fetchall() + for entries in entriesList: #for every entrie in the database add a line to the table + cursor2 = conn.cursor() + try: + calls = str(cursor2.execute('SELECT CALLS FROM ANALYTICS WHERE SHORT_URL=?', [entries[1]]).fetchone()[0]) + except: + calls = "0" + response = response + "<tr>\n<td>" + entries[0] + "</td>\n<td>" + entries[1] + '</td>\n<td>' + calls + '</td>\n<td><a id="red" href="/user/delete?link=' + escape(entries[1]) + '">delete</a> <a href="#" id="dialog-link" onclick="buttonListener(\'' + entries[1] + '\', this)">QR</a></tr>\n' + response = response + "</table>" #Close the table + if(len(entriesList) == 0): response = 'you have no shorten links.' #If user has no shorten links make this message + except: + print(Exception) + abort(500) #Shouldn't happen, 500 means internal server error + return render_template('editEntries.html', content=response, loginbar=loginbar, cookieNotice=cookieNotice) #Put the table and the login div inside the template and server it to the user