new
This commit is contained in:
@ -1,76 +0,0 @@
|
|||||||
import BaseHTTPServer
|
|
||||||
#import Cheetah.Template
|
|
||||||
import SocketServer
|
|
||||||
#import cgi
|
|
||||||
import threading
|
|
||||||
|
|
||||||
class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|
||||||
server_version = "yadynHTTP/1.0"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setParams(cls, entries):
|
|
||||||
cls.entries = entries
|
|
||||||
|
|
||||||
# def do_POST(self):
|
|
||||||
# self.log_message("Request: %s", self.path)
|
|
||||||
# sid = self.getSidFromCookie()
|
|
||||||
# self.log_message("Cookie: %s" % self.headers.get('Cookie'))
|
|
||||||
#
|
|
||||||
# d = HscWebApp(sid)
|
|
||||||
#
|
|
||||||
# l = int(self.headers.get('Content-Length', '0'))
|
|
||||||
# q = self.rfile.read(l)
|
|
||||||
# e = cgi.parse_qs(q)
|
|
||||||
# self.log_message("QUERY: %s" % str(e))
|
|
||||||
# d.setParams(Params(e))
|
|
||||||
#
|
|
||||||
# d.processAction()
|
|
||||||
#
|
|
||||||
# self.send_response(303, "See Other")
|
|
||||||
# self.setSidInCookie(d.sid)
|
|
||||||
# self.send_header("Location", "/")
|
|
||||||
# self.end_headers()
|
|
||||||
#
|
|
||||||
# d.finish()
|
|
||||||
|
|
||||||
def do_GET(self):
|
|
||||||
self.log_message("Request: %s", self.path)
|
|
||||||
cmd = self.path.split('/')[1]
|
|
||||||
dynid = self.path.split('/')[2]
|
|
||||||
|
|
||||||
d = ''
|
|
||||||
if cmd == 'status':
|
|
||||||
if dynid == 'all':
|
|
||||||
for entry in self.entries.values():
|
|
||||||
d += str(entry) + '\n'
|
|
||||||
elif self.entries.has_key(dynid):
|
|
||||||
d = str(self.entries[dynid])
|
|
||||||
else:
|
|
||||||
d = "unknown dynid"
|
|
||||||
else:
|
|
||||||
d = "unknown cmd"
|
|
||||||
|
|
||||||
self.send_response(200, "OK")
|
|
||||||
|
|
||||||
self.send_header("Content-Type", "text/plain")
|
|
||||||
self.end_headers()
|
|
||||||
self.wfile.write(str(d))
|
|
||||||
|
|
||||||
|
|
||||||
class WebServer(SocketServer.ThreadingTCPServer):
|
|
||||||
def __init__(self, server_address, RequestHandlerClass):
|
|
||||||
self.allow_reuse_address = True
|
|
||||||
SocketServer.ThreadingTCPServer.__init__(self, server_address, RequestHandlerClass)
|
|
||||||
|
|
||||||
|
|
||||||
class WebHandler(threading.Thread):
|
|
||||||
def __init__(self, webAddr, entries):
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
self.webAddr = webAddr
|
|
||||||
MyHandler.setParams(entries)
|
|
||||||
self.setDaemon(True)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
WebServer(self.webAddr, MyHandler).serve_forever()
|
|
||||||
|
|
||||||
|
|
222
server/WebReceiver.py
Normal file
222
server/WebReceiver.py
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
from params import Params
|
||||||
|
from session import Session, SessionContainer
|
||||||
|
import BaseHTTPServer
|
||||||
|
import Cheetah.Template
|
||||||
|
import Cookie
|
||||||
|
import SocketServer
|
||||||
|
import StringIO
|
||||||
|
import cgi
|
||||||
|
import sys
|
||||||
|
import uuid
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
from logger import Logger
|
||||||
|
|
||||||
|
|
||||||
|
TEMPLATE_DIR='./templates'
|
||||||
|
|
||||||
|
|
||||||
|
class YaDynWebApp(object):
|
||||||
|
@classmethod
|
||||||
|
def setSessionContainer(cls, sessionContainer):
|
||||||
|
cls.sessionContainer = sessionContainer
|
||||||
|
|
||||||
|
def __init__(self, sid):
|
||||||
|
self.buf = StringIO.StringIO()
|
||||||
|
self.params = None
|
||||||
|
self.session = YaDynWebApp.sessionContainer.get(sid)
|
||||||
|
self.error_msg = None
|
||||||
|
self.msg = None
|
||||||
|
self.sid = sid
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def log(self, msg):
|
||||||
|
Logger.log(msg)
|
||||||
|
|
||||||
|
def setParams(self, p):
|
||||||
|
self.params = p
|
||||||
|
|
||||||
|
def showPage(self, forcePage=''):
|
||||||
|
if self.session:
|
||||||
|
if forcePage != '':
|
||||||
|
page = forcePage
|
||||||
|
else:
|
||||||
|
page = self.session.redirectTarget
|
||||||
|
else:
|
||||||
|
page = 'login'
|
||||||
|
|
||||||
|
if page == 'logout':
|
||||||
|
YaDynWebApp.sessionContainer.delete(self.sid)
|
||||||
|
self.session = None
|
||||||
|
page = 'login'
|
||||||
|
|
||||||
|
templateFile = page + '.tmpl'
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.session:
|
||||||
|
self.log("showPage, session: %s" % str(self.session))
|
||||||
|
self.log("showPage, params: %s" % str(self.session.params))
|
||||||
|
self.session.setShownPage(page)
|
||||||
|
|
||||||
|
try:
|
||||||
|
loaderObjectClass = eval("loader_" + page)
|
||||||
|
loaderObject = loaderObjectClass(self.session)
|
||||||
|
loaderObject.mergeAdditionalData()
|
||||||
|
except NameError, e:
|
||||||
|
self.log("loader class for %s not found: %s" % (page, str(e)))
|
||||||
|
except HscException, e:
|
||||||
|
s = "HSC Failure loading data: %s" % str(e)
|
||||||
|
self.log(s)
|
||||||
|
raise Exception(s)
|
||||||
|
|
||||||
|
tmpl = Cheetah.Template.Template(file=TEMPLATE_DIR + '/' + templateFile,
|
||||||
|
searchList=[self])
|
||||||
|
#self.log("OUTPUT: %s" % str(tmpl))
|
||||||
|
self.buf.write(str(tmpl))
|
||||||
|
|
||||||
|
if self.session:
|
||||||
|
self.session.clearMsgs()
|
||||||
|
except Exception, e:
|
||||||
|
YaDynWebApp.sessionContainer.delete(self.sid)
|
||||||
|
self.buf.write("Problems to show this page: %s %s" % (e.__class__.__name__, str(e)))
|
||||||
|
finally:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def processAction(self):
|
||||||
|
if self.params.action == "Login":
|
||||||
|
authTuple = getAuthTuple(self.params.username, self.params.password)
|
||||||
|
if authTuple:
|
||||||
|
self.params = Params({})
|
||||||
|
self.sid = str(uuid.uuid1())
|
||||||
|
session = Session(self.sid, self.params)
|
||||||
|
|
||||||
|
session.setAuthTuple(authTuple)
|
||||||
|
session.setGoodMsg("Welcome")
|
||||||
|
session.setRedirectTarget("menu")
|
||||||
|
YaDynWebApp.sessionContainer.add(session)
|
||||||
|
else:
|
||||||
|
self.sid = 0
|
||||||
|
else:
|
||||||
|
if self.session == None:
|
||||||
|
self.sid = 0
|
||||||
|
else:
|
||||||
|
shownPage = self.session.getShownPage()
|
||||||
|
self.session.setParams(self.params)
|
||||||
|
self.log("shownPage: %s" % shownPage)
|
||||||
|
try:
|
||||||
|
dataObjectClass = eval(shownPage)
|
||||||
|
dataObject = dataObjectClass(self.session)
|
||||||
|
dataObject.process()
|
||||||
|
self.session.setGoodMsg("Action on %s performed successfully" % shownPage)
|
||||||
|
self.session.setRedirectTarget(dataObject.getNextPage())
|
||||||
|
except NameError, e:
|
||||||
|
self.log("processAction, NameError PARAMS: %s" % str(self.session.params))
|
||||||
|
s = "Internal program error: %s" % str(e)
|
||||||
|
self.session.setBadMsg(s)
|
||||||
|
self.log(s)
|
||||||
|
self.session.setRedirectTarget("menu")
|
||||||
|
except HscException, e:
|
||||||
|
self.log("processAction, HscException PARAMS: %s" % str(self.session.params))
|
||||||
|
s = "HSC Failure: %s" % str(e)
|
||||||
|
self.session.setBadMsg(s)
|
||||||
|
self.log(s)
|
||||||
|
self.session.setRedirectTarget(shownPage)
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.buf.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
|
server_version = "YaDynWebAppHTTP/1.0"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setParams(cls, entries):
|
||||||
|
cls.entries = entries
|
||||||
|
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
Logger.log(format%args)
|
||||||
|
|
||||||
|
def getSidFromCookie(self):
|
||||||
|
try:
|
||||||
|
cookie = Cookie.SimpleCookie(self.headers.get('Cookie'))
|
||||||
|
sid = cookie['sid'].value
|
||||||
|
except KeyError:
|
||||||
|
sid = None
|
||||||
|
return sid
|
||||||
|
|
||||||
|
def setSidInCookie(self, sid):
|
||||||
|
cookie = Cookie.SimpleCookie()
|
||||||
|
cookie['sid'] = sid
|
||||||
|
cookie['sid']['Max-Age'] = 10*60
|
||||||
|
self.wfile.write(str(cookie) + '\r\n')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
self.log_message("Request: %s", self.path)
|
||||||
|
sid = self.getSidFromCookie()
|
||||||
|
self.log_message("Cookie: %s" % self.headers.get('Cookie'))
|
||||||
|
|
||||||
|
d = YaDynWebApp(sid)
|
||||||
|
|
||||||
|
l = int(self.headers.get('Content-Length', '0'))
|
||||||
|
q = self.rfile.read(l)
|
||||||
|
e = cgi.parse_qs(q)
|
||||||
|
self.log_message("QUERY: %s" % str(e))
|
||||||
|
d.setParams(Params(e))
|
||||||
|
|
||||||
|
d.processAction()
|
||||||
|
|
||||||
|
self.send_response(303, "See Other")
|
||||||
|
self.setSidInCookie(d.sid)
|
||||||
|
self.send_header("Location", "/")
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
|
d.finish()
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
self.log_message("Request: %s", self.path)
|
||||||
|
sid = self.getSidFromCookie()
|
||||||
|
self.log_message("SID: %s" % sid)
|
||||||
|
|
||||||
|
d = YaDynWebApp(sid)
|
||||||
|
|
||||||
|
d.showPage(self.path.split('/')[1])
|
||||||
|
|
||||||
|
self.send_response(200, "OK")
|
||||||
|
|
||||||
|
self.send_header("Content-Type", "text/html")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(str(d))
|
||||||
|
|
||||||
|
d.finish()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MyServer(SocketServer.ThreadingTCPServer):
|
||||||
|
def __init__(self, server_address, RequestHandlerClass):
|
||||||
|
self.allow_reuse_address = True
|
||||||
|
SocketServer.ThreadingTCPServer.__init__(self, server_address, RequestHandlerClass)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class WebReceiver(threading.Thread):
|
||||||
|
def __init__(self, webAddr, entries):
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
self.webAddr = webAddr
|
||||||
|
MyHandler.setParams(entries)
|
||||||
|
self.setDaemon(True)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.sessionContainer = SessionContainer(60, 600)
|
||||||
|
self.sessionContainer.setDaemon(True)
|
||||||
|
self.sessionContainer.start()
|
||||||
|
YaDynWebApp.setSessionContainer(self.sessionContainer)
|
||||||
|
|
||||||
|
MyServer(self.webAddr, MyHandler).serve_forever()
|
||||||
|
|
26
server/logger.py
Normal file
26
server/logger.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import syslog
|
||||||
|
|
||||||
|
|
||||||
|
class Logger(object):
|
||||||
|
def log(data):
|
||||||
|
syslog.syslog(syslog.LOG_INFO, data)
|
||||||
|
if Logger.debugFlag:
|
||||||
|
print data
|
||||||
|
|
||||||
|
def debug(data):
|
||||||
|
syslog.syslog(syslog.LOG_DEBUG, data)
|
||||||
|
if Logger.debugFlag:
|
||||||
|
print data
|
||||||
|
|
||||||
|
def openlog():
|
||||||
|
syslog.openlog('hsc', syslog.LOG_PID, syslog.LOG_LOCAL0)
|
||||||
|
|
||||||
|
def debugEnable():
|
||||||
|
Logger.debugFlag = True
|
||||||
|
|
||||||
|
log = staticmethod(log)
|
||||||
|
debug = staticmethod(debug)
|
||||||
|
openlog = staticmethod(openlog)
|
||||||
|
debugEnable = staticmethod(debugEnable)
|
||||||
|
debugFlag = False
|
||||||
|
|
30
server/params.py
Normal file
30
server/params.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
from logger import Logger
|
||||||
|
|
||||||
|
class Params(object):
|
||||||
|
def __init__(self, e):
|
||||||
|
Logger.log("Params: %s" % str(e))
|
||||||
|
for k in e.keys():
|
||||||
|
self.__dict__[k] = e[k][0]
|
||||||
|
|
||||||
|
def __getattr__(self, a):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def get(self, k):
|
||||||
|
if self.__dict__.has_key(k):
|
||||||
|
return self.__dict__[k]
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def check_existance(self, l):
|
||||||
|
for i in l:
|
||||||
|
if not self.__dict__.has_key(i):
|
||||||
|
raise KeyError, i
|
||||||
|
|
||||||
|
def items(self):
|
||||||
|
for k in self.__dict__.keys():
|
||||||
|
yield (k, self.__dict__[k])
|
||||||
|
return
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.__dict__)
|
||||||
|
|
110
server/session.py
Normal file
110
server/session.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import time
|
||||||
|
import threading
|
||||||
|
from logger import Logger
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SessionContainer(threading.Thread):
|
||||||
|
def __init__(self, checkPeriod, expiryTime):
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
self.checkPeriod = checkPeriod
|
||||||
|
self.expiryTime = expiryTime
|
||||||
|
self.sessions = {}
|
||||||
|
self.sessionsLock = threading.Lock()
|
||||||
|
|
||||||
|
def get(self, sid):
|
||||||
|
if not sid:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
self.sessionsLock.acquire()
|
||||||
|
session = self.sessions[sid]
|
||||||
|
session.updateTimestamp()
|
||||||
|
return session
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
finally:
|
||||||
|
self.sessionsLock.release()
|
||||||
|
|
||||||
|
def delete(self, sid):
|
||||||
|
if sid:
|
||||||
|
try:
|
||||||
|
self.sessionsLock.acquire()
|
||||||
|
del self.sessions[sid]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
self.sessionsLock.release()
|
||||||
|
|
||||||
|
def add(self, session):
|
||||||
|
try:
|
||||||
|
self.sessionsLock.acquire()
|
||||||
|
self.sessions[session.getId()] = session
|
||||||
|
finally:
|
||||||
|
self.sessionsLock.release()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
self.sessionsLock.acquire()
|
||||||
|
|
||||||
|
currentTime = int(time.time())
|
||||||
|
for session in self.sessions.values():
|
||||||
|
Logger.log("SessionContainer: checking session %s, expires in %d s" % (session.getId(),
|
||||||
|
(session.getTimestamp() + self.expiryTime) - currentTime))
|
||||||
|
if (session.getTimestamp() + self.expiryTime) < currentTime:
|
||||||
|
Logger.log("SessionContainer: about to drop session %s" % session.getId())
|
||||||
|
del self.sessions[session.getId()]
|
||||||
|
finally:
|
||||||
|
self.sessionsLock.release()
|
||||||
|
|
||||||
|
time.sleep(self.checkPeriod)
|
||||||
|
|
||||||
|
|
||||||
|
class Session(object):
|
||||||
|
def __init__(self, id, params):
|
||||||
|
self.timestamp = int(time.time())
|
||||||
|
self.id = id
|
||||||
|
self.params = params
|
||||||
|
self.badmsg = ''
|
||||||
|
self.goodmsg = ''
|
||||||
|
self.role = ''
|
||||||
|
|
||||||
|
def updateTimestamp(self):
|
||||||
|
self.timestamp = int(time.time())
|
||||||
|
|
||||||
|
def getTimestamp(self):
|
||||||
|
return self.timestamp
|
||||||
|
|
||||||
|
def getId(self):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
def setParams(self, params):
|
||||||
|
self.params = params
|
||||||
|
|
||||||
|
def getParams(self):
|
||||||
|
return self.params
|
||||||
|
|
||||||
|
def setGoodMsg(self, msg):
|
||||||
|
self.goodmsg = msg
|
||||||
|
|
||||||
|
def setBadMsg(self, msg):
|
||||||
|
self.badmsg = msg
|
||||||
|
|
||||||
|
def setRedirectTarget(self, target):
|
||||||
|
self.redirectTarget = target
|
||||||
|
|
||||||
|
def setShownPage(self, page):
|
||||||
|
self.page = page
|
||||||
|
|
||||||
|
def getShownPage(self):
|
||||||
|
return self.page
|
||||||
|
|
||||||
|
def setAuthTuple(self, authTuple):
|
||||||
|
self.authTuple = authTuple
|
||||||
|
self.username = authTuple['username']
|
||||||
|
self.password = authTuple['password']
|
||||||
|
self.role = authTuple['role']
|
||||||
|
|
||||||
|
def clearMsgs(self):
|
||||||
|
self.badmsg = ''
|
||||||
|
self.goodmsg = ''
|
3
server/templates/admin_menu.inc
Normal file
3
server/templates/admin_menu.inc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<ul>
|
||||||
|
<li><a href="/person">Person</a></li>
|
||||||
|
</ul>
|
4
server/templates/any_menu.inc
Normal file
4
server/templates/any_menu.inc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<ul>
|
||||||
|
<li><a href="/change_password">Change password</a></li>
|
||||||
|
<li><a href="/logout">Logout</a></li>
|
||||||
|
</ul>
|
4
server/templates/footer.inc
Normal file
4
server/templates/footer.inc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
42
server/templates/header.inc
Normal file
42
server/templates/header.inc
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>YaDyn</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="head">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="goodmsg">
|
||||||
|
#if $session and $session.goodmsg
|
||||||
|
<p>
|
||||||
|
<font color="green">$session.goodmsg</font>
|
||||||
|
</p>
|
||||||
|
#end if
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="badmsg">
|
||||||
|
#if $session and $session.badmsg
|
||||||
|
<p>
|
||||||
|
<font color="red">$session.badmsg</font>
|
||||||
|
</p>
|
||||||
|
#end if
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="menu">
|
||||||
|
<p>
|
||||||
|
#if $session
|
||||||
|
#include "templates/any_menu.inc"
|
||||||
|
#end if
|
||||||
|
|
||||||
|
#if $session and $session.role == "user"
|
||||||
|
#include "templates/user_menu.inc"
|
||||||
|
#end if
|
||||||
|
|
||||||
|
#if $session and $session.role == "admin"
|
||||||
|
#include "templates/admin_menu.inc"
|
||||||
|
#end if
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="main">
|
||||||
|
<p>
|
17
server/templates/login.tmpl
Normal file
17
server/templates/login.tmpl
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "templates/header.inc"
|
||||||
|
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>Username:</td><td><input type="text" name="username" value=""/></td>
|
||||||
|
</tr><tr>
|
||||||
|
<td>Password:</td><td><input type="password" name="password" value=""/></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
<input type="submit" name="action" value="Login"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
#include "templates/footer.inc"
|
1
server/templates/logout.tmpl
Normal file
1
server/templates/logout.tmpl
Normal file
@ -0,0 +1 @@
|
|||||||
|
bye
|
3
server/templates/user_menu.inc
Normal file
3
server/templates/user_menu.inc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<ul>
|
||||||
|
<li><a href="/personal_statistics">View your personal statistics</a></li>
|
||||||
|
</ul>
|
@ -8,7 +8,7 @@ import DynHandler
|
|||||||
# import DnsHandler
|
# import DnsHandler
|
||||||
import Expirer
|
import Expirer
|
||||||
import DynReceiver
|
import DynReceiver
|
||||||
import WebHandler
|
import WebReceiver
|
||||||
import Entry
|
import Entry
|
||||||
import Event
|
import Event
|
||||||
|
|
||||||
@ -44,8 +44,8 @@ try:
|
|||||||
expirer = Expirer.Expirer(entries, EVENT_LIFE_TIME, NULL_ADDRESS)
|
expirer = Expirer.Expirer(entries, EVENT_LIFE_TIME, NULL_ADDRESS)
|
||||||
expirer.start()
|
expirer.start()
|
||||||
|
|
||||||
webHandler = WebHandler.WebHandler(("", 8080), entries)
|
webReceiver = WebReceiver.WebReceiver(("", 8080), entries)
|
||||||
webHandler.start()
|
webReceiver.start()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
entries.sync()
|
entries.sync()
|
||||||
|
Reference in New Issue
Block a user