Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

first commit

  • Loading branch information...
commit 3dc507ab8c6d8fce88a435d0d3e46346f01a76b3 0 parents
Louis Sobel authored
Showing with 1,150 additions and 0 deletions.
  1. +16 −0 README
  2. BIN  config.pyc
  3. +5 −0 drapache/__init__.py
  4. BIN  drapache/__init__.pyc
  5. +37 −0 drapache/dbapiaccess.py
  6. BIN  drapache/dbapiaccess.pyc
  7. +37 −0 drapache/dbapijinja.py
  8. BIN  drapache/dbapijinja.pyc
  9. +154 −0 drapache/dbapiserver.py
  10. BIN  drapache/dbapiserver.pyc
  11. BIN  drapache/dbpy_builtins.pyc
  12. BIN  drapache/dbpy_execute.pyc
  13. +74 −0 drapache/dbpybuiltins.py
  14. BIN  drapache/dbpybuiltins.pyc
  15. +194 −0 drapache/dbpyexecute.py
  16. BIN  drapache/dbpyexecute.pyc
  17. BIN  drapache/drapacheHTTPServer.pyc
  18. BIN  drapache/drapache_instance.pyc
  19. BIN  drapache/drapache_subdomain.pyc
  20. BIN  drapache/drapache_util.pyc
  21. BIN  drapache/drapbox_server.pyc
  22. BIN  drapache/dropache_util.pyc
  23. BIN  drapache/dropbox_access.pyc
  24. BIN  drapache/dropbox_jinja.pyc
  25. BIN  drapache/dropbox_server.pyc
  26. +134 −0 drapache/httpserver.py
  27. BIN  drapache/httpserver.pyc
  28. +32 −0 drapache/index_generator.py
  29. BIN  drapache/index_generator.pyc
  30. +23 −0 drapache/instance.py
  31. BIN  drapache/instance.pyc
  32. +121 −0 drapache/mysql_connect.py
  33. BIN  drapache/mysql_connect.pyc
  34. +78 −0 drapache/subdomain.py
  35. BIN  drapache/subdomain.pyc
  36. +19 −0 drapache/templates/index.html.tem
  37. +122 −0 drapache/twistd_resource.py
  38. BIN  drapache/twistd_resource.pyc
  39. +27 −0 drapache/util.py
  40. BIN  drapache/util.pyc
  41. +5 −0 requirements.txt
  42. +29 −0 sample_config.py
  43. +32 −0 sample_server.py
  44. +11 −0 setup.py
