commit d20914d034e679d951aeb1edd4aa9d1dd67f342f
Author: David Sorber <david.sorber@gmail.com>
Date:   Wed Dec 19 21:12:19 2018 -0500

    Initial create of handbrake interface project.

diff --git a/software/hbkif/conf/apache/hbkif.conf b/software/hbkif/conf/apache/hbkif.conf
new file mode 100644
index 0000000..e272504
--- /dev/null
+++ b/software/hbkif/conf/apache/hbkif.conf
@@ -0,0 +1,19 @@
+WSGIScriptAlias /hbkif    /home/dsorber/Documents/repo/software/hbkif/hbkif.wsgi
+<Directory /home/dsorber/Documents/repo/software/hbkif/>
+        WSGIApplicationGroup %{GLOBAL}
+        WSGIPassAuthorization On
+        Require all granted
+</Directory>
+
+#Alias "/docs/uspto/claims" "/ryftone/regression/uspto/claims/"
+#<Directory /ryftone/regression/uspto/claims/>
+#        Options +Indexes
+#        Require all granted
+#</Directory>
+
+#Alias "/docs/uspto/parsed" "/ryftone/regression/uspto/split-parsed/"
+#<Directory /ryftone/regression/uspto/split-parsed/>
+#        Options +Indexes
+#        Require all granted
+#</Directory>
+
diff --git a/software/hbkif/hbkif.wsgi b/software/hbkif/hbkif.wsgi
new file mode 100755
index 0000000..0525215
--- /dev/null
+++ b/software/hbkif/hbkif.wsgi
@@ -0,0 +1,38 @@
+import re
+
+from hbkif.controllers.status import StatusController
+from hbkif.controllers.version import VersionController
+
+class Delegator(object):
+
+    def __init__(self):
+        pass
+
+    def __call__(self, environ, start_response):
+        path = environ['PATH_INFO']
+
+        if re.match('^/status(/)?$', path):
+            # Authenticate user
+            controller = StatusController(environ)
+            status, headers, body = controller.generate_response()
+        elif re.match('^/version(/)?$', path):
+           # All bookmarks page
+           controller = VersionController(environ)
+           status, headers, body = controller.generate_response()
+        else:
+            # User supplied an invalid URL
+            status = '200 OK'
+            body = 'hbkif is working!\n\nInvalid URL "{:s}"'.format(environ['PATH_INFO'])
+            body = body.encode()
+            headers = [('Content-Type', 'text/plain'),
+                       ('Content-Length', str(len(body)))]
+
+        # Send the status and response headers to the server
+        start_response(status, headers)
+        # Return the response body. (Notice it is wrapped in a list although it
+        # could be any iterable.)
+        return [body]
+        
+
+# Set the callable object
+application = Delegator()
diff --git a/software/hbkif/hbkif/__init__.py b/software/hbkif/hbkif/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/software/hbkif/hbkif/config.py b/software/hbkif/hbkif/config.py
new file mode 100644
index 0000000..4edad4e
--- /dev/null
+++ b/software/hbkif/hbkif/config.py
@@ -0,0 +1,17 @@
+import logging
+
+# Version information
+VERSION_MAJOR  = 0
+VERSION_MINOR  = 1
+VERSION_BUGFIX = 0
+
+VERSION_STR = '{:d}.{:d}.{:d}'.format(VERSION_MAJOR, VERSION_MINOR, 
+                                      VERSION_BUGFIX)
+
+# Logfile information
+LOGFILE_PATH = '/var/log/hbkif/hbkif.log'
+LOG_FORMAT_STR = '%(asctime)s|%(levelname)s|%(filename)s:%(lineno)s|%(message)s'
+LOG_DATE_FORMAT_STR = '%a %b %d %H:%M:%S %Y'
+DEFAULT_LOG_LEVEL = logging.DEBUG
+
+
diff --git a/software/hbkif/hbkif/controllers/__init__.py b/software/hbkif/hbkif/controllers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/software/hbkif/hbkif/controllers/base.py b/software/hbkif/hbkif/controllers/base.py
new file mode 100644
index 0000000..c5c26d5
--- /dev/null
+++ b/software/hbkif/hbkif/controllers/base.py
@@ -0,0 +1,86 @@
+import logging
+
+from hbkif.config import LOGFILE_PATH, LOG_FORMAT_STR, LOG_DATE_FORMAT_STR, \
+                         DEFAULT_LOG_LEVEL
+
+
+def auth_with_jwt(func):
+    """ Method decorator for setting up JWT authorization for controllers that
+    require it.
+    """
+    def func_wrapper(self, *args, **kwargs):
+        self.status = '403 Forbidden'
+        self.content = b''
+        
+        http_auth = self.environ.get('HTTP_AUTHORIZATION', '')
+        logging.debug('HTTP_AUTHORIZATION: {:s}'.format(http_auth))
+        if http_auth.startswith('Bearer '):
+            logging.debug('Attempting to authorize JWT')
+            self.token_valid, self.token_expired, self.user_id = \
+                authorize_jwt(http_auth[7:])
+            
+            if not self.token_expired:
+                # The token is not expired, so decide what to do next...
+                # For now we'll fully authorize if the token is valid (this 
+                # can be changed in the future if fine-grained permissions are 
+                # needed)
+                self.authorized = self.token_valid
+            else:
+                # TODO: handle expired... 
+                logging.debug('JWT is expired')
+        return func(self, *args, **kwargs)
+        
+    return func_wrapper
+    
+
+class BaseController(object):
+   
+    def __init__(self, environ):
+            
+        logging.basicConfig(format=LOG_FORMAT_STR, 
+                            datefmt=LOG_DATE_FORMAT_STR, 
+                            filename=LOGFILE_PATH, level=DEFAULT_LOG_LEVEL)
+       
+        self.environ = environ
+        self.content = ''
+        
+        # Override this as necessary
+        self.content_type = 'text/html; charset=utf-8'
+        
+        # Unless overridden status should be 200 for success
+        self.status = '200 OK'
+        
+        # Add custom headers as needed (formatted as tuple of: name, value)
+        self.custom_headers = []
+        
+        # Used for token authorization
+        self.authorized = False
+        self.token_valid = False
+        self.token_expired = False
+        self.user_id = None
+       
+    def build_content(self):
+        """ This method is used to build the content that is used to generate
+        the response. This method should populate the self.content dict.
+       
+        Override this method with your own to make this controller actually
+        do stuff!
+        """
+        return self.content.encode()
+       
+    def generate_response(self):
+        """ This function generates the actual response which is made up of two
+        parts, the response headers and the response body. It then returns the
+        status along with the headers and body.
+        """
+        body = self.build_content()
+                        
+        # Set the appropriate response headers
+        headers = [('Content-Type', self.content_type),
+                   ('Content-Length', str(len(body)))]
+        
+        # Include any custom headers
+        if self.custom_headers:
+            headers.extend(self.custom_headers)
+        
+        return self.status, headers, body
diff --git a/software/hbkif/hbkif/controllers/status.py b/software/hbkif/hbkif/controllers/status.py
new file mode 100644
index 0000000..be1bba8
--- /dev/null
+++ b/software/hbkif/hbkif/controllers/status.py
@@ -0,0 +1,31 @@
+import datetime
+import json
+import logging
+import subprocess
+
+from hbkif.controllers.base import BaseController
+from hbkif.config import VERSION_STR
+
+pwd = 'admin'
+
+class StatusController(BaseController):
+   
+    def __init__(self, environ):
+        super(StatusController, self).__init__(environ)
+        
+        # We return JSON
+        self.content_type = 'application/json'
+   
+    def build_content(self):
+        logging.info('Handling /status request')
+        
+        # Build command
+        cmd = 'ipmitool -I lanplus -U admin -P {:s} -H brutus-ipmi power status'
+        cmd = cmd.format(pwd)
+        proc = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, 
+                              stderr=subprocess.STDOUT)
+        
+        version_dict = {'returncode': proc.returncode,
+                        'status': proc.stdout.decode('utf-8').strip()}
+        self.content = json.dumps(version_dict).encode()
+        return self.content
diff --git a/software/hbkif/hbkif/controllers/version.py b/software/hbkif/hbkif/controllers/version.py
new file mode 100644
index 0000000..1a5f98d
--- /dev/null
+++ b/software/hbkif/hbkif/controllers/version.py
@@ -0,0 +1,22 @@
+import datetime
+import json
+import logging
+
+from hbkif.controllers.base import BaseController
+from hbkif.config import VERSION_STR
+
+class VersionController(BaseController):
+   
+    def __init__(self, environ):
+        super(VersionController, self).__init__(environ)
+        
+        # We return JSON
+        self.content_type = 'application/json'
+   
+    def build_content(self):
+        logging.info('Handling /version request')
+        now = datetime.datetime.now()        
+        version_dict = {'version': VERSION_STR,
+                        'timestamp': now.strftime('%Y-%m-%d %H:%M:%S')}
+        self.content = json.dumps(version_dict).encode()
+        return self.content
diff --git a/software/hbkif/setup.py b/software/hbkif/setup.py
new file mode 100644
index 0000000..cd1be1b
--- /dev/null
+++ b/software/hbkif/setup.py
@@ -0,0 +1,16 @@
+from setuptools import setup, find_packages
+
+# Use implicit ryft namespace package to keep things organized
+setup(
+    name='hbkif',
+    version='0.1.0',
+    description='Handbrake web interface',
+    packages=['hbkif', 'hbkif.controllers'],
+    # ~ packages=find_packages('hbkif'),
+    # ~ entry_points = {
+        # ~ 'console_scripts': [
+            # ~ 'docsim_daemon = ryft.xrest.docsim_daemon:main',
+        # ~ ]
+    # ~ },
+    zip_safe=False,
+)
