-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathschort.py
executable file
·99 lines (88 loc) · 3.48 KB
/
schort.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Flask, render_template, url_for, request, redirect, abort, escape
import sqlite3, random, string, time, hashlib, base64
from urllib.parse import urlparse
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
@app.route('/<shortLink>', methods=['GET', 'POST'])
def short(shortLink=""):
if request.method == "GET" or request.method == "HEAD":
if shortLink:
noauto = shortLink[-1] == "+"
if noauto: shortLink = shortLink[:-1]
url = retrieveUrlFromShortLink(shortLink)
if url is not None:
if "resolve" in request.args:
return escape(url)
else:
if noauto:
url = str(escape(url))
html = "<a href=" + url + ">" + url + "</a>"
return html
else:
return redirect(url, code=301) # Redirect to long URL saved in the database
else:
return render_template("index.html", name=shortLink, message="Enter long URL for "+ request.url_root + shortLink+":", message_type="info"), 404 # Custom link page
else:
return render_template("index.html", name=shortLink) # Landing page
elif request.method == "POST": # Someone submitted a new link to short
longUrl = request.form.get("url", "")
wishId = request.form.get("wishId")
if len(longUrl) <= 0:
abort(400)
databaseId = insertIdUnique(longUrl, idToCheck=wishId)
return request.url_root + databaseId # Short link in plain text
def retrieveUrlFromShortLink(shortLink):
conn = sqlite3.connect("data/links.sqlite")
c = conn.cursor()
result = c.execute ('SELECT longLink FROM links WHERE shortLink=?', (shortLink, )).fetchone()
conn.close()
if result:
url = result[0]
parsedUrl = urlparse(url)
if parsedUrl.scheme == "":
url = "http://" + url
return url
else:
return None
def insertIdUnique(longUrl, idToCheck=None):
hashUrl = hashlib.sha256(longUrl.encode()).digest()
base64Url = base64.urlsafe_b64encode(hashUrl).decode()
if idToCheck == None or idToCheck == "":
idToCheck = base64Url[:4]
conn = sqlite3.connect("data/links.sqlite")
c = conn.cursor()
try:
c.execute('INSERT INTO links VALUES (?, ?, ?, ?, ?)', (idToCheck, longUrl, int(time.time()), request.remote_addr, "default" ))
databaseId = idToCheck
conn.commit()
conn.close()
except sqlite3.IntegrityError as e:
print("Hash already exists, does the long URL matches?")
longUrlDb = c.execute('SELECT * FROM links WHERE shortLink=?', (idToCheck, )).fetchone()
if longUrl == longUrlDb[1]:
print(longUrl + " is already in database with id " + idToCheck + ". Serving old id…")
databaseId = idToCheck
else:
print("Found real hash collision for " + longUrl + " and " + longUrlDb[1])
conn.commit()
conn.close()
if len(base64Url) - 1 >= len(idToCheck) + 1:
databaseId = insertIdUnique(longUrl, idToCheck=base64Url[:len(idToCheck)+1])
else:
print("Can't produce a long enough hash from the new link to be unique. This should never happen")
print("Bailing out, you are on your own. Good luck.")
print("=========================================================================================")
abort(500)
return databaseId
def initDB():
conn = sqlite3.connect("data/links.sqlite")
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS links (shortLink UNIQUE NOT NULL, longLink, timestamp, ip, redirectMethod);''')
conn.commit()
conn.close()
if __name__ == '__main__':
initDB()
app.run(debug=True) # If you call this file directly it will always run in debug mode. THIS IS VERY DANGEROUS!
# vim: noexpandtab:ts=2:sw=2:sts=2