summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--browser.py125
-rw-r--r--gemini.py112
2 files changed, 119 insertions, 118 deletions
diff --git a/browser.py b/browser.py
index 9d1d421..e0868b3 100644
--- a/browser.py
+++ b/browser.py
@@ -1,118 +1,8 @@
import sys
-import socket
-import ssl
from PySide2 import QtCore, QtWidgets
-def htmlescape(text):
- return text.replace('<', '&lt;').replace('>', '&gt;')
-
-def gem2html(gem):
- html = []
- state = 'text'
- blanklines = 0
- for line in gem.split('\n'):
- if line.startswith('```'):
- if state == 'pre':
- newstate = 'text'
- blanklines = 0
- else:
- newstate = 'pre'
- elif state == 'pre':
- newstate = 'pre'
- elif line.startswith('=>'):
- newstate = 'links'
- elif line.startswith('* '):
- newstate = 'list'
- else:
- newstate = 'text'
-
- if state != 'pre':
- if len(line.strip()) == 0:
- blanklines += 1
- if blanklines > 1:
- html.append('<br/>')
- continue
- blanklines = 0
-
- if state != newstate:
- if state in ('links', 'list'):
- html.append('</ul>')
- elif state == 'pre':
- html.append('</pre>')
-
- if newstate in ('links', 'list'):
- html.append('<ul>')
- elif newstate == 'pre':
- html.append('<pre>')
- state = newstate
-
- if line.startswith('```'):
- pass
- elif state == 'links':
- tokens = line.split(None, 2)
- if len(tokens) == 3:
- _, url, text = tokens
- html.append('<li><a href="{url}">{text}</a></li>'.format(url=url, text=text))
- else:
- _, url = tokens
- html.append('<li><a href="{url}">{url}</a></li>'.format(url=url))
- elif state == 'list':
- html.append('<li>{}</li>'.format(line[2:]))
- elif state == 'pre':
- html.append(line)
- else:
- if line.startswith('###'):
- html.append('<h3>{}</h3>'.format(line[3:].lstrip()))
- elif line.startswith('##'):
- html.append('<h2>{}</h2>'.format(line[2:].lstrip()))
- elif line.startswith('#'):
- html.append('<h1>{}</h1>'.format(line[1:].lstrip()))
- else:
- html.append('<p>{}</p>'.format(htmlescape(line)))
- return '\n'.join(html)
-
-def absolute_url(base, url):
- """
- modifies `url` in place
- """
- if not url.scheme():
- url.setScheme(base.scheme())
- if not url.host():
- url.setHost(base.host())
- if not url.path().startswith('/'):
- url.setPath(base.path().rsplit('/', 1)[0] + '/' + url.path())
- if url.port() == -1:
- url.setPort(1965)
- return url
-
-def gem_get(url):
- if len(url.path()) == 0:
- url.setPath('/')
- return {
- 'status': '32',
- 'meta': url.toDisplayString(),
- }
- context = ssl.create_default_context()
- context.check_hostname = False
- context.verify_mode = ssl.CERT_NONE
- with socket.create_connection((url.host(), url.port())) as sock:
- with context.wrap_socket(sock, server_hostname=url.host()) as ssock:
- ssock.sendall('gemini://{}{}\r\n'.format(url.host(), url.path()).encode('utf8'))
- fp = ssock.makefile(mode='rb')
- header = fp.readline(1027)
- status, meta = header.decode('utf8').split(None, 1)
- if status[0] != '2':
- return {
- 'status': status,
- 'meta': meta.strip(),
- }
- body = fp.read()
- return {
- 'status': status,
- 'meta': meta.strip(),
- 'body': body.decode('utf8'),
-}
+import gemini
class GViewport(QtWidgets.QTextBrowser):
@@ -128,9 +18,8 @@ class GViewport(QtWidgets.QTextBrowser):
def mouseMoveEvent(self, event):
cur = self.cursorForPosition(event.localPos().toPoint())
hover_url = cur.charFormat().anchorHref()
- hover_url = absolute_url(self._current_url, QtCore.QUrl(hover_url))
+ hover_url = gemini.absolute_url(self._current_url, QtCore.QUrl(hover_url))
if hover_url != self._hover_url:
- print(hover_url)
self._hover_url = hover_url
self.hoverUrlChanged.emit(self._hover_url.toString())
return super().mouseMoveEvent(event)
@@ -147,9 +36,9 @@ class GViewport(QtWidgets.QTextBrowser):
url.setPort(1965)
if not url.path().startswith('/'):
url.setPath(self._current_url.path().rsplit('/', 1)[0] + '/' + url.path())
- gem = gem_get(absolute_url(self._current_url, url))
+ gem = gemini.get(gemini.absolute_url(self._current_url, url))
if 'body' in gem:
- html = gem2html(gem['body'])
+ html = gemini.gem2html(gem['body'])
else:
html = '<h1>{} {}</h1>'.format(gem['status'], gem['meta'])
self._current_url = url
@@ -158,13 +47,13 @@ class GViewport(QtWidgets.QTextBrowser):
def setSource(self, url):
if url.scheme() != 'gemini':
return
- gem = gem_get(absolute_url(self._current_url, url))
+ gem = gemini.get(gemini.absolute_url(self._current_url, url))
while gem['status'][0] == '3':
url = QtCore.QUrl(gem['meta'])
if url.port() == 1965:
url.setPort(-1)
print('redirect: {}'.format(url))
- gem = gem_get(absolute_url(self._current_url, url))
+ gem = gemini.get(gemini.absolute_url(self._current_url, url))
self._last_redirect = (url, gem)
if url.port() == 1965:
url.setPort(-1)
@@ -185,7 +74,7 @@ class GUrlBar(QtWidgets.QLineEdit):
class GBrowser(QtWidgets.QMainWindow):
def __init__(self):
- super(GBrowser, self).__init__()
+ QtWidgets.QMainWindow.__init__(self)
self.initUI()
def initUI(self):
diff --git a/gemini.py b/gemini.py
new file mode 100644
index 0000000..c0d09c1
--- /dev/null
+++ b/gemini.py
@@ -0,0 +1,112 @@
+import socket
+import ssl
+
+def htmlescape(text):
+ return text.replace('<', '&lt;').replace('>', '&gt;')
+
+def gem2html(gem):
+ html = []
+ state = 'text'
+ blanklines = 0
+ for line in gem.split('\n'):
+ if line.startswith('```'):
+ if state == 'pre':
+ newstate = 'text'
+ blanklines = 0
+ else:
+ newstate = 'pre'
+ elif state == 'pre':
+ newstate = 'pre'
+ elif line.startswith('=>'):
+ newstate = 'links'
+ elif line.startswith('* '):
+ newstate = 'list'
+ else:
+ newstate = 'text'
+
+ if state != 'pre':
+ if len(line.strip()) == 0:
+ blanklines += 1
+ if blanklines > 1:
+ html.append('<br/>')
+ continue
+ blanklines = 0
+
+ if state != newstate:
+ if state in ('links', 'list'):
+ html.append('</ul>')
+ elif state == 'pre':
+ html.append('</pre>')
+
+ if newstate in ('links', 'list'):
+ html.append('<ul>')
+ elif newstate == 'pre':
+ html.append('<pre>')
+ state = newstate
+
+ if line.startswith('```'):
+ pass
+ elif state == 'links':
+ tokens = line.split(None, 2)
+ if len(tokens) == 3:
+ _, url, text = tokens
+ html.append('<li><a href="{url}">{text}</a></li>'.format(url=url, text=text))
+ else:
+ _, url = tokens
+ html.append('<li><a href="{url}">{url}</a></li>'.format(url=url))
+ elif state == 'list':
+ html.append('<li>{}</li>'.format(line[2:]))
+ elif state == 'pre':
+ html.append(line)
+ else:
+ if line.startswith('###'):
+ html.append('<h3>{}</h3>'.format(line[3:].lstrip()))
+ elif line.startswith('##'):
+ html.append('<h2>{}</h2>'.format(line[2:].lstrip()))
+ elif line.startswith('#'):
+ html.append('<h1>{}</h1>'.format(line[1:].lstrip()))
+ else:
+ html.append('<p>{}</p>'.format(htmlescape(line)))
+ return '\n'.join(html)
+
+def absolute_url(base, url):
+ """
+ modifies `url` in place
+ """
+ if not url.scheme():
+ url.setScheme(base.scheme())
+ if not url.host():
+ url.setHost(base.host())
+ if not url.path().startswith('/'):
+ url.setPath(base.path().rsplit('/', 1)[0] + '/' + url.path())
+ if url.port() == -1:
+ url.setPort(1965)
+ return url
+
+def get(url):
+ if len(url.path()) == 0:
+ url.setPath('/')
+ return {
+ 'status': '32',
+ 'meta': url.toDisplayString(),
+ }
+ context = ssl.create_default_context()
+ context.check_hostname = False
+ context.verify_mode = ssl.CERT_NONE
+ with socket.create_connection((url.host(), url.port())) as sock:
+ with context.wrap_socket(sock, server_hostname=url.host()) as ssock:
+ ssock.sendall('gemini://{}{}\r\n'.format(url.host(), url.path()).encode('utf8'))
+ fp = ssock.makefile(mode='rb')
+ header = fp.readline(1027)
+ status, meta = header.decode('utf8').split(None, 1)
+ if status[0] != '2':
+ return {
+ 'status': status,
+ 'meta': meta.strip(),
+ }
+ body = fp.read()
+ return {
+ 'status': status,
+ 'meta': meta.strip(),
+ 'body': body.decode('utf8'),
+ }