going-flying.com gemini git repository
86f7239e588cc0cada4094cc6550af8ec3f067a9 - Matthew Ernisse - 1614028128
move GeminiCGI out into a module so I can use it from multiple scripts and create a date cgi.
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