16 README
@@ -0,0 +1,16 @@
+Drapache:
+
+A python server that uses the dropbox API to serve files
+
+It will create an index for a folder if one doesn't exist
+
+Static files work great.
+
+I'm trying to include the ability to execute python scripts that in a dropbox folder, this is where problems come in.
+
+It works, but its very hack-y and unstable
+
+
+
+I'm hosting an instance on heroku, go to get.drapache.com to set it up.
+
BIN  config.pyc
Binary file not shown
5 drapache/__init__.py
@@ -0,0 +1,5 @@
+import subdomain
+import dbapiaccess
+from instance import Drapache
+
+from twistd_resource import DrapacheTwistdResource
BIN  drapache/__init__.pyc
Binary file not shown
37 drapache/dbapiaccess.py
@@ -0,0 +1,37 @@
+"""
+Defines utility classes for connecting to dropbox
+"""
+
+import dropbox
+
+
+#credentials for Dropaches
+
+
+
+class DropboxClientCreator:
+
+
+ def __init__(self,app_key,app_secret,access_type='app_folder'):
+
+ self.app_key = app_key
+ self.app_secret = app_secret
+ self.access_type = access_type
+
+
+ def get_client(self,oauth_token,oauth_token_secret):
+
+ sess = dropbox.session.DropboxSession(self.app_key,self.app_secret,self.access_type)
+ sess.set_token(oauth_token,oauth_token_secret)
+ client = dropbox.client.DropboxClient(sess)
+ return client
+
+
+def generate_oauth_credentials():
+ sess = dropbox.session.DropboxSession(APP_KEY,APP_SECRET,'app_folder')
+ rt = sess.obtain_request_token()
+ url = sess.build_authorize_url(rt)
+ print url
+ raw_input()
+ at = sess.obtain_access_token(rt)
+ print at
BIN  drapache/dbapiaccess.pyc
Binary file not shown
37 drapache/dbapijinja.py
@@ -0,0 +1,37 @@
+"""
+The class that allows templateing from dropbox
+"""
+import jinja2
+import os
+import dropbox
+
+class DropboxLoader(jinja2.BaseLoader):
+
+
+ def __init__(self,client,search_root):
+ self.client = client
+ self.search_root = search_root
+
+
+ def get_source(self,environment,template):
+
+ template_path = os.path.join(self.search_root,template)
+
+ try:
+ f = self.client.get_file(template_path).read()
+ except dropbox.rest.ErrorResponse as e:
+ raise jinja2.TemplateNotFound(template)
+
+ return f,template_path,True
+
+
+def render_dropbox_template(client,template,data,search_root):
+ env = jinja2.Environment(loader=DropboxLoader(client,search_root))
+
+ try:
+ template = env.get_template(template)
+ output = template.render(**data)
+ except jinja2.TemplateNotFound as e:
+ output = "Template %s not found; looking in %s" % (template,search_root)
+
+ return output
BIN  drapache/dbapijinja.pyc
Binary file not shown
154 drapache/dbapiserver.py
@@ -0,0 +1,154 @@
+"""
+Implements the interaction with the dropbox api
+"""
+import dropbox
+
+import dbpyexecute
+import index_generator
+from util import ResponseObject,DropacheException
+
+import os.path
+import re
+
+#for dev
+import pprint
+#
+
+
+
+class FileServer:
+ """
+ The class responsable for hitting the dropbox and processing the results
+
+ most of the power of the service will come from here
+ """
+
+
+ def __init__(self,client,get_params,query_string):
+ self.client = client
+ self.get_params = get_params
+ self.query_string = query_string
+
+ def serve(self,path):
+ """
+ serves the given path, returning a Response Object
+
+ some special rules
+ - if it is a directory,
+ returns an indexed list of the files
+ - if it is a directory without a trailing slash,
+ returns a redirect request (these will also be able to come fro)
+ """
+
+ try:
+ meta_info = self.client.metadata(path)
+
+ #### checking for the is_Deleted flag
+ try:
+ if meta_info['is_deleted']:
+ raise DropacheException(410,"File is deleted")
+ except KeyError:
+ pass #its not deleted
+
+ if meta_info['is_dir']:
+ #that means we are dealing with a directory
+ #first check if it doesn't end with a slash
+ if not path.endswith('/'):
+ redirect_location = path+'/'
+ if self.query_string:
+ redirect_location += '?'+self.query_string
+
+ return ResponseObject(301,'redirect',{'Location':redirect_location})
+ else:
+ return self._find_and_serve_index(meta_info,path)
+
+ else:
+ #serve file handles the routing of pyhton vs file
+ return self._serve_file(meta_info)
+
+
+ except dropbox.rest.ErrorResponse as e:
+ return ResponseObject(e.status,e.reason,headers=e.headers,error=True)
+
+ except DropacheException as e:
+ return ResponseObject(e.status,e.message,error=True)
+
+ def _serve_file(self,file_meta):
+ #here is where special handling must be invoked
+ if file_meta['path'].endswith('.dbpy'):
+ return self._serve_python(file_meta)
+ else:
+ return self._serve_static(file_meta)
+
+
+ def _serve_static(self,file_meta):
+ """
+ downloads and serves the file in path
+ """
+ path = file_meta['path']
+ f = self.client.get_file(path).read()
+ headers = {'Content-type':self._get_content_type(file_meta)}
+ return ResponseObject(200,f,headers)
+
+
+ def _serve_python(self,file_meta):
+ path = file_meta['path']
+ f = self.client.get_file(path).read()
+ if f.startswith("#NOEXECUTE"):
+ #allows these files to be shared without getting executed
+ headers = {'Content-type':'text/plain'}
+ return ResponseObject(200,f,headers)
+
+
+ param_dict = {}
+ param_dict['client'] = self.client
+ param_dict['get_params'] = self.get_params
+
+ return dbpyexecute.execute(f,**param_dict)
+
+ def _find_and_serve_index(self,directory_meta,path):
+ """
+ called when asked to serce a directory
+ check for the presence of an index file and serve it (without redirect of course)
+ or present an index if there isn't one
+ lets lok through meta_info[contents], anything with index is of interest
+ precedence is .dbpy, .html, .txt, and thats it
+
+ for now, just auto generate an index, fun!
+ """
+ extensions_precedence = ('dbpy','html','txt')
+
+ #build the re
+ re_string = "^index\.(%s)$"%( '|'.join(extensions_precedence) )
+ index_re = re.compile(re_string)
+
+ index_paths = {}
+
+ for file_meta in directory_meta['contents']:
+ file_path = file_meta['path']
+ base_name = os.path.basename(file_path)
+
+ index_re_match = index_re.match(base_name)
+
+ if index_re_match:
+ match_type = index_re_match.group(1)
+ index_paths[match_type] = file_meta
+
+
+ for extension in extensions_precedence:
+ if extension in index_paths:
+ return self._serve_file(index_paths[extension])
+
+
+ return ResponseObject(200,index_generator.get_index_file(directory_meta['contents'],path,self.client))
+
+
+ def _get_content_type(self,file_meta):
+ given = file_meta['mime_type']
+ if given.startswith('text/x-'):
+ return 'text/plain'
+ else:
+ return given
+
+
+
BIN  drapache/dbapiserver.pyc
Binary file not shown
BIN  drapache/dbpy_builtins.pyc
Binary file not shown
BIN  drapache/dbpy_execute.pyc
Binary file not shown
74 drapache/dbpybuiltins.py
@@ -0,0 +1,74 @@
+"""
+Here are the functions that will ship with python executable code
+"""
+
+import dbapijinja
+import sys
+import signal
+import time
+
+def get_builtins(**kwargs):
+ """
+ client is the client
+ get_params are the params from the get request
+ sandbox is the sandbox in which it runs
+ """
+
+ client = kwargs['client']
+ get_params = kwargs['get_params']
+ sandbox = kwargs['sandbox']
+
+
+ built_in_hash = {'GETPARAMS':get_params}
+ def register(function):
+
+ curglobs = globals()
+ curlocs = locals()
+
+ def outer_wrapper(*args,**kwargs):
+
+ for p in reversed(sandbox.protections):
+ p.disable(sandbox)
+
+ retval = function(*args,**kwargs)
+
+ #redo the protections
+ for p in sandbox.protections:
+ p.enable(sandbox)
+
+ return retval
+
+
+ built_in_hash[function.func_name] = outer_wrapper
+ return function
+
+
+
+
+ @register
+ def render_template(path,with_data=None,search_root="/_templates"):
+ """
+ renders the given template
+ """
+
+ print dbapijinja.render_dropbox_template(client,path,with_data,search_root)
+
+ @register
+ def die(message=""):
+ """
+ Raises an Exception
+ """
+ raise Exception(message)
+
+
+
+
+
+
+
+
+
+
+ return built_in_hash
+
+
BIN  drapache/dbpybuiltins.pyc
Binary file not shown
194 drapache/dbpyexecute.py
@@ -0,0 +1,194 @@
+"""
+responsible for executing downloaded code
+"""
+
+import StringIO
+import sys
+import traceback
+
+
+import threading
+import ctypes
+import trace
+
+import multiprocessing
+
+import sandbox as pysandbox
+from util import ResponseObject
+import dbpybuiltins
+
+class Timeout(Exception):
+ pass
+
+
+class KThread(threading.Thread):
+ """A subclass of threading.Thread, with a kill()
+ method.
+ found this @ http://www.velocityreviews.com/forums/t330554-kill-a-thread-in-python.html"""
+ def __init__(self, *args, **keywords):
+ threading.Thread.__init__(self, *args, **keywords)
+ self.killed = False
+ self.timeout = None
+
+ def start(self):
+ """Start the thread."""
+ self.__run_backup = self.run
+ self.run = self.__run # Force the Thread to install our trace.
+ threading.Thread.start(self)
+
+ def __run(self):
+ """Hacked run function, which installs the
+ trace."""
+ sys.settrace(self.globaltrace)
+ self.__run_backup()
+ self.run = self.__run_backup
+
+ def globaltrace(self, frame, why, arg):
+ if why == 'call':
+ return self.localtrace
+ else:
+ return None
+
+ def localtrace(self, frame, why, arg):
+ if self.killed:
+ if why == 'line':
+ raise Timeout("DBPY code timed out after %s seconds"%self.timeout)
+ return self.localtrace
+
+ def kill(self):
+ self.killed = True
+
+
+
+class DBPYExecThread(KThread):
+
+ def __init__(self,sandbox,builtins,code,timeout):
+ KThread.__init__(self)
+ self.sandbox = sandbox
+ self.builtins = builtins
+ self.code = code
+ self.timeout = timeout
+ self.error = None
+
+ def run(self):
+
+ try:
+ #getting around the sanboxes protections by manually getting to the call
+ self.sandbox._call(pysandbox.sandbox_class._call_exec, (self.code,self.builtins,None), {})
+
+ except Exception as e:
+
+ self.error = e
+
+class DBPYExecProcess(multiprocessing.Process):
+
+ def __init__(self,kwargs,code,timeout):
+ multiprocessing.Process.__init__(self)
+ self.sandbox = get_sandbox()
+ self.builtins = dbpybuiltins.get_builtins(sandbox=self.sandbox,**kwargs)
+ self.code = code
+ self.timeout = timeout
+ self.error = None
+
+ def run(self):
+
+ try:
+ self.sandbox._call(pysandbox.sandbox_class._call_exec, (self.code,self.builtins,None), {})
+
+ except Exception as e:
+
+ self.error = e
+
+def get_sandbox():
+ sandbox_config = pysandbox.SandboxConfig()
+ sandbox_config.enable("stdout")
+ sandbox_config.enable("time")
+ sandbox_config.enable("math")
+ sandbox_config.enable("exit")
+
+
+ sandbox_config.timeout = None
+
+
+
+ sandbox = pysandbox.Sandbox(sandbox_config)
+ return sandbox
+
+
+def execute(filestring,**kwargs):
+
+
+ PRINT_EXCEPTIONS = True
+ EXEC_TIMEOUT = 15
+
+
+ sandbox = get_sandbox()
+ builtin_dict = dbpybuiltins.get_builtins(sandbox=sandbox,**kwargs)
+
+ old_stdout = sys.stdout
+ new_stdout = StringIO.StringIO()
+
+ sys.stdout = new_stdout
+
+ try:
+ #im passing things to my sandbox in raw...
+ #sandbox._call(pysandbox.sandbox_class._call_exec, (filestring,builtin_dict,None),{})
+
+ #trying the kill here
+ USE = 'thread'
+
+ if USE == 'thread':
+ sys.stderr.write("starting run exec thread\n")
+ sandbox_thread = DBPYExecThread(sandbox,builtin_dict,filestring,EXEC_TIMEOUT)
+ sandbox_thread.start()
+
+ tid = sandbox_thread.ident
+
+ sandbox_thread.join(EXEC_TIMEOUT)
+
+ if sandbox_thread.isAlive():
+ #time to kill it
+ for protection in reversed(sandbox.protections):
+ protection.disable(sandbox)
+
+ sandbox_thread.kill()
+ sandbox_thread.join()
+
+ if sandbox_thread.error is not None:
+ raise sandbox_thread.error
+
+ elif USE == 'process':
+ sys.stderr.write("starting run exec procss\n")
+ sandbox_process = DBPYExecProcess(kwargs,filestring,EXEC_TIMEOUT)
+ sandbox_process.start()
+
+ pid = sandbox_process.pid
+
+ sandbox_process.join(EXEC_TIMEOUT)
+
+ if sandbox_process.is_alive():
+ #time to kill it
+ #for protection in reversed(sandbox.protections):
+ # proctection.disable(sandbox)
+
+ sandbox_process.terminate()
+ sandbox_process.join()
+
+ if sandbox_process.error is not Noen:
+ raise sandbox_process.error
+
+ except:
+ if PRINT_EXCEPTIONS:
+ print "<pre>"
+ traceback.print_exc(file=new_stdout)
+ print "</pre>"
+
+
+ sys.stdout = old_stdout
+
+ sys.stderr.write("done run exec thread\n")
+ sys.stderr.write("donthread: %s"%str(threading.enumerate()))
+ return ResponseObject(200,new_stdout.getvalue(),{'Content-type':'text/html'})
+
+
+
BIN  drapache/dbpyexecute.pyc
Binary file not shown
BIN  drapache/drapacheHTTPServer.pyc
Binary file not shown
BIN  drapache/drapache_instance.pyc
Binary file not shown
BIN  drapache/drapache_subdomain.pyc
Binary file not shown
BIN  drapache/drapache_util.pyc
Binary file not shown
BIN  drapache/drapbox_server.pyc
Binary file not shown
BIN  drapache/dropache_util.pyc
Binary file not shown
BIN  drapache/dropbox_access.pyc
Binary file not shown
BIN  drapache/dropbox_jinja.pyc
Binary file not shown
BIN  drapache/dropbox_server.pyc
Binary file not shown
134 drapache/httpserver.py
@@ -0,0 +1,134 @@
+"""
+The http server implementation
+"""
+
+import BaseHTTPServer
+import SocketServer
+import re
+import os
+import sys
+import urlparse
+
+import dbapiserver
+
+
+
+
+class DropboxHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ """
+ This class is responsible for sub-routing and headers,
+ getting/processing content gets farmed out (this will help when i thread?)
+ also, pulling out parameters
+ """
+
+
+
+
+ #def setup(self):
+ # BaseHTTPServer.BaseHTTPRequestHandler.setup(self)
+ #print self.request.settimeout(5)
+
+ def do_GET(self):
+
+
+ try:
+ host_string = self.headers.get("Host")
+ host_rest = host_string.split(".",1) #at most one split
+ if len(host_rest) == 1:
+ subdomain = None
+ else:
+ subdomain = host_rest[0]
+
+
+ path,query_string = self.parse_raw_path(self.path)
+
+ if query_string is not None:
+ query_dict = urlparse.parse_qs(query_string)
+ else:
+ query_dict = None
+
+ if subdomain is None:
+ self.send_error(400,"Dropache requires a username as the route")
+ return None
+
+
+ subdomain_manager = self.server.get_subdomain_manager()
+
+ try:
+ subdomain_exists = subdomain_manager.check_subdomain(subdomain)
+ except drapache_subdomain.SubdomainException as e:
+ self.send_error(503,"Error in subdomain lookup:\n"+e.message)
+ return None
+
+
+ if not subdomain_exists:
+ self.send_error(404,"Subdomain %s does not exist"%subdomain)
+ return None
+
+
+ try:
+ subdomain_token = subdomain_manager.get_token(subdomain)
+ except drapache_subdomain.SubdomainException as e:
+ self.end_error(503,"Error in subdomain lookup:\n"+e.message)
+ return None
+
+ subdomain_client = self.server.get_dropbox_client(subdomain_token)
+
+ file_server = dbapiserver.FileServer(subdomain_client,query_dict,query_string)
+
+ response = file_server.serve(path)
+
+ if response.error:
+ self.send_error(response.status,response.body)
+ return None
+
+ else:
+ self.send_response(response.status)
+ for h,v in response.headers.items():
+ self.send_header(h,v)
+ self.end_headers()
+ self.wfile.write(response.body)
+ return None
+
+ except Exception as e:
+ self.send_error(500,str(e))
+
+
+ def parse_raw_path(self,path):
+ """
+ pulls out the user, the path, and the query if any
+
+ the routing for now is going to be that the user is the first "directory"
+ """
+
+ #*.drapache:port/<the rest of the path>
+ po = urlparse.urlparse(path)
+ sub_path = po.path
+ query = po.query
+ if sub_path == '':
+ sub_path = '/'
+ if query == '':
+ query = None
+
+
+
+ return sub_path,query
+
+
+class DropboxMultiThreadHTTPServer(SocketServer.ThreadingMixIn,BaseHTTPServer.HTTPServer):
+
+ def set_config(self,subdomain_manager_factory,dropbox_client_factory):
+ self.get_subdomain_manager = subdomain_manager_factory
+ self.get_dropbox_client = dropbox_client_factory
+
+
+ #catchall errors
+ def finish_request(self,*args,**kwargs):
+ try:
+ BaseHTTPServer.HTTPServer.finish_request(self,*args,**kwargs)
+ except Exception as e:
+ sys.stderr.write("Uncought response exception: %s\n"%str(e))
+
+
+
+
BIN  drapache/httpserver.pyc
Binary file not shown
32 drapache/index_generator.py
@@ -0,0 +1,32 @@
+"""
+The index auto generator
+"""
+import os
+import jinja2
+from jinja2 import Environment,PackageLoader
+import dbapijinja
+
+jinja_env = Environment(loader=PackageLoader(__name__,'templates'))
+
+def get_index_file(file_list,folder_path,client):
+
+
+
+
+ files = []
+ for filemeta in file_list:
+ file_name = os.path.basename(filemeta['path'])
+ if filemeta['is_dir']:
+ file_name = file_name + '/'
+ files.append(file_name)
+
+ dropbox_env = jinja2.Environment(loader=dbapijinja.DropboxLoader(client,'/_templates'))
+
+ try:
+ custom_index_template = dropbox_env.get_template('index.html')
+ print 'hh'
+ return custom_index_template.render(files=files,path=folder_path)
+
+ except jinja2.TemplateNotFound:
+ index_template = jinja_env.get_template('index.html.tem')
+ return index_template.render(files=files,path=folder_path)
BIN  drapache/index_generator.pyc
Binary file not shown
23 drapache/instance.py
@@ -0,0 +1,23 @@
+from httpserver import DropboxMultiThreadHTTPServer,DropboxHTTPRequestHandler
+
+class Drapache:
+
+
+ def __init__(self):
+ self.port = None
+ self.subdomain_manager_factory = None
+ self.dropbox_client_factory = None
+
+ def start(self):
+
+ assert self.port
+ assert self.subdomain_manager_factory
+ assert self.dropbox_client_factory
+
+
+ server_address = ('0.0.0.0',self.port)
+ self.httpd = DropboxMultiThreadHTTPServer(server_address,DropboxHTTPRequestHandler)
+ self.httpd.set_config(self.subdomain_manager_factory,self.dropbox_client_factory)
+ self.httpd.serve_forever()
+
+
BIN  drapache/instance.pyc
Binary file not shown
121 drapache/mysql_connect.py
@@ -0,0 +1,121 @@
+"""
+Low level API for finding our mysql database, and returning a connection to it
+I am not trying to wrap MySQLdb here, just the guts of getting a connection object
+well i ended up wrapping it a little.
+enjoy.
+
+"""
+
+
+import MySQLdb
+from MySQLdb.cursors import Cursor,DictCursor
+
+
+
+class MysqlError(Exception):
+
+ def __init__(self,message,errcode=0):
+ Exception.__init__(self,message)
+ self.errcode = errcode
+
+class DBConnection:
+ """
+ An object oriented wrapper around a mysql connection
+ create it, and the connection is automatically established
+ to access the raw API, just use the .db attribute (it is the raw connection)
+ """
+
+ def __init__(self,mysql_dict=None):
+ self.db = get_db_connection(mysql_dict)
+
+ def execute_query(self,query_string,params=None,result_type='DICT'):
+ """
+ Executes the given query, and returns a generator containing the rows
+ row type can be either DICT, returning a dictionary per row
+ or 'LIST' which will return a list per row
+ """
+
+ if result_type == 'DICT':
+ cursor = self.db.cursor(DictCursor)
+ elif result_type == 'LIST':
+ cursor = self.db.cursor(Cursor)
+ else:
+ raise ValueError("result_type must be DICT or LIST")
+
+ try:
+ if params is None:
+ cursor.execute(query_string)
+ else:
+ cursor.execute(query_string,params)
+ except MySQLdb.Error as e:
+ #args is a tuple of (errcode,message)
+ raise MysqlError(e.args[1],e.args[0])
+
+
+ return query_result_set(cursor)
+
+ def execute_many(self,query_string,params,result_type='DICT'):
+
+ if result_type == 'DICT':
+ cursor = self.db.cursor(DictCursor)
+ elif result_type == 'LIST':
+ cursor = self.db.cursor(Cursor)
+ else:
+ raise ValueError("Only options for result type is 'DICT' or 'LIST'")
+
+ try:
+ cursor.executemany(query_string,params)
+ except MySQLdb.Error as e:
+ raise MysqlError(e.args[1],e.args[0])
+
+ return query_result_set(cursor)
+
+ def close(self):
+ self.db.close()
+
+ def escape_string(self,in_string):
+ return self.db.escape_string(in_string)
+
+ def get_mysql_list(self,input_sequence):
+ return "("+','.join(str(s) for s in input_sequence)+")"
+
+
+def _get_db_params(param_dict):
+ """
+ gets the database connection information
+ returns it as a dict
+ """
+ if param_dict is None:
+ import dropache_dbconfig as config
+
+ param_dict = {}
+ param_dict['user'] = config.USER
+ param_dict['passwd'] = config.PASS
+ param_dict['host'] = config.HOST
+ param_dict['db'] = config.DB
+
+ param_dict['use_unicode'] = True
+
+ return param_dict
+
+def get_db_connection(param_dict):
+ param_dict = _get_db_params(param_dict)
+ try:
+ return MySQLdb.connect(**param_dict)
+ except MySQLdb.MySQLError:
+ raise MysqlError("Unable to connect to mysql database")
+
+
+def query_result_set(cursor):
+
+ #for row in cursor.fetchall():
+ # yield row
+ #cursor.close()
+ #raise StopIteration
+ while True:
+ row = cursor.fetchone()
+ if row is None:
+ cursor.close()
+ raise StopIteration
+ else:
+ yield row
BIN  drapache/mysql_connect.pyc
Binary file not shown
78 drapache/subdomain.py
@@ -0,0 +1,78 @@
+"""
+Module for handling users, the oauth tokens
+
+THIS COULD AND SHOULD BE OPTIMIZE WITH CACHEINGINGINGINGIN!!!!
+"""
+import mysql_connect
+
+class SubdomainException(Exception):
+ pass
+
+
+class SubdomainManager:
+ """
+ Base class for subdomain manager
+ really an interface
+ """
+
+ def check_subdomain(subdomain):
+ raise SubdomainException("not implemented")
+
+ def get_token(subdomain):
+ raise SubdomainException("not implemented")
+
+class MysqlSubdomainManager:
+
+ def __init__(self,mysql_dict):
+ self.db_connection = mysql_connect.DBConnection(mysql_dict)
+
+
+ def check_subdomain(self,subdomain):
+ """
+ Returns a boolean whether or not this is a valid user
+ """
+ try:
+ CHECK_SUBDOMAIN = "SELECT count(*) as count FROM subdomains WHERE subdomain=%s"
+ result = self.db_connection.execute_query(CHECK_SUBDOMAIN,subdomain)
+ return result.next()['count'] == 1
+ except Exception as e:
+ raise SubdomainException(e.message)
+
+
+ def get_token(self,subdomain):
+ """
+ returns a (oauth_token,oauth_token_secret) tuple for the given user, or None
+ """
+ try:
+ SUBDOMAIN_QUERY = "SELECT oauth_token,oauth_token_secret FROM subdomains WHERE subdomain=%s"
+ result = self.db_connection.execute_query(SUBDOMAIN_QUERY,subdomain)
+ row = result.next()
+ return (row['oauth_token'],row['oauth_token_secret'])
+ except Exception as e:
+ raise SubdomainException(e.message)
+
+class FlatFileSubdomainManager:
+
+ def __init__(self,filename):
+ """
+ reads the file into memory
+ subdomain|oauth_token|oauth_token_secret
+ """
+
+ self.subdomains_oauth_map = {}
+ f = open(filename)
+ for line in f:
+ line = line.strip()
+ subdomain,oauth_token,oauth_token_secret = line.split('|')
+ self.subdomains_oauth_map[subdomain] = (oauth_token,oauth_token_secret)
+ f.close()
+
+ def check_subdomains(self,subdomain):
+ return subdomain in self.subdomains_oauth_map
+
+ def get_token(self,subdomain):
+ return self.subdomains_oauth_map.get(subdomain)
+
+
+
+
BIN  drapache/subdomain.pyc
Binary file not shown
19 drapache/templates/index.html.tem
@@ -0,0 +1,19 @@
+<html>
+
+<head>
+<title>Index - {{path}}</title>
+</head>
+
+
+<body>
+
+ <h1> Index for {{path}} </h1>
+
+ {% for file in files %}
+ <a href="{{file}}">{{file}}</a><br>
+ {% endfor %}
+
+</body>
+
+</html>
+
122 drapache/twistd_resource.py
@@ -0,0 +1,122 @@
+"""
+The http server implementation
+"""
+
+import BaseHTTPServer
+import SocketServer
+import re
+import os
+import sys
+import urlparse
+import urllib
+
+import threading
+
+import dbapiserver
+
+from twisted.web import server, resource
+from twisted.internet import threads, reactor, defer
+
+ErrorPage = resource.ErrorPage
+
+class DrapacheTwistdResource(resource.Resource):
+
+ isLeaf = True
+
+ def __init__(self,subdomain_manager_factory,dropbox_client_factory):
+ self.get_subdomain_manager = subdomain_manager_factory
+ self.get_dropbox_client = dropbox_client_factory
+
+ def render_GET(self,request):
+
+ try:
+ host_string = request.getHeader("Host")
+ host_rest = host_string.split(".",1) #at most one split
+ if len(host_rest) == 1:
+ subdomain = None
+ else:
+ subdomain = host_rest[0]
+
+ path = request.path
+ query_dict = request.args
+ query_string = urllib.urlencode(request.args)
+
+ if subdomain is None:
+ return ErrorPage(400,"Dropache requires a username as the route",None).render(request)
+
+
+ subdomain_manager = self.get_subdomain_manager()
+
+ try:
+ subdomain_exists = subdomain_manager.check_subdomain(subdomain)
+ except drapache_subdomain.SubdomainException as e:
+ return ErrorPage(503,"Error in subdomain lookup:\n"+e.message,None).render(request)
+
+
+ if not subdomain_exists:
+ return ErrorPage(404,"Subdomain %s does not exist"%subdomain,None).render(request)
+ return None
+
+
+ try:
+ subdomain_token = subdomain_manager.get_token(subdomain)
+ except drapache_subdomain.SubdomainException as e:
+ return ErrorPage(503,"Error in subdomain lookup:\n"+e.message,None).render(request)
+
+ subdomain_client = self.get_dropbox_client(subdomain_token)
+
+ file_server = dbapiserver.FileServer(subdomain_client,query_dict,query_string)
+
+
+ #i think i have to overload this beause of threads?
+ #deferred = threads.deferToThread(file_server.serve,path)
+ deferred = defer.Deferred()
+ def on_finish(success,result):
+ sys.stderr.write("at leat the thread finished\n")
+ if success:
+ sys.stderr.write("success\n")
+ reactor.wakeUp()
+
+ reactor.callFromThread(deferred.callback,result)
+ else:
+ sys.stderr.write("fail\n")
+ reactor.callFromThread(deferred.errback,result)
+
+ reactor.getThreadPool().callInThreadWithCallback(on_finish,file_server.serve,path)
+
+
+
+ def response_failed(err,deferr):
+ sys.stderr.write('foo')
+ deferr.cancel()
+
+ def response_errback(err):
+ sys.stderr.write('here;!!!\n')
+
+ def response_callback(response):
+ sys.stderr.write('hmm\n')
+ if response.error:
+ #ErrorPage(response.status,response.body,None).render(request)
+ request.write("fuck")
+ request.finish()
+
+ else:
+ request.setResponseCode(response.status)
+ for h,v in response.headers.items():
+ request.setHeader(h,v)
+ request.write(str(response.body))
+ request.finish()
+
+ request.notifyFinish().addErrback(response_failed,deferred)
+ deferred.addErrback(response_errback)
+ deferred.addCallback(response_callback)
+
+ return server.NOT_DONE_YET
+
+
+ except Exception as e:
+ sys.stderr.write('hmmm!!!ahaha\n')
+ return ErrorPage(500,str(e),None).render(request)
+
+
+
BIN  drapache/twistd_resource.pyc
Binary file not shown
27 drapache/util.py
@@ -0,0 +1,27 @@
+"""
+Refactoring utilitys
+"""
+
+
+
+
+class ResponseObject:
+
+ def __init__(self,status,body,headers=None,error=False):
+ self.status = status
+ self.body = body
+ self.error = error
+ if headers is None:
+ self.headers = {}
+ else:
+ self.headers = headers
+
+
+
+class DropacheException(Exception):
+
+ def __init__(self,status,message=""):
+
+ self.status = status
+ self.message = message
+
BIN  drapache/util.pyc
Binary file not shown
5 requirements.txt
@@ -0,0 +1,5 @@
+dropbox==1.3
+-e git+git://github.com/haypo/pysandbox.git#egg=sandbox
+
+### only needed if using mysql for the subdomain list
+Mysql-python
29 sample_config.py
@@ -0,0 +1,29 @@
+"""
+Provides configuration for the drapache server
+"""
+
+################## mysql config
+# mysql host
+HOST = ""
+
+# mysql user
+USER = ""
+
+# mysql password
+PASS = ""
+
+# mysql database
+DB = ""
+
+################### flat file
+# subdomain file path
+SUBDOMAIN_FILE = ""
+
+
+################## dropbox config
+# api app-key
+APP_KEY = ''
+
+# api app-secret
+APP_SECRET = ''
+
32 sample_server.py
@@ -0,0 +1,32 @@
+import drapache
+import os
+
+import sample_config as config
+
+def run():
+
+ param_dict = {}
+ param_dict['user'] = config.USER
+ param_dict['passwd'] = config.PASS
+ param_dict['host'] = config.HOST
+ param_dict['db'] = config.DB
+ def subdomain_manager_factory():
+ return drapache.subdomain.MysqlSubdomainManager(param_dict)
+
+ #to use a flat file
+ def subdomain_manager_factort():
+ return drapache.subdomain.FlatFileSubdomainManager(config.SUBDOMAIN_FILE)
+
+ dropbox_client_generator = drapache.dropbox_access.DropboxClientCreator(config.APP_KEY,config.APP_SECRET)
+ def dropbox_client_factory(token_tuple):
+ return dropbox_client_generator.get_client(*token_tuple)
+
+ instance = drapache.Drapache()
+ instance.port = os.environ.get('PORT',5501)
+ instance.subdomain_manager_factory = subdomain_manager_factory
+ instance.dropbox_client_factory = dropbox_client_factory
+ instance.start()
+
+if __name__=="__main__":
+
+ run()
11 setup.py
@@ -0,0 +1,11 @@
+from distutils.core import setup
+
+
+setup(
+ name='drapache',
+ version='0.1',
+ description='Dropbox-backed web server',
+ url='get.drapache.com',
+ author='Louis Sobel',
+ packages=["drapache"]
+)
Please sign in to comment.
Something went wrong with that request. Please try again.