2025-01-12 21:36:58 -08:00
#!/usr/bin/python3
2025-01-12 22:31:01 -08:00
import asyncio , traceback , socket , ssl
2025-01-12 22:37:07 -08:00
from urllib . parse import urlparse
2025-01-13 16:27:15 -08:00
from flask import Flask , request , redirect , send_file , Response
2025-01-12 21:36:58 -08:00
from hypercorn . config import Config
from hypercorn . asyncio import serve
app = Flask ( __name__ )
@app.route ( " / " )
def root ( ) :
2025-01-12 22:00:24 -08:00
return """
< ! DOCTYPE html >
< html >
< head >
< meta charset = " UTF-8 " >
2025-01-13 15:55:48 -08:00
< link rel = " stylesheet " href = " /style.css " >
2025-01-12 22:00:24 -08:00
< title > Gem2Browser < / title >
< / head >
< body >
< center >
< h1 > Gem2Browser < / h1 >
< form action = " /gem " >
2025-01-13 15:55:48 -08:00
< p > gemini : / / < input class = " input " value = " geminiprotocol.net " type = " text " name = " gemini " > < / p > < br >
< input type = " submit " class = " go " value = " Go! " >
2025-01-12 22:00:24 -08:00
< / form >
< / center >
< / body >
< / html >
"""
2025-01-13 15:53:40 -08:00
@app.route ( " /external.png " )
def external ( ) :
return send_file ( " external.png " )
@app.route ( " /cross-server.png " )
2025-01-13 15:55:48 -08:00
def crosserver ( ) :
return send_file ( " cross-server.png " )
@app.route ( " /style.css " )
2025-01-13 16:00:57 -08:00
def style ( ) :
2025-01-13 16:13:58 -08:00
return send_file ( " style.css " )
2025-01-12 21:51:59 -08:00
@app.route ( " /gem " )
def relay ( ) :
url = request . args . get ( ' gemini ' )
if url == None :
return redirect ( " / " )
2025-01-12 22:00:24 -08:00
code = " <h1>Something went wrong...</h1> \n "
2025-01-12 21:36:58 -08:00
title = " Something went wrong... "
try :
2025-01-12 22:31:01 -08:00
gsocket = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
2025-01-12 22:37:07 -08:00
fulladdr = " gemini:// " + url
2025-01-12 22:39:23 -08:00
gemsocket = ssl . _create_unverified_context ( ) . wrap_socket ( gsocket , server_hostname = urlparse ( fulladdr ) . hostname )
2025-01-12 22:37:07 -08:00
gemsocket . connect ( ( urlparse ( fulladdr ) . hostname , 1965 ) )
2025-01-12 22:31:01 -08:00
gemsocket . send ( bytes ( " gemini:// " + url + " \r \n " , " UTF-8 " ) )
received = " "
while True :
gemresponse = gemsocket . recv ( 2048 )
2025-01-13 16:37:23 -08:00
try :
if gemresponse . decode ( ) != " " :
received + = gemresponse . decode ( )
else :
break
except :
2025-01-13 16:39:33 -08:00
return f ' <!DOCTYPE html> \n <html><head><meta charset= " UTF-8 " ><link rel= " stylesheet " href= " /style.css " ><title>Something went wrong...</title></head><body><h1>Something went wrong...</h1><p>The file that the Gemini server sent couldn \' t be decoded by Gem2Browser.</p></body></html> '
2025-01-12 22:31:01 -08:00
received = received . replace ( " \r " , " " )
firstline = True
redirected = False
gemtext = True
code = " "
for i in received . split ( " \n " ) :
2025-01-13 18:30:41 -08:00
i = i . replace ( " < " , " < " ) . replace ( " < " , " > " )
2025-01-12 22:31:01 -08:00
if firstline :
if i . split ( " " ) [ 0 ] [ 0 ] == " 3 " :
2025-01-12 22:43:10 -08:00
return redirect ( " /gem?gemini= " + i . split ( " " ) [ 1 ] [ 9 : ] )
2025-01-12 22:31:01 -08:00
elif i . split ( " " ) [ 0 ] [ 0 ] != " 2 " :
2025-01-13 16:19:28 -08:00
return f ' <!DOCTYPE html> \n <html><head><meta charset= " UTF-8 " ><link rel= " stylesheet " href= " /style.css " ><title>Something went wrong...</title></head><body><h1>Something went wrong...</h1><p>The specified Gemini server returned a status of: { i } </p></body></html> '
2025-01-12 22:31:01 -08:00
else :
firstline = False
2025-01-13 16:28:55 -08:00
if i . split ( " " ) [ 1 ] . split ( " ; " ) [ 0 ] != " text/gemini " :
2025-01-13 16:28:25 -08:00
print ( " Unrecognised type: " + i . split ( " " ) [ 1 ] )
2025-01-13 16:27:15 -08:00
return Response ( " " . join ( received . split ( " \n " ) [ 1 : ] ) , mimetype = i . split ( " " ) [ 1 ] )
2025-01-12 22:31:01 -08:00
else :
2025-01-13 15:28:42 -08:00
if i [ 0 : 2 ] == " # " :
2025-01-12 22:31:01 -08:00
if title == " Something went wrong... " :
title = i [ 2 : ]
temp = i [ 2 : ]
code + = f " <h1> { temp } </h1> \n "
2025-01-13 15:28:42 -08:00
elif i [ 0 : 3 ] == " ## " :
2025-01-12 22:31:01 -08:00
temp = i [ 3 : ]
code + = f " <h2> { temp } </h2> \n "
2025-01-13 15:28:42 -08:00
elif i [ 0 : 4 ] == " ### " :
2025-01-12 22:31:01 -08:00
temp = i [ 4 : ]
code + = f " <h3> { temp } </h3> \n "
2025-01-13 15:28:42 -08:00
elif i [ 0 : 2 ] == " * " :
2025-01-12 22:31:01 -08:00
temp = i [ 2 : ]
2025-01-13 15:53:40 -08:00
code + = f " <ul><li> { temp } </li></ul> \n "
elif i [ 0 : 2 ] == " => " :
2025-01-13 16:15:06 -08:00
temp = " " . join ( i [ 2 : ] . strip ( ) . replace ( " " , " " ) . split ( " " ) )
2025-01-13 15:53:40 -08:00
goto = temp . split ( " " ) [ 0 ]
prse = urlparse ( goto )
extra = " "
2025-01-13 16:17:08 -08:00
extracomment = goto
2025-01-13 15:53:40 -08:00
if prse . netloc == " " and prse . scheme == " " :
isdir = url [ len ( url ) - 1 ] == " / "
2025-01-13 16:35:53 -08:00
isabs = goto [ 0 ] == " / " or goto [ 0 : 2 ] == " // "
dubleslash = goto [ 0 : 2 ] == " // "
if isabs :
tempurl = urlparse ( fulladdr ) . hostname + " / " + ( goto [ 1 : ] if dubleslash else goto )
elif isdir :
2025-01-13 15:53:40 -08:00
tempurl = url + goto
else :
tempurl = " / " . join ( url . split ( " / " ) [ : - 1 ] ) + " / " + goto
goto = f " /gem?gemini= { tempurl } "
elif prse . scheme != " gemini " :
2025-01-13 16:22:55 -08:00
extra = " <img height= \" 19 \" src= \" /external.png \" > "
2025-01-13 16:17:08 -08:00
extracomment = f " This link points to an address that isn ' t gemini ( { prse . scheme } ) "
2025-01-13 16:13:58 -08:00
else :
if prse . hostname != urlparse ( fulladdr ) . hostname :
2025-01-13 16:22:55 -08:00
extra = " <img height= \" 19 \" src= \" /cross-server.png \" > "
2025-01-13 16:17:08 -08:00
extracomment = f " This link points to an address that isn ' t from the server you ' re currently connecting to ( { prse . hostname } ) "
2025-01-13 16:13:58 -08:00
goto = goto . replace ( " gemini:// " , " " )
goto = f " /gem?gemini= { goto } "
2025-01-13 15:53:40 -08:00
if temp . split ( " " ) == 1 :
comment = goto
else :
comment = " " . join ( temp . split ( " " ) [ 1 : ] )
2025-01-13 16:17:08 -08:00
code + = f " <p title= \" { extracomment } \" ><a href= \" { goto } \" > { comment } { extra } </a></p> \n "
2025-01-13 16:19:28 -08:00
elif i [ 0 : 2 ] == " > " :
code + = f " <p class= ' greentext ' > { i } </p> \n "
2025-01-12 22:31:01 -08:00
else :
2025-01-13 15:27:48 -08:00
code + = f " <p> { i } </p> \n "
2025-01-12 22:31:01 -08:00
if title == " Something went wrong... " :
title = " gemini:// " + url
2025-01-12 21:36:58 -08:00
except :
2025-01-13 16:24:58 -08:00
code + = " <pre> " + traceback . format_exc ( ) + " </pre> "
2025-01-13 15:55:48 -08:00
return f ' <!DOCTYPE html> \n <html><head><meta charset= " UTF-8 " ><link rel= " stylesheet " href= " /style.css " ><title> { title } </title></head><body> { code } </body></html> '
2025-01-12 21:36:58 -08:00
# Run the Hypercorn ASGI server
2025-01-12 21:49:36 -08:00
conf = Config ( )
conf . bind = " 0.0.0.0:2009 "
asyncio . run ( serve ( app , conf ) )