gemini.git

going-flying.com gemini git repository

summary

tree

log

refs

86f7239e588cc0cada4094cc6550af8ec3f067a9 - Matthew Ernisse - 1614028128

move GeminiCGI out into a module so I can use it from multiple scripts and create a date cgi.

view tree

view raw

diff --git a/cgi-bin/converter b/cgi-bin/converter
index ab82bde..197b2b2 100755
--- a/cgi-bin/converter
+++ b/cgi-bin/converter
@@ -29,13 +29,14 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 '''
 import base64
 import datetime
-import os
 import secrets
 import sys
 import qrcode
 from urllib.parse import parse_qs, quote, unquote, urlparse
 
 
+import gmicgi
+
 BACK_LINK = '/cgi-bin/converter/'
 MAIN_SCREEN = '''
 # Matt's Gemini CGI Toolbox.
@@ -76,73 +77,6 @@ Want to see something here?  Feel free to drop me a line.
 '''
 
 
-class GeminiCGI(object):
-	'''A convenience class to handle CGI under Molly Brown.
-	Contains a Response class to make it easy to not forget the
-	nuances of the Gemini header line.
-	'''
-
-	class Response(object):
-		''' Convenience class to create a Gemini Response. '''
-		codes = {
-			10: ('10', ''),
-			20: ('20', ''),
-			30: ('30', ''),
-			50: ('50', 'PERMANENT FAILURE'),
-			51: ('51', 'NOT FOUND'),
-			59: ('59', 'BAD REQUEST'),
-		}
-
-		def __init__(self, code=10, meta=''):
-			''' Construct and print a response header per spec 
-			section 3.1
-			'''
-			header = self.codes[code][0]
-			if meta:
-				header += ' ' + meta
-			elif self.codes[code][1]:
-				header += ' ' + self.codes[code][1]
-
-			sys.stdout.write(header + '\r\n')
-
-		@classmethod
-		def BadRequest(cls):
-			return cls(59)
-
-		@classmethod
-		def Fail(cls):
-			return cls(50)
-
-		@classmethod
-		def Input(cls, meta):
-			return cls(10, meta)
-
-		@classmethod
-		def NotFound(cls):
-			return cls(51)
-
-		@classmethod
-		def Ok(cls, meta):
-			return cls(20, meta)
-
-		@classmethod
-		def Redirect(cls, meta):
-			return cls(30, meta)
-
-	def __init__(self):
-		self.path_info = os.environ.get('PATH_INFO')
-		self.script_path = os.environ.get('SCRIPT_PATH')
-		self.query_string = os.environ.get('QUERY_STRING')
-
-		self.query_dequoted = ''
-		self.query_parsed = {}
-
-		if self.query_string:
-			self.query_dequoted = unquote(self.query_string)
-			self.query_parsed = parse_qs(self.query_string)
-			
-
-
 def encode_response(cgi, input, result):
 	global BACK_LINK
 	cgi.Response.Ok('text/gemini')
@@ -217,7 +151,7 @@ def secret_response(cgi, val, qr=False):
 
 
 if __name__ == '__main__':
-	cgi = GeminiCGI()
+	cgi = gmicgi.GeminiCGI()
 
 	if not cgi.query_string:
 		if not cgi.path_info:
diff --git a/cgi-bin/date b/cgi-bin/date
new file mode 100755
index 0000000..f6b6e23
--- /dev/null
+++ b/cgi-bin/date
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+import datetime
+import dateutil.tz
+import gmicgi
+
+MAIN_SCREEN = '''
+# Matt's Random Dates
+
+## The Eternal September
+
+{}
+
+> Eternal September or the September that never ended is Usenet slang for a period beginning in September 1993, the month that Internet service provider America Online (AOL) began offering Usenet access to its many users, overwhelming the existing culture for online forums.
+
+=> https://en.wikipedia.org/wiki/Eternal_September
+
+## The Eternal March
+
+{}
+
+> A period starting March 2020 marking the begining of shutdowns and general social disruption due to the COVID-19 (SARS-CoV-2) pandemic in the United States of America.
+
+
+(Times are in US/Eastern (GMT-4/GMT-5))
+
+=> /    ↩ Home
+'''
+
+def prettySinceEpoch(y, m, d, month_name):
+	''' Return a pretty string describing the date from an epoch,
+	eg: September 10, 2020 is March 194, 2020 in the eternal march.
+
+	1000% stolen from my Azure Function taht does the same thing.
+
+	'''
+	my_tz = dateutil.tz.gettz('America/New_York')
+
+	epoch = datetime.datetime(y, m, d, tzinfo=my_tz)
+	now = datetime.datetime.now(tz=my_tz)
+
+	dow = now.strftime('%a')
+	time = now.strftime('%H:%M:%S %Z')
+	days = now - epoch
+	days = days.days
+
+	return f'{dow} {month_name} {days} {time} {y}'
+
+if __name__ == '__main__':
+	cgi = gmicgi.GeminiCGI()
+
+	eternal_march = prettySinceEpoch(2020, 3, 1, 'Mar')
+	eternal_september = prettySinceEpoch(1993, 9, 1, 'Sep')
+
+	cgi.Response.Ok('text/gemini')
+	print(MAIN_SCREEN.format(eternal_september, eternal_march))
diff --git a/cgi-bin/gmicgi/__init__.py b/cgi-bin/gmicgi/__init__.py
new file mode 100755
index 0000000..d9a5b25
--- /dev/null
+++ b/cgi-bin/gmicgi/__init__.py
@@ -0,0 +1,97 @@
+# -*- coding: UTF-8 -*-
+'''gmicgi (c) 2021 Matthew J Ernisse <matt@going-flying.com>
+All Rights Reserved.
+
+Redistribution and use in source and binary forms,
+with or without modification, are permitted provided
+that the following conditions are met:
+
+    * Redistributions of source code must retain the
+      above copyright notice, this list of conditions
+      and the following disclaimer.
+    * Redistributions in binary form must reproduce
+      the above copyright notice, this list of conditions
+      and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+'''
+import os
+import sys
+from urllib.parse import parse_qs, quote, unquote, urlparse
+
+
+class GeminiCGI(object):
+	'''A convenience class to handle CGI under Molly Brown.
+	Contains a Response class to make it easy to not forget the
+	nuances of the Gemini header line.
+	'''
+
+	class Response(object):
+		''' Convenience class to create a Gemini Response. '''
+		codes = {
+			10: ('10', ''),
+			20: ('20', ''),
+			30: ('30', ''),
+			50: ('50', 'PERMANENT FAILURE'),
+			51: ('51', 'NOT FOUND'),
+			59: ('59', 'BAD REQUEST'),
+		}
+
+		def __init__(self, code=10, meta=''):
+			''' Construct and print a response header per spec 
+			section 3.1
+			'''
+			header = self.codes[code][0]
+			if meta:
+				header += ' ' + meta
+			elif self.codes[code][1]:
+				header += ' ' + self.codes[code][1]
+
+			sys.stdout.write(header + '\r\n')
+
+		@classmethod
+		def BadRequest(cls):
+			return cls(59)
+
+		@classmethod
+		def Fail(cls):
+			return cls(50)
+
+		@classmethod
+		def Input(cls, meta):
+			return cls(10, meta)
+
+		@classmethod
+		def NotFound(cls):
+			return cls(51)
+
+		@classmethod
+		def Ok(cls, meta):
+			return cls(20, meta)
+
+		@classmethod
+		def Redirect(cls, meta):
+			return cls(30, meta)
+
+	def __init__(self):
+		self.path_info = os.environ.get('PATH_INFO')
+		self.script_path = os.environ.get('SCRIPT_PATH')
+		self.query_string = os.environ.get('QUERY_STRING')
+
+		self.query_dequoted = ''
+		self.query_parsed = {}
+
+		if self.query_string:
+			self.query_dequoted = unquote(self.query_string)
+			self.query_parsed = parse_qs(self.query_string)
diff --git a/cgi-bin/gmicgi/__pycache__/__init__.cpython-39.pyc b/cgi-bin/gmicgi/__pycache__/__init__.cpython-39.pyc
new file mode 100644
index 0000000..8582931
Binary files /dev/null and b/cgi-bin/gmicgi/__pycache__/__init__.cpython-39.pyc differ