Commit d2f204d7 authored by Jean Michel Rouly's avatar Jean Michel Rouly
Browse files

first commit

parents
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
##Redirect queries to the /rd script
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /rd [L]
</IfModule>
<hr/>
<div id="signoff">
A project of
<a href="http://srct.gmu.edu/">GMU SRCT</a>.
<a href="http://opensource.org/licenses/MIT/">Some rights reserved</a>.
</div>
</div>
</body>
</html>
\ No newline at end of file
/*
* ########################################################################
* ########################################################################
* ######## ########
* ######## ########
* ######## Project: GO - URL Shorterner ########
* ######## File: style.css ########
* ######## Date: 2013.04.04 ########
* ######## Author: Michel Rouly ########
* ######## ########
* ######## Style sheet for entire web application. Modify ########
* ######## any design changes here. ########
* ######## ########
* ######## ########
* ########################################################################
* ########################################################################
*/
body {
background: #EFEFEF;
}
h1 {
font-size: 3em;
color: #006633;
}
h1 a {
color: inherit;
text-decoration: none;
}
hr {
color: #006633;
background-color: #006633;
height: 2px;
width: 50%;
border: 0px;
}
#box {
color: #383838;
width: 500px;
margin: auto;
padding: 20px;
border-radius: 15px;
background-color: #EFEF88;
text-align: center;
font-family: 'Carrois Gothic', sans-serif;
}
#signoff {
color: #000000;
text-align: center;
font-size: .6em;
font-style: italic;
}
label {
color: #565656;
cursor: pointer;
font-weight: bold;
text-transform: uppercase;
}
p {
font-size: .8em;
}
input {
color: #9a9a9a;
cursor: pointer;
border: 0px solid #343434;
border-radius: 5px;
background-color: #FDFDB8;
padding: 5px;
}
input:focus {
color: #000000;
}
input[type="submit"] {
color: #000000;
background-color: #E86847;
border: 0px;
}
a {
text-decoration: none;
}
\ No newline at end of file
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>
Go Url Shortener
</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="http://fonts.googleapis.com/css?family=Carrois+Gothic" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="/site_data/style.css" />
</head>
<body>
<div id="box">
<h1><a href="/">Go (URL Shortener)</a></h1>
<hr/>
##############################
# Site configuration variables
#
# These variables configure how the website is structured.
##############################
# doc_root: Describes where the website's root directory is.
doc_root = "/srv/http"
##############################
# MySQL connection variables
#
# These variables configure how the website connects
# to the required MySQL database.
##############################
# sql_domain: The location of the SQL database to connect to.
sql_domain = "localhost"
# sql_usr: The username to use with this database.
sql_usr = "go-user"
# sql_pasw: The plaintext password used to connec to the database.
sql_pasw = "georgemasonsrct"
# sql_db: The SQL database or schema name to which to connect.
sql_db = "srctgo"
# sql_table: The SQL table storing the URL listing.
sql_table = "urls"
##############################
# Behaviour configuration variables
#
# These variables define various system behaviors, such
# as global limits or values.
##############################
# min_url_len: This is the minimum required length of
# a "short url" or url identifier.
min_url_len = 5
import ldap
import site
site.addsitedir('/srv/http/wsgi')
import library
import goconfig
def application(environ, start_response):
## This application should display two the user one of two
## screens, depending on the situation.
##
## Given an unverified user, display the login screen.
## On login, authenticate the user's credentials. If the credentials
## are good, then consider the user logged in.
##
## Given a logged in user, display the URL register screen. On
## submission, transfer control to the url-register script and
## allow it to verify submission content. If the url-register script
## sends the user back here, the user should remain logged in and
## have no issues travelling back and forth.
## IDEAS FOR LOGGING-IN USERS:
## store active users in a database somewhere
## look for an existing framework to integrate with
## cookies / sessions (a la PHP)
# Construct the default body, along with its header/footer wrapper.
body = []
f = open(goconfig.doc_root + "/site_data/top.part", "r")
top_part = f.read()
f.close()
f = open(goconfig.doc_root + "/site_data/bottom.part", "r")
bottom_part = f.read()
f.close()
login_form = """
<form action="" method="post">
<label for="usr">username</label>
<p>Your administrator username (MasonID).</p>
<input type="text" id="usr" name="usr" value="" />
<br /><br />
<label for="pass">password</label>
<p>Your administrator password.</p>
<input type="password" id="pass" name="pass" value="" />
<br /><br />
<input type="submit" name="submit" value="LOGIN" />
<br />
</form>
"""
#body.append( login_form )
url_form = """
<form action="/exec/rg" method="post" target="_self">
<label for="long-url">long URL</label>
<p>Make sure to include http:// in front.</p>
<input type="text" id="long-url" name="long-url" value="http://" />
<br /><br />
<label for="short-url">identifier</label>
<p>What your want your URL to look like. This is optional.</p>
<p>Identifier must be at least 5 characters, and only
contain letters and numbers.</p>
<input type="text" id="short-url" name="short-url" value="" />
<br /><br />
<input type="submit" name="submit" value="SHORTEN" />
<br />
</form>
"""
#body.append( url_form )
if( library.user_logged_in() ):
body.append( url_form )
else:
body.append( login_form )
body = ''.join( body )
response = top_part + body + bottom_part
status = '200 OK'
response_headers = [('Content-type', 'text/html'),
('Content-Length', str(len(response)))]
start_response(status, response_headers)
return [response]
import random
import math
import site
# Determine if a user is appropriately validated through LDAP.
def user_logged_in():
return True
# Given a dictionary and a set of relevant entries, this procedure
# removes all irrelevant entries, effectively trimming the noise level
def trim_noise( dictionary, relevant_keys ):
marked_for_removal = []
for key in dictionary:
if key not in relevant_keys:
marked_for_removal.append( key )
for item in marked_for_removal:
del dictionary[item]
# Parse post data submitted to this function. That is, split it up as a
# dictionary and return that readable dictionary.
def parse_post_data( post_data ):
delimiter = "&"
subdelimiter = "="
# read stream to a list
data = post_data.read()
if len( data ) > 0:
# create a dictionary as {field:val, field:val, ... }
data = dict( item.split(subdelimiter) for item in data.split( delimiter ) )
# return the dictionary of data
return data
# if there is no data, return an empty result
return None
# Generate a random short url from a list of possible characters
# and the minimum allowed length.
def generate_short_url( long_url, min_len ):
decimal = 10
encoding = 62
# determine the range of possible values (set by min_len)
min_val = encoding ** (min_len - 1)
max_val = (encoding ** min_len) - 1
# generate the short url (some val between min and max)
value = random.randint( min_val, max_val )
# Encode the short url value in the most appropriate base.
short = []
# define the list of possible characters
charlist = list("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
while value > 0:
short.append(charlist[ int(value % encoding) ])
value = math.floor( value / encoding )
return ''.join( short )
# This function should return true if the specified short_url
# already exists in the mySQL database. This prevents overlapping.
def short_url_exists( cursor, table, short_url ):
output = cursor.execute(
""" SELECT * from """ + table +
""" WHERE shorturl = %s """, (short_url))
output = True if output > 0 else False
return output
import MySQLdb
import site
site.addsitedir('/srv/http/wsgi')
import library
import goconfig
def application(environ, start_response):
# Construct the default body, along with its header/footer wrapper.
body = ["<p>Nothing here.</p>"]
f = open(goconfig.doc_root + "/site_data/top.part", "r")
top_part = f.read()
f.close()
f = open(goconfig.doc_root + "/site_data/bottom.part", "r")
bottom_part = f.read()
f.close()
# Set up an empty URL in case we can't find one in the database.
url = None
# Grab the requested short_url. Strip it of white space, and remove
# the initial forward slash.
request = environ["REQUEST_URI"]
target = request.strip()[1:]
try:
# Connect to and choose the database.
mdb = MySQLdb.connect(
goconfig.sql_domain,
goconfig.sql_usr,
goconfig.sql_pasw,
goconfig.sql_db )
cursor = mdb.cursor()
# If we need to create the urls table, then construct it.
cursor.execute("""CREATE TABLE IF NOT EXISTS %s(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
longurl VARCHAR(100),
shorturl VARCHAR(100))""" % goconfig.sql_table)
# Query the database for the short_url value.
query = cursor.execute(
""" SELECT * from """ + goconfig.sql_table +
""" WHERE shorturl = %s """, (target))
# If at least one row has been found, then grab its short_url.
# If no rows are found, though, then don't do anything more!
if query > 0:
# Grab the data from the database.
selection = cursor.fetchall()
row = selection[0] # we are only interested in the first result
url = row[1] # this is the index of the longurl field
# Close the connection to the mySQL database.
mdb.close()
except MySQLdb.OperationalError:
body.append( "<p>Error in mySQL database.</p>" )
body = ''.join( body )
if url == None:
response = top_part + body + bottom_part
status = '200 OK'
response_headers = [('Content-type', 'text/html'),
('Content-Length', str(len(response)))]
start_response(status, response_headers)
return [response]
else:
response = "here we go!"
status = '303 See other'
response_headers = [('Location', url),
('Content-Length', str(len(response)))]
start_response(status, response_headers)
return [response]
import re
import urllib
import MySQLdb
import ldap
import site
site.addsitedir('/srv/http/wsgi')
import library
import goconfig
################ CONSTANT VALUES ####
# relevant input fields
fields = ["short-url", "long-url"]
url_regex = re.compile(
r'^(?:http|ftp)s?://' # http:// or https://
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain...
r'localhost|' #localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
short_regex = re.compile(
r'^[a-z0-9]+$', re.IGNORECASE)
#####################################
def application(environ, start_response):
# Boolean flags which describe the validity of user input.
INV_LU = False # invalid long url
INV_SU = False # invalid short url
SU_TS = False # short url too short
SU_EX = False # short url already exists
# Set default "empty page" text.
body = ["<p>Nothing here.</p>"]
# If the page was requested via POST, that means the URL-input
# form was submitted. Scan over the input data, parse it, validate
# it, and then finally connect to the DB and store it. Then output.
while environ["REQUEST_METHOD"] == "POST" and library.user_logged_in():
# Grab user data, cut off non-relevant fields.
data = environ['wsgi.input']
data = library.parse_post_data( data )
library.trim_noise( data, fields )
# Store parsed user data in these handy variables.
long_url = data["long-url"]
short_url = data["short-url"]
long_url = urllib.unquote( long_url )
short_url = urllib.unquote( short_url )
# Try to connect to the database and store this pairing
# in the appropriate DB and Table.
try:
# Connect to and choose the database.
mdb = MySQLdb.connect(
goconfig.sql_domain,
goconfig.sql_usr,
goconfig.sql_pasw,
goconfig.sql_db )
cursor = mdb.cursor()
# If we need to create the urls table, then construct it.
cursor.execute("""CREATE TABLE IF NOT EXISTS %s(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
longurl VARCHAR(100),
shorturl VARCHAR(100))""" % goconfig.sql_table)
# If no custom short url identifier was entered, then
# generate a randomized value from the library.
if len( short_url ) == 0:
short_url = library.generate_short_url( long_url, goconfig.min_url_len )
while library.short_url_exists( cursor, goconfig.sql_table, short_url ):
short_url = library.generate_short_url( long_url, goconfig.min_url_len )
# Check each possible error case and set flags accordingly.
# 1) Check that long_url is a valid URL
# 2) Check that short_url exceeds the minimum length
# 3) Check that short_url only contains the appropriate characters
# 4) Check that short_url doesn't already exist in the database
if re.match(url_regex, long_url) == None:
INV_LU = True # long url validity
if len( short_url ) < goconfig.min_url_len:
SU_TS = True # short url length
if re.match(short_regex, short_url) == None:
INV_SU = True # short url validity
if library.short_url_exists( cursor, goconfig.sql_table, short_url ):
SU_EX = True # short url uniqueness
# Reset body of output to prepare for possible error/success msg.
body = []
# Depending on the values of error flags, display an appropriate
# message to the user.
if INV_LU:
body.append("<p>You entered an invalid long url!</p>")
if INV_SU:
body.append("<p>The identifier can contain only letters and numbers.</p>")
if SU_TS:
body.append("<p>The identifier must be at least " + str(goconfig.min_url_len) + " characters.</p>")
if SU_EX:
body.append("<p>The identifier already exists in the database!</p>")
# If none of the error flags have been thrown, then
# append the success messages.
# ie. Display the long and short URLs.
if (not INV_LU) and (not INV_SU) and (not SU_TS) and (not SU_EX):
body.append(
'<p><em>Original URL:</em> <a href="%s">%s</a></p>' %
(long_url, long_url))
body.append(
'<p><em>Shortened URL:</em> <a href="/%s">%s</a></p>' %
(short_url, short_url))
else:
body.append(
'<input type="submit" value="BACK" ' +
'onclick="history.back()" />')
break;
# If execution has proceeded this far, then all the error
# flags must be down, and we are connected successfully to the
# SQL database. If this is the case, then move on to
# insert the longurl-shorturl pairing in the database.
cursor.execute(
"""INSERT INTO """ + goconfig.sql_table + """ (id, longurl, shorturl) """ +
"""VALUES (NULL, %s, %s)""", (long_url, short_url))
# Commit changes and close connection.
mdb.commit()
mdb.close()
except MySQLdb.OperationalError:
body.append( "<p>Error in mySQL database.</p>" )
finally:
break
break # failsafe in case we get to the end somehow!
# Read and store in memory the header and footer sections
# of the page display.
f = open(goconfig.doc_root + "/site_data/top.part", "r")
top_part = f.read()
f.close()
f = open(goconfig.doc_root + "/site_data/bottom.part", "r")
bottom_part = f.read()
f.close()
# Construct the HTML output using the wrapper and body data.
body = ''.join( body )
response = top_part + body + bottom_part
# Do web-stuff
status = '200 OK'
response_headers = [('Content-type', 'text/html'),
('Content-Length', str(len(response)))]
start_response(status, response_headers)
return [response]
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment