repo: gemcall
action: commit
revision: 
path_from: 
revision_from: 7c3c3b42ac0ea50ef8d79ac6359199a2f6c4cfe1:
path_to: 
revision_to: 
git.thebackupbox.net
gemcall
git clone git://git.thebackupbox.net/gemcall
commit 7c3c3b42ac0ea50ef8d79ac6359199a2f6c4cfe1
Author: Björn Wärmedal 
Date:   Sun Jul 18 16:29:06 2021 +0200

    Made installable by pip.

diff --git a/gemcall b/gemcall
deleted file mode 100755
index 5546e94079b94f986b5f9d947911dcc77c29e9ad..0000000000000000000000000000000000000000
--- a/gemcall
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env python3
-# vim: tabstop=4 shiftwidth=4 expandtab
-
-import socket
-import urllib.parse
-import ssl
-import argparse
-import sys
-from cryptography import x509
-from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives.serialization import Encoding,PublicFormat
-
-
-
-class Response():
-    def __init__(self, socket: socket.socket) -> "Response":
-        self._socket = socket
-
-        cert = self._socket.getpeercert(binary_form=True)
-        certobj = x509.load_der_x509_certificate(cert, default_backend())
-        pubkey = certobj.public_key()
-
-        self.serverpubkey = pubkey.public_bytes(Encoding('PEM'), PublicFormat('X.509 subjectPublicKeyInfo with PKCS#1'))
-        self._filehandle = self._socket.makefile(mode = "rb")
-
-        # Two code digits, one space and a maximum of 1024 bytes of meta info.
-        try:
-            self.responsecode, self.meta = self._filehandle.readline(1027).split(maxsplit=1)
-            self.responsecode = int(self.responsecode)
-            self.meta = self.meta.strip().decode("UTF-8")
-        except:
-            self.discard()
-            raise RuntimeError("Received malformed header from gemini server")
-
-    def read(self, bufsize: int = 4096) -> "bytes":
-        return self._filehandle.read(bufsize)
-
-    def discard(self) -> None:
-        self._filehandle.close()
-        self._socket.close()
-
-def request(url: str = "", clientcert: str = None, clientkey: str = None, timeout: int = 3) -> "Response":
-    parsed = urllib.parse.urlparse(url)
-    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
-    context.minimum_version = ssl.TLSVersion.TLSv1_2
-    context.check_hostname = False
-    context.verify_mode = ssl.CERT_NONE
-    if (clientcert and clientkey):
-        context.load_cert_chain(clientcert, clientkey)
-
-    sock = socket.create_connection((parsed.hostname, parsed.port or 1965))
-    ssock = context.wrap_socket(sock, server_hostname=parsed.hostname)
-    ssock.sendall((url+"\r\n").encode("UTF-8"))
-
-    return Response(ssock)
-
-if __name__ == "__main__":
-
-    parser = argparse.ArgumentParser(description="Python module/CLI program for making network requests with the gemini protocol.")
-    parser.add_argument("-c","--clientcert",help="Path to client certificate. This is optional, but must be used when -k/--clientkey is used.")
-    parser.add_argument("-k","--clientkey",help="Path to the private key file for a client certificate. This is optional, but must be used when -c/--clientcert is used.")
-    parser.add_argument("-u","--url",help="Fully qualified URL to fetch.")
-    parser.add_argument("-o","--outputfile",help="File to output response body to.")
-    parser.add_argument("-t","--timeout",help="Timeout of connection attempt, in seconds. Default is 3.",default=3,type=int)
-    parser.add_argument("-q","--quiet",help="Don't print response header.",action="store_true")
-    parser.add_argument("-n","--nobody",help="Only print response header.",action="store_true")
-    parser.add_argument("-s","--stdoutonly",help="Print everything to stdout",action="store_true")
-    args = parser.parse_args()
-
-    if not args.url:
-        parser.print_help()
-    else:
-        responseobj = request(url = args.url, clientcert = args.clientcert, clientkey = args.clientkey, timeout = args.timeout)
-
-        if not args.quiet:
-            header = str.encode("%s %s\n" % (responseobj.responsecode, responseobj.meta))
-            if args.stdoutonly:
-                sys.stdout.buffer.write(header)
-            else:
-                sys.stderr.buffer.write(header)
-        outputfile = open(args.outputfile, "wb") if args.outputfile else None
-        while not args.nobody:
-            buf = responseobj.read()
-            if len(buf) == 0:
-                break
-            elif outputfile:
-                outputfile.write(buf)
-                outputfile.close()
-            else:
-                sys.stdout.buffer.write(buf) 
-
-        responseobj.discard()
diff --git a/gemcall/__init__.py b/gemcall/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d3cb203ef94324d7bfd1f24fb8e42f1b32ffe9d
--- /dev/null
+++ b/gemcall/__init__.py
@@ -0,0 +1 @@
+from .gemcall import request, Response
diff --git a/gemcall/__main__.py b/gemcall/__main__.py
new file mode 100755
index 0000000000000000000000000000000000000000..9b58ad95392aafb37f65b3e7e7ec41a60c790a3f
--- /dev/null
+++ b/gemcall/__main__.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# vim: tabstop=4 shiftwidth=4 expandtab
+
+import argparse
+import sys
+import gemcall
+
+if __name__ == "__main__":
+
+    parser = argparse.ArgumentParser(prog="gemcall",description="Python module/CLI program for making network requests with the gemini protocol.")
+    parser.add_argument("-c","--clientcert",help="Path to client certificate. This is optional, but must be used when -k/--clientkey is used.")
+    parser.add_argument("-k","--clientkey",help="Path to the private key file for a client certificate. This is optional, but must be used when -c/--clientcert is used.")
+    parser.add_argument("-u","--url",help="Fully qualified URL to fetch.")
+    parser.add_argument("-o","--outputfile",help="File to output response body to.")
+    parser.add_argument("-t","--timeout",help="Timeout of connection attempt, in seconds. Default is 3.",default=3,type=int)
+    parser.add_argument("-q","--quiet",help="Don't print response header.",action="store_true")
+    parser.add_argument("-n","--nobody",help="Only print response header.",action="store_true")
+    parser.add_argument("-s","--stdoutonly",help="Print everything to stdout",action="store_true")
+    args = parser.parse_args()
+
+    if not args.url:
+        parser.print_help()
+    else:
+        responseobj = gemcall.request(url = args.url, clientcert = args.clientcert, clientkey = args.clientkey, timeout = args.timeout)
+
+        if not args.quiet:
+            header = str.encode("%s %s\n" % (responseobj.responsecode, responseobj.meta))
+            if args.stdoutonly:
+                sys.stdout.buffer.write(header)
+            else:
+                sys.stderr.buffer.write(header)
+        outputfile = open(args.outputfile, "wb") if args.outputfile else None
+        while not args.nobody:
+            buf = responseobj.read()
+            if len(buf) == 0:
+                break
+            elif outputfile:
+                outputfile.write(buf)
+                outputfile.close()
+            else:
+                sys.stdout.buffer.write(buf) 
+
+        responseobj.discard()
diff --git a/gemcall/gemcall.py b/gemcall/gemcall.py
new file mode 100755
index 0000000000000000000000000000000000000000..f7e0d60792991f62eea320757ab503fba26e082e
--- /dev/null
+++ b/gemcall/gemcall.py
@@ -0,0 +1,51 @@
+# vim: tabstop=4 shiftwidth=4 expandtab
+
+import socket
+import urllib.parse
+import ssl
+from cryptography import x509
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.serialization import Encoding,PublicFormat
+
+class Response():
+    def __init__(self, socket: socket.socket) -> "Response":
+        self._socket = socket
+
+        cert = self._socket.getpeercert(binary_form=True)
+        certobj = x509.load_der_x509_certificate(cert, default_backend())
+        pubkey = certobj.public_key()
+
+        self.serverpubkey = pubkey.public_bytes(Encoding('PEM'), PublicFormat('X.509 subjectPublicKeyInfo with PKCS#1'))
+        self._filehandle = self._socket.makefile(mode = "rb")
+
+        # Two code digits, one space and a maximum of 1024 bytes of meta info.
+        try:
+            self.responsecode, self.meta = self._filehandle.readline(1027).split(maxsplit=1)
+            self.responsecode = int(self.responsecode)
+            self.meta = self.meta.strip().decode("UTF-8")
+        except:
+            self.discard()
+            raise RuntimeError("Received malformed header from gemini server")
+
+    def read(self, bufsize: int = 4096) -> "bytes":
+        return self._filehandle.read(bufsize)
+
+    def discard(self) -> None:
+        self._filehandle.close()
+        self._socket.close()
+
+def request(url: str = "", clientcert: str = None, clientkey: str = None, timeout: int = 3) -> "Response":
+    parsed = urllib.parse.urlparse(url)
+    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+    context.minimum_version = ssl.TLSVersion.TLSv1_2
+    context.check_hostname = False
+    context.verify_mode = ssl.CERT_NONE
+    if (clientcert and clientkey):
+        context.load_cert_chain(clientcert, clientkey)
+
+    sock = socket.create_connection((parsed.hostname, parsed.port or 1965))
+    ssock = context.wrap_socket(sock, server_hostname=parsed.hostname)
+    ssock.sendall((url+"\r\n").encode("UTF-8"))
+
+    return Response(ssock)
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..51849e94129bf3628bd1558b075d9cb7db8725e7
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,32 @@
+from setuptools import setup
+
+setup(
+    name='gemcall',
+    version='0.6',    
+    description='A library and CLI tool for making gemini requests.',
+    url='https://notabug.org/tinyrabbit/gemcall',
+    author='Björn Wärmedal',
+    author_email='bjorn.warmedal@gmail.com',
+    license='MIT License',
+    packages=['gemcall'],
+    install_requires=[],
+
+    classifiers=[
+        'Development Status :: 4 - Beta',
+        'Intended Audience :: End Users/Desktop',
+        'License :: OSI Approved :: MIT License',  
+        'Operating System :: POSIX :: Linux',        
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.4',
+        'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
+        'Programming Language :: Python :: 3.7',
+        'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.9',
+        'Topic :: Internet :: Gemini',
+        'Topic :: Software Development :: Libraries',
+        'Topic :: Utilities',
+        'Typing :: Typed',
+    ],
+)
+

-----END OF PAGE-----