commit ee4d108b8abd15b7f3816260d0927ad3cc3d1536
parent f05af1e5e9433435c1c81dbdfd0e6b4ee89cfea6
Author: xfnw <xfnw@ttm.sh>
Date:   Thu, 24 Dec 2020 15:40:17 -0500
use spaces instead of tabs
Diffstat:
| M | gem.css |  |  | 42 | +++++++++++++++++++++--------------------- | 
| M | gem2html.py |  |  | 140 | ++++++++++++++++++++++++++++++++++++++++---------------------------------------- | 
| M | index.cgi |  |  | 274 | ++++++++++++++++++++++++++++++++++++++++---------------------------------------- | 
| M | mimeparse.py |  |  | 118 | ++++++++++++++++++++++++++++++++++++++++---------------------------------------- | 
4 files changed, 287 insertions(+), 287 deletions(-)
diff --git a/gem.css b/gem.css
@@ -10,7 +10,7 @@ padding: 10px;
 }
 
 h1, h2, h3 {
-	font-weight: 100;
+    font-weight: 100;
 }
 
 a {
@@ -34,54 +34,54 @@ overflow:auto;
 }
 
 mark {
-	background-color: #dc5;
+    background-color: #dc5;
 }
 
 @font-face {
-	font-family: 'VT323';
-	src: url(https://xfnw.ttm.sh/assets/PressStart2P-Regular.ttf);
+    font-family: 'VT323';
+    src: url(https://xfnw.ttm.sh/assets/PressStart2P-Regular.ttf);
 }
 
 
 summary {
-	cursor: pointer;
-	padding: 10px;
+    cursor: pointer;
+    padding: 10px;
 }
 
 th, summary {
-	background-color: #222;
+    background-color: #222;
 }
 
 th, td, details {
-	border: 1px solid #222;
+    border: 1px solid #222;
 }
 th, td {
-	padding: 5px;
+    padding: 5px;
 }
 
 table, details *:not(summary) {
-	border-collapse: collapse;
-	margin: 10px;
+    border-collapse: collapse;
+    margin: 10px;
 }
 
 .flex {
-	display: flex;
-	flex-wrap: wrap;
+    display: flex;
+    flex-wrap: wrap;
 }
 
 .flex div {
-	display:flex;
-	min-width: 10em;
-	width: 100%;
-	margin: 10px;
-	flex: 1 1 0;
-	flex-direction: column;
+    display:flex;
+    min-width: 10em;
+    width: 100%;
+    margin: 10px;
+    flex: 1 1 0;
+    flex-direction: column;
 }
 
 .flex div h3 {
-	margin: 0;
+    margin: 0;
 }
 .flex div p {
-	flex: 1 1 0;
+    flex: 1 1 0;
 }
 
diff --git a/gem2html.py b/gem2html.py
@@ -6,17 +6,17 @@ _rand_n = lambda: functools.reduce(lambda x, y: (x<<8)+y,os.urandom(4))
 ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"
 USED_IDS = set()
 def rand_id():
-	n = _rand_n()
-	id = ""
-	while n>0:
-		n, index = divmod(n,len(ALPHABET))
-		id = ALPHABET[index]+id
-	if id in USED_IDS: return rand_id()
-	return id
+    n = _rand_n()
+    id = ""
+    while n>0:
+        n, index = divmod(n,len(ALPHABET))
+        id = ALPHABET[index]+id
+    if id in USED_IDS: return rand_id()
+    return id
 
 def gem2html(content,link_callback=lambda url, text: (url, text)):
-	lines = content.splitlines()
-	out = """<!DOCTYPE html>
+    lines = content.splitlines()
+    out = """<!DOCTYPE html>
 <html>
 <head>
   <meta charset="UTF-8">
@@ -25,64 +25,64 @@ def gem2html(content,link_callback=lambda url, text: (url, text)):
 <body>
 
         """
-	pre = False
-	pre_alt = False
-	set_title = False
-	for line in lines:
-		if pre:
-			if line[:3]=="```":
-				pre=False
-				out+="</pre>\n"
-				if pre_alt:
-					out+="</figure>\n"
-					pre_alt=False
-			else:
-				out+=escape(line)+"\n"
-		else:
-			if line[:3]=="```":
-				if len(line)>3:
-					cap_id = rand_id()
-					out+="<figure role='img' aria-captionedby='{0}'><figcaption id='{0}' style='clip: rect(0 0 0 0); clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px;'>{1}</figcaption>\n".format(cap_id,escape(line[3:]))
-					pre_alt = True
-				pre = True
-				out+="<pre>\n"
-			elif line.startswith("#"):
-				if line[:3]=="###":
-					out+="<h3>{}</h3>".format(escape(line[3:].strip()))
-				elif line[:2]=="##":
-					out+="<h2>{}</h2>".format(escape(line[2:].strip()))
-				elif line[:1]=="#":
-					out+="<h1>{}</h1>".format(escape(line[1:].strip()))
-					if not set_title:
-						out+="<title>{}</title>".format(escape(line[1:].strip()))
-						set_title = True
-			elif line.startswith("* "):
-				out += "<ul>\n<li>{}</li>\n</ul>\n".format(escape(line[1:].strip()))
-				# combine consecutive unordered list items into one unordered list
-				out = out.replace("</ul>\n<ul>\n","")
-			elif line.startswith("=>"):
-				parts = line.split(None,2)
-				try:
-					url, text = parts[1:]
-				except ValueError:
-					try:
-						url=parts[1]
-						text=parts[1]
-					except:
-						# no link content at all
-						# just put a literal => in there
-						out+="<p></p>".format(escape(parts[0]))
-						continue
-				# now comes the fun part, use the link callback to mutilate these
-				url, text = link_callback(url, text)
-				# and now render
-				out+="<p><a href='{}'>{}</a></p>".format(escape(url),escape(text))
-			elif line.startswith(">"):
-				out+="<blockquote><p>{}</p></blockquote>".format(escape(line))
-			else: # any other line is a text line
-				if line:
-					out+="<p>{}</p>".format(escape(line))
-				else:
-					out+="<p><br></p>"
-	out+="</body>"
-	return out
+    pre = False
+    pre_alt = False
+    set_title = False
+    for line in lines:
+        if pre:
+            if line[:3]=="```":
+                pre=False
+                out+="</pre>\n"
+                if pre_alt:
+                    out+="</figure>\n"
+                    pre_alt=False
+            else:
+                out+=escape(line)+"\n"
+        else:
+            if line[:3]=="```":
+                if len(line)>3:
+                    cap_id = rand_id()
+                    out+="<figure role='img' aria-captionedby='{0}'><figcaption id='{0}' style='clip: rect(0 0 0 0); clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px;'>{1}</figcaption>\n".format(cap_id,escape(line[3:]))
+                    pre_alt = True
+                pre = True
+                out+="<pre>\n"
+            elif line.startswith("#"):
+                if line[:3]=="###":
+                    out+="<h3>{}</h3>".format(escape(line[3:].strip()))
+                elif line[:2]=="##":
+                    out+="<h2>{}</h2>".format(escape(line[2:].strip()))
+                elif line[:1]=="#":
+                    out+="<h1>{}</h1>".format(escape(line[1:].strip()))
+                    if not set_title:
+                        out+="<title>{}</title>".format(escape(line[1:].strip()))
+                        set_title = True
+            elif line.startswith("* "):
+                out += "<ul>\n<li>{}</li>\n</ul>\n".format(escape(line[1:].strip()))
+                # combine consecutive unordered list items into one unordered list
+                out = out.replace("</ul>\n<ul>\n","")
+            elif line.startswith("=>"):
+                parts = line.split(None,2)
+                try:
+                    url, text = parts[1:]
+                except ValueError:
+                    try:
+                        url=parts[1]
+                        text=parts[1]
+                    except:
+                        # no link content at all
+                        # just put a literal => in there
+                        out+="<p></p>".format(escape(parts[0]))
+                        continue
+                # now comes the fun part, use the link callback to mutilate these
+                url, text = link_callback(url, text)
+                # and now render
+                out+="<p><a href='{}'>{}</a></p>".format(escape(url),escape(text))
+            elif line.startswith(">"):
+                out+="<blockquote><p>{}</p></blockquote>".format(escape(line))
+            else: # any other line is a text line
+                if line:
+                    out+="<p>{}</p>".format(escape(line))
+                else:
+                    out+="<p><br></p>"
+    out+="</body>"
+    return out
diff --git a/index.cgi b/index.cgi
@@ -6,94 +6,94 @@ uses_relative.append("gemini")
 uses_netloc.append("gemini")
 
 class StartComparison(str):
-	def __eq__(self,lhs):
-		return lhs.startswith(self)
+    def __eq__(self,lhs):
+        return lhs.startswith(self)
 
 class ResponseCodes:
-	# use like: response.status==ResponseCodes.GENERIC_SUCCESS
-	GENERIC_INPUT = StartComparison("1")
-	GENERIC_SUCCESS = StartComparison("2")
-	GENERIC_REDIRECT = StartComparison("3")
-	GENERIC_TEMPFAIL = StartComparison("4")
-	GENERIC_PERMFAIL = StartComparison("5")
-	GENERIC_CERTFAIL = StartComparison("6")
+    # use like: response.status==ResponseCodes.GENERIC_SUCCESS
+    GENERIC_INPUT = StartComparison("1")
+    GENERIC_SUCCESS = StartComparison("2")
+    GENERIC_REDIRECT = StartComparison("3")
+    GENERIC_TEMPFAIL = StartComparison("4")
+    GENERIC_PERMFAIL = StartComparison("5")
+    GENERIC_CERTFAIL = StartComparison("6")
 
-	# the rest of these are just normal strings
-	INPUT_NEEDED = "10"
-	INPUT_NEEDED_SENSITIVE = "11"
+    # the rest of these are just normal strings
+    INPUT_NEEDED = "10"
+    INPUT_NEEDED_SENSITIVE = "11"
 
-	SUCCESS = "20"
+    SUCCESS = "20"
 
-	REDIRECT_TEMP = "30"
-	REDIRECT_PERM = "31"
+    REDIRECT_TEMP = "30"
+    REDIRECT_PERM = "31"
 
-	TEMPFAIL_GENERIC = "40"
-	TEMPFAIL_SERVER_UNAVAILABLE = "41"
-	TEMPFAIL_CGI_ERROR = "42"
-	TEMPFAIL_PROXY_ERROR = "43"
-	TEMPFAIL_SLOW_DOWN = "44"
+    TEMPFAIL_GENERIC = "40"
+    TEMPFAIL_SERVER_UNAVAILABLE = "41"
+    TEMPFAIL_CGI_ERROR = "42"
+    TEMPFAIL_PROXY_ERROR = "43"
+    TEMPFAIL_SLOW_DOWN = "44"
 
-	PERMFAIL_GENERIC = "50"
-	PERMFAIL_NOT_FOUND = "51"
-	PERMFAIL_GONE = "52"
-	PERMFAIL_PROXY_REFUSED = "53"
-	PERMFAIL_BAD_REQUEST = "59"
+    PERMFAIL_GENERIC = "50"
+    PERMFAIL_NOT_FOUND = "51"
+    PERMFAIL_GONE = "52"
+    PERMFAIL_PROXY_REFUSED = "53"
+    PERMFAIL_BAD_REQUEST = "59"
 
-	CERTFAIL_NEED_CERT = "60"
-	CERTFAIL_BAD_CERT = "61"
-	CERTFAIL_INVALID_CERT = "62"
+    CERTFAIL_NEED_CERT = "60"
+    CERTFAIL_BAD_CERT = "61"
+    CERTFAIL_INVALID_CERT = "62"
 
 class ResponseObject:
-	def __init__(self,status,meta,content):
-		self.status=status
-		self.meta=meta
-		self.content=content
+    def __init__(self,status,meta,content):
+        self.status=status
+        self.meta=meta
+        self.content=content
 
 def connect_factory(url,parsed=None):
-	if parsed is None: parsed = urlparse(url)
-	event = threading.Event()
-	def _connect():
-		ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
-		ctx.minimum_version = ssl.TLSVersion.TLSv1_2
-		ctx.check_hostname = False
-		ctx.verify_mode = ssl.CERT_NONE
-		try:
-			sock = socket.create_connection((parsed.hostname,parsed.port or 1965),2)
-		except socket.timeout:
-			threading.current_thread().ret="Socket connection timed out"
-			return
-		sock.settimeout(5)
-		ssock = ctx.wrap_socket(sock,server_hostname=parsed.hostname)
-		ssock.sendall((url+"\r\n").encode("utf-8"))
-		out = b''
-		try:
-			while (data:=ssock.recv(1024)) and not event.is_set():
-				out+=data
-		except socket.timeout:
-			threading.current_thread().ret="Read timed out, the page is probably too big."
-			return
-		ssock.shutdown(socket.SHUT_RDWR)
-		ssock.close()
-		header, data = out.split(b"\n",1)
-		header = header.strip().decode("utf-8")
-		status, meta = header.split(None,1)
-		threading.current_thread().ret = ResponseObject(status,meta,data)
-	return _connect, event
+    if parsed is None: parsed = urlparse(url)
+    event = threading.Event()
+    def _connect():
+        ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+        ctx.minimum_version = ssl.TLSVersion.TLSv1_2
+        ctx.check_hostname = False
+        ctx.verify_mode = ssl.CERT_NONE
+        try:
+            sock = socket.create_connection((parsed.hostname,parsed.port or 1965),2)
+        except socket.timeout:
+            threading.current_thread().ret="Socket connection timed out"
+            return
+        sock.settimeout(5)
+        ssock = ctx.wrap_socket(sock,server_hostname=parsed.hostname)
+        ssock.sendall((url+"\r\n").encode("utf-8"))
+        out = b''
+        try:
+            while (data:=ssock.recv(1024)) and not event.is_set():
+                out+=data
+        except socket.timeout:
+            threading.current_thread().ret="Read timed out, the page is probably too big."
+            return
+        ssock.shutdown(socket.SHUT_RDWR)
+        ssock.close()
+        header, data = out.split(b"\n",1)
+        header = header.strip().decode("utf-8")
+        status, meta = header.split(None,1)
+        threading.current_thread().ret = ResponseObject(status,meta,data)
+    return _connect, event
 
 BASE_URL = "https://xfnw.ttm.sh/gem/?"
 def link_callback(lurl, text):
-	global url
-	lurl = urljoin(url,lurl)
-	parsed = urlparse(lurl)
-	query = None
-	if parsed.query:
-		query = parsed.query
-		parsed = parsed._replace(query=None)
-		lurl = urlunparse(parsed)
-	params = dict(addr=lurl)
-	if query is not None:
-		params["query"]=query
-	return BASE_URL+urlencode(params), text
+    global url
+    lurl = urljoin(url,lurl)
+    parsed = urlparse(lurl)
+    query = None
+    if parsed.query:
+        query = parsed.query
+        parsed = parsed._replace(query=None)
+        lurl = urlunparse(parsed)
+    params = dict(addr=lurl)
+    if query is not None:
+        params["query"]=query
+    return BASE_URL+urlencode(params), text
 
 qs = parse_qs(os.environ["QUERY_STRING"])
 
@@ -101,13 +101,13 @@ url = next(iter(qs.get("addr",["gemini://tilde.team/~xfnw/start.gmi"])))
 parsed = urlparse(url)
 query = next(iter(qs.get("query",[None]))) or parsed.query
 if query:
-	parsed = parsed._replace(query=query)
-	url = urlunparse(parsed)
+    parsed = parsed._replace(query=query)
+    url = urlunparse(parsed)
 
 if parsed.scheme and parsed.scheme!="gemini":
-	print("Content-Type: text/html")
-	print()
-	print("""<!DOCTYPE html>
+    print("Content-Type: text/html")
+    print()
+    print("""<!DOCTYPE html>
 <html>
 <head>
 <meta http-equiv="refresh" content="0;URL='{0}'">
@@ -120,7 +120,7 @@ if parsed.scheme and parsed.scheme!="gemini":
 <p>If the redirect doesn't work, <a href="{0}">click here.</a></p>
 </body>
 </html>""".format(escape(url)))
-	sys.exit()
+    sys.exit()
 
 connect_func, killswitch = connect_factory(url,parsed)
 connect_thread = threading.Thread(target=connect_func)
@@ -130,13 +130,13 @@ connect_thread.start()
 connect_thread.join(5)
 # now if the thread's still alive, we'll set the killswitch and join the thread again
 if connect_thread.is_alive():
-	killswitch.set()
-	connect_thread.join()
+    killswitch.set()
+    connect_thread.join()
 # now we know for a fact the thread is done
 # get the return value as Thread.ret (done manually in the function definition)
 response = getattr(connect_thread,"ret")
 if type(response)!=ResponseObject:
-	print("""Content-Type: text/html
+    print("""Content-Type: text/html
 
 <!DOCTYPE html>
 <html>
@@ -148,13 +148,13 @@ if type(response)!=ResponseObject:
 </head>
 <body>
 <pre>""")
-	print(response or "Unknown error occurred.")
-	sys.exit()
+    print(response or "Unknown error occurred.")
+    sys.exit()
 
 if response.status==ResponseCodes.GENERIC_INPUT:
-	print("Content-Type: text/html")
-	print()
-	print("""<!DOCTYPE html>
+    print("Content-Type: text/html")
+    print()
+    print("""<!DOCTYPE html>
         <html>
         <head>
           <meta charset="UTF-8">
@@ -172,28 +172,28 @@ if response.status==ResponseCodes.GENERIC_INPUT:
 </form>
 <p>I take no responsibility if shoulder-surfers read your input.</p>""".format(escape(response.meta),"password" if response.status==ResponseCodes.INPUT_NEEDED_SENSITIVE else "text",escape(url)))
 elif response.status==ResponseCodes.GENERIC_SUCCESS:
-	mime = response.meta
-	content = response.content
-	mimeparsed = mimeparse.parse_mime(mime)
-	if mimeparsed[0][0]=="text": # mimeparsed is ([type, subtype],parameters)
-		content = content.decode(mimeparsed[1].get("charset","UTF-8"))
-		if mimeparsed[0][1]=="html":
-			mime = mime.replace("text/html","text/plain")
-		elif mimeparsed[0][1]=="gemini":
-			mime = "text/html; charset=UTF-8"
-			content = gem2html.gem2html(content,link_callback)
-		print("Content-Type: "+mime)
-		print("")
-		print(content)
-	else:
-		print("Content-Type: "+mime)
-		print()
-		sys.stdout.flush()
-		sys.stdout.buffer.write(content)
+    mime = response.meta
+    content = response.content
+    mimeparsed = mimeparse.parse_mime(mime)
+    if mimeparsed[0][0]=="text": # mimeparsed is ([type, subtype],parameters)
+        content = content.decode(mimeparsed[1].get("charset","UTF-8"))
+        if mimeparsed[0][1]=="html":
+            mime = mime.replace("text/html","text/plain")
+        elif mimeparsed[0][1]=="gemini":
+            mime = "text/html; charset=UTF-8"
+            content = gem2html.gem2html(content,link_callback)
+        print("Content-Type: "+mime)
+        print("")
+        print(content)
+    else:
+        print("Content-Type: "+mime)
+        print()
+        sys.stdout.flush()
+        sys.stdout.buffer.write(content)
 elif response.status==ResponseCodes.GENERIC_REDIRECT:
-	print("Content-Type: text/html")
-	print()
-	print("""<!DOCTYPE html>
+    print("Content-Type: text/html")
+    print()
+    print("""<!DOCTYPE html>
 <html>
 <head>
 <title>Redirecting...</title>
@@ -203,11 +203,11 @@ elif response.status==ResponseCodes.GENERIC_REDIRECT:
 </head>
 <body>
 """)
-	print("<p>The current page ({}) wishes to redirect you to {}.</p>".format(escape(url),escape(response.meta)))
-	tmp, tmp2 = link_callback(response.meta,"Click here to follow this redirect.")
-	print("<p><a href={}>{}</a></p>".format(escape(tmp),escape(tmp2)))
+    print("<p>The current page ({}) wishes to redirect you to {}.</p>".format(escape(url),escape(response.meta)))
+    tmp, tmp2 = link_callback(response.meta,"Click here to follow this redirect.")
+    print("<p><a href={}>{}</a></p>".format(escape(tmp),escape(tmp2)))
 elif response.status==ResponseCodes.GENERIC_TEMPFAIL or response.status==ResponseCodes.GENERIC_PERMFAIL:
-	print("""Content-Type: text/html
+    print("""Content-Type: text/html
 
 <!DOCTYPE html>
 <html>
@@ -219,27 +219,27 @@ elif response.status==ResponseCodes.GENERIC_TEMPFAIL or response.status==Respons
 </head>
 <body>
 <pre>""")
-	msg = "Unknown {} error".format("permanent" if response.status==ResponseCodes.GENERIC_PERMFAIL else "temporary")
-	if response.status==ResponseCodes.TEMPFAIL_SERVER_UNAVAILABLE:
-		msg = "Server unavailable"
-	if response.status==ResponseCodes.TEMPFAIL_CGI_ERROR:
-		msg = "CGI script error"
-	if response.status==ResponseCodes.TEMPFAIL_PROXY_ERROR:
-		msg = "Proxy error"
-	if response.status==ResponseCodes.TEMPFAIL_SLOW_DOWN:
-		msg = "Slow down"
-	if response.status==ResponseCodes.PERMFAIL_NOT_FOUND:
-		msg = "Not Found"
-	if response.status==ResponseCodes.PERMFAIL_GONE:
-		msg = "Gone"
-	if response.status==ResponseCodes.PERMFAIL_PROXY_REFUSED:
-		msg = "Proxy request refused"
-	if response.status==ResponseCodes.PERMFAIL_BAD_REQUEST:
-		msg = "Bad request"
-	print(f"Error {response.status}: {msg}")
-	print(f"Server says: {response.meta}")
+    msg = "Unknown {} error".format("permanent" if response.status==ResponseCodes.GENERIC_PERMFAIL else "temporary")
+    if response.status==ResponseCodes.TEMPFAIL_SERVER_UNAVAILABLE:
+        msg = "Server unavailable"
+    if response.status==ResponseCodes.TEMPFAIL_CGI_ERROR:
+        msg = "CGI script error"
+    if response.status==ResponseCodes.TEMPFAIL_PROXY_ERROR:
+        msg = "Proxy error"
+    if response.status==ResponseCodes.TEMPFAIL_SLOW_DOWN:
+        msg = "Slow down"
+    if response.status==ResponseCodes.PERMFAIL_NOT_FOUND:
+        msg = "Not Found"
+    if response.status==ResponseCodes.PERMFAIL_GONE:
+        msg = "Gone"
+    if response.status==ResponseCodes.PERMFAIL_PROXY_REFUSED:
+        msg = "Proxy request refused"
+    if response.status==ResponseCodes.PERMFAIL_BAD_REQUEST:
+        msg = "Bad request"
+    print(f"Error {response.status}: {msg}")
+    print(f"Server says: {response.meta}")
 elif response.status==ResponseCodes.GENERIC_CERTFAIL:
-	print("""Content-Type: text/html
+    print("""Content-Type: text/html
 
 <!DOCTYPE html>
 <html>
@@ -251,9 +251,9 @@ elif response.status==ResponseCodes.GENERIC_CERTFAIL:
 </head>
 <body>
 <pre>""")
-	print("Page requires the use of client certificates, which are outside the scope of this proxy.")
+    print("Page requires the use of client certificates, which are outside the scope of this proxy.")
 else:
-	print("""Content-Type: text/html
+    print("""Content-Type: text/html
 
 <!DOCTYPE html>
 <html>
@@ -265,7 +265,7 @@ else:
 </head>
 <body>
 <pre>""")
-	print("Page returned status code {} which is unimplemented.".format(response.status))
-	print("META = {!r}".format(response.meta))
-	print("Response body = {!r}".format(response.content))
+    print("Page returned status code {} which is unimplemented.".format(response.status))
+    print("META = {!r}".format(response.meta))
+    print("Response body = {!r}".format(response.content))
 
diff --git a/mimeparse.py b/mimeparse.py
@@ -1,62 +1,62 @@
 import string
 # Utility function to parse a MIME type
 def parse_mime(mimetype):
-	mimetype = mimetype.strip()
-	index = 0
-	type = ""
-	# type is everything before the /
-	while index<len(mimetype) and mimetype[index]!="/":
-		type+=mimetype[index]
-		index+=1
-	index+=1
-	subtype = ""
-	# subtype is everything after the slash and before the semicolon (if the latter exists)
-	while index<len(mimetype) and mimetype[index]!=";":
-		subtype+=mimetype[index]
-		index+=1
-	index+=1
-	# if there's no semicolon, there are no params
-	if index>=len(mimetype): return [type,subtype], dict()
-	params = dict()
-	while index<len(mimetype):
-		# skip whitespace
-		while index<len(mimetype) and mimetype[index] in string.whitespace:
-			index+=1
-		paramName = ""
-		# the parameter name is everything before the = or ;
-		while index<len(mimetype) and mimetype[index] not in "=;":
-			paramName+=mimetype[index]
-			index+=1
-		# if the string is over or there isn't an equals sign, there's no param value
-		if index>=len(mimetype) or mimetype[index]==";":
-			index+=1
-			params[paramName]=None
-			continue
-		# otherwise, grab the param value
-		index+=1
-		paramValue = ""
-		if mimetype[index]=='"':
-			index+=1
-			while True:
-				while index<len(mimetype) and mimetype[index] not in '\\"':
-					paramValue+=mimetype[index]
-					index+=1
-				if index>=len(mimetype): break
-				c = mimetype[index]
-				index+=1
-				if c=="\\":
-					if index>=len(mimetype):
-						paramValue+=c
-						break
-					paramValue+=mimetype[index]
-					index+=1
-				else:
-					break
-			# skip until next ;
-			while index<len(mimetype) and mimetype[index]!=";": index+=1
-		else:
-			while index<len(mimetype) and mimetype[index]!=";":
-				paramValue+=mimetype[index]
-				index+=1
-		if paramName: params[paramName]=paramValue
-	return [type, subtype], params
+    mimetype = mimetype.strip()
+    index = 0
+    type = ""
+    # type is everything before the /
+    while index<len(mimetype) and mimetype[index]!="/":
+        type+=mimetype[index]
+        index+=1
+    index+=1
+    subtype = ""
+    # subtype is everything after the slash and before the semicolon (if the latter exists)
+    while index<len(mimetype) and mimetype[index]!=";":
+        subtype+=mimetype[index]
+        index+=1
+    index+=1
+    # if there's no semicolon, there are no params
+    if index>=len(mimetype): return [type,subtype], dict()
+    params = dict()
+    while index<len(mimetype):
+        # skip whitespace
+        while index<len(mimetype) and mimetype[index] in string.whitespace:
+            index+=1
+        paramName = ""
+        # the parameter name is everything before the = or ;
+        while index<len(mimetype) and mimetype[index] not in "=;":
+            paramName+=mimetype[index]
+            index+=1
+        # if the string is over or there isn't an equals sign, there's no param value
+        if index>=len(mimetype) or mimetype[index]==";":
+            index+=1
+            params[paramName]=None
+            continue
+        # otherwise, grab the param value
+        index+=1
+        paramValue = ""
+        if mimetype[index]=='"':
+            index+=1
+            while True:
+                while index<len(mimetype) and mimetype[index] not in '\\"':
+                    paramValue+=mimetype[index]
+                    index+=1
+                if index>=len(mimetype): break
+                c = mimetype[index]
+                index+=1
+                if c=="\\":
+                    if index>=len(mimetype):
+                        paramValue+=c
+                        break
+                    paramValue+=mimetype[index]
+                    index+=1
+                else:
+                    break
+            # skip until next ;
+            while index<len(mimetype) and mimetype[index]!=";": index+=1
+        else:
+            while index<len(mimetype) and mimetype[index]!=";":
+                paramValue+=mimetype[index]
+                index+=1
+        if paramName: params[paramName]=paramValue
+    return [type, subtype], params