#!/usr/bin/python

import sys
import socket
import HTMLParser
import gzip
import os

# Default port
PORT = 80

# Host to connect to
FAKEBOOK_HOST = "fring.ccs.neu.edu"

# Main domain
REQUIRED_DOMAIN = "http://fring.ccs.neu.edu"

# Keep track of csrf token for login
csrfToken = ""

# Setup get requests
def setupGetRequest(path):
	global setCookie
	
	cookie = ""
	for c in setCookie:
		c = c.replace(" ", "")
		if cookie != "":
			cookie = cookie + ";"
		cookie = cookie + " " + c
	
	if (len(setCookie) == 0):
		request = "GET " + path + " HTTP/1.1 \r\nHost: " + FAKEBOOK_HOST + "\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36\r\nAccept-Encoding: gzip\r\n\r\n"

	else:
		request = "GET " + path + " HTTP/1.1 \r\nHost: " + FAKEBOOK_HOST + " \r\nCookie: sessionid=0e62830cdf220a7a2bb4623ac68f85ca\r\nAccept: text/html,application/xhtml+xml,application/xml; q=0.9,image/webp,*/*; q=0.8\r\nAccept-Language: en-US,en; q=0.8\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36\r\nAccept-Encoding: gzip, deflate, sdch\r\n\r\n"

	print request	
        return request
		
# Setup the post request to login
def setupLoginRequest():
	global csrfToken
	global setCookie

	cookieString = ""
	for c in setCookie:
		if cookieString != "":
			cookieString = cookieString + ";"
		cookieString = cookieString + " " + c

	request = "POST /accounts/login/ HTTP/1.1 \r\nHost: " + FAKEBOOK_HOST + "\r\nContent-Length: 109\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\nOrigin: http://fring.ccs.neu.edu\r\nCookie: " + cookieString +"\r\nUpgrade-Insecure-Requests: 1\r\nContent-Type: application/x-www-form-urlencoded\r\nDNT: 1\r\nReferer: http://fring.ccs.neu.edu/accounts/login/\r\nAccept-Language: en-US,en;q=0.8\r\n\r\nusername=" + username + "&password=" + password + "&csrfmiddlewaretoken=" + csrfToken + "&next=%2Ffakebook%2F\r\n"
	
	return request

# Get the response
def getResponse(sock):
	fullResponse = ""; 
	while True:
		response = sock.recv(3000)

                if not response:
                       break;
                else:
                       fullResponse = fullResponse + response

	return fullResponse
	
# Get the status from the response
def getStatus(response):
	# Get each line of the response
	lines = response.split("\r\n")

	# The first line gives status information
	# the second param is the status information
	initLine = lines[0].split()

	if initLine[0] == "HTTP/1.1":
		status = str(initLine[1])

	print status
	return status
		
# Go through the html and look for more
# URLs as well as the secret flags
def handleOkStatus(response):
	global setCookie
	isChunked = False
	isZipped = False

	# Get the message body
	lines = response.split("\r\n")
	
	# Check the Header lines for cookies if we don't
	# have them yet and whether or not it is chunked
	for l in lines:
		headerInfo = l.split(":")
		if headerInfo[0] == "Set-Cookie":
			cookie = headerInfo[1].split(";")
			setCookie.append(cookie[0])
		elif headerInfo[0] == "Transfer-Encoding":
			if headerInfo[1] == " chunked":
				isChunked = True
		elif headerInfo[0] == "Content-Encoding":
			if headerInfo[1] == " gzip":
				isZipped = True

	if (isChunked):
		for l in lines:
			if l == "0":
				index = lines.index(l) - 1
				body = lines[index]
	
	else:
		body = lines[-1]
	
	# Unzip the body if necessary
	if isZipped:
		#print "BODY " + body
		body = unzip(body)

	# Parse through it looking for key items
	parser = PyHtmlParser()
	parser.feed(body)
	
# Go through the response and add the
# new location to the list of urls
def handleRedirectStatus(response):
	global unvisitedUrls
	global setCookie

	setCookie = []

	lines = response.split("\r\n")
	
	# Find location and set cookie header
	for l in lines:
		lineDetails = l.split(" ")
		if lineDetails[0] == "Location:":
			fullUrl = lineDetails[1]
			path = fullUrl[24:]
			unvisitedUrls.insert(0, path)
	 	elif lineDetails[0] == "Set-Cookie:":
			cookie = lineDetails[1].split(";")
			setCookie.append(cookie[0])
			
								
# Check starter tags for certain attributes
def checkAttributes(key, value, attrs):
	for attr in attrs:
		if attr[0] == key and (attr[1] == value or value == ""):
			return True
			
	return False

# Get an attribute value
def getStartTagAttributeValue(value, attrs):
	for attr in attrs:
		if attr[0] == value:
			return attr[1]

# Print the found flags
def printSecretFlags():
	global secretFlags
	for flag in secretFlags:
		print flag + "\n"

# Check if the url we found is one we need to
# crawl or not (if it begins with http://fring.ccs.neu.edu)
# return true if yes false, if no
def validateURL(url):
	global REQUIRED_DOMAIN

	if url == "/":
		return False

	if REQUIRED_DOMAIN in url or url[0] == "/":
		return True
	else:
		return False

# Set the CSRF token
def setCsrf(token):
	global csrfToken
	csrfToken = token
	#print "Setting token :" + csrfToken

# Find urls in HTML and add them
# to the list of urls to visit
def addUrlToUnvisitedList(attrs):
	global unvisitedUrls
	global visitedUrls
	
	for a in attrs:
		if a[0] == "href":
			if not a[1] in visitedUrls and not a[1] in unvisitedUrls:
				#print "Adding URL " + a[1]
				unvisitedUrls.append(a[1])
	
# Add the secret flag to the list
def addFlag(flag):
	global secretFlags
	secretFlags.append(flag)

# Handle html parsing
class PyHtmlParser(HTMLParser.HTMLParser):
	#global unvisitedUrls
	#global secretFlags
	global isLastStartTagFlag

	isLastStartTagFlag = False

	def handle_starttag(self, tag, attrs):
		#print "Encountered a start tag:", tag

		# Check for csrf value
		# If found, store it to use for login
		if tag == "input":
			if (checkAttributes("type", "hidden", attrs)) and (checkAttributes("name", "csrfmiddlewaretoken", attrs)) and (checkAttributes("value", "", attrs)):
				setCsrf(getStartTagAttributeValue("value", attrs))

		# Check for URLs
		# If one is found add it to unvisitedUrls list
		if tag == "a":
			if (checkAttributes("href", "", attrs)):
				addUrlToUnvisitedList(attrs)

		# Check for secret flags
		if tag == "h2":
			if checkAttributes("class", "secret_flag", attrs) and checkAttributes("style", "color:red", attrs):
				print "Found flag start tag"						
				isLastStartTagFlag = True

	def handle_data(self, data):
       		#print "Encountered some data  :", data
		if isLastStartTagFlag:
			print "This data is a flag" + data
			#addFlag(data)

# Unzip the response
def unzip(response):
	print "HERE" 

	print "RESPONSE "  + response

	unzippedResponse = ""

	#if os.path.isfile('file.txt.gz'):
	#	print "FILE STILL EXISTS"

	# create a gzip file with response
	fi = open('file.txt.gz', 'wb')
	fi.write(response)
	fi.close()

	# unzip the file
	with gzip.open('file.txt.gz', 'rb') as f:
		unzippedResponse = f.read()
		f.close()

	#if os.path.isfile('file.txt.gz'):
	#	print "removing file"
	#	os.remove('file.txt.gz')

	return unzippedResponse
	
# Get the user's username and password
# for fakebook
username = sys.argv[1];
password = sys.argv[2];

# URLs to search
unvisitedUrls = []
unvisitedUrls.append("/accounts/login/")

# Visited URLs
visitedUrls = []

# Current URL that is being requested
currentURL = ""

# Secret flags to find and print at end
secretFlags = []

# Keep track of set cookie values
setCookie = []

# Number of unvisited urls there are
numUnvisitedUrls = len(unvisitedUrls)

# Keep track of response from a request
fullResponse = ""

# Keep track if last start tag signifies a flag
isLastStartTagFlag = False

# Booleans for login sequence
loggedIn = False
postRequest = False

# While more urls to go through
while numUnvisitedUrls > 0:
	
	for x in unvisitedUrls:
		currentURL = x
		break;
	
	# if we have already visited this url
	# remove it and move on to the next
	if currentURL in visitedUrls:
		unvisitedUrls.remove(currentURL)
	
	# Check to make sure it is a link we need to check
	# meaning is from domain fring.ccs.neu.edu
	elif (not validateURL(currentURL)):
		unvisitedUrls.remove(currentURL)

	else:	
		# create a TCP socket
		sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

		# connect to fakebook
		server_address = (FAKEBOOK_HOST, PORT)
		sock.connect(server_address)
		
		if (not postRequest):
			# send GET request to fakebook
			message = setupGetRequest(currentURL)
		elif (postRequest):
			# send POST request to fakebook to login
			message = setupLoginRequest()
			
		sock.send(message)

		# get full response
		fullResponse = getResponse(sock)
	
		# print response
		print fullResponse

		# get the status code and handle each
		status = getStatus(fullResponse)
	
		# check the response after trying to login
		# if we got a 2xx or 3xx response then success
		if (postRequest and int(status) < 400):
			loggedIn = True
			postRequest = False
	
		# OK, 200 - parse response
		if int(status) == 200:
			handleOkStatus(fullResponse)
			
			# if we are not logged in yet, do this process
			if (not loggedIn):
				postRequest = True
				
		# Moved permanently, 301 - request new location
		elif int(status) >= 300 and int(status) < 400:
			handleRedirectStatus(fullResponse)
	
		# As long as the url does not need to be requested again
		# (any status except 500 including 403 and 404)
		# remove it from the unvisited list
		if int(status) != 500 and loggedIn:
			visitedUrls.append(currentURL)
	    		unvisitedUrls.remove(currentURL)
		
	# Update the number of urls left to visit
	numUnvisitedUrls = len(unvisitedUrls)
	#print numUnvisitedUrls

printSecretFlags()
