diff --git a/server.py b/server.py index 65f5f78..b03c635 100644 --- a/server.py +++ b/server.py @@ -1,13 +1,6 @@ -import http.server -import os -import time -import base64 -import re +import os, re, time, base64 import supybot.utils as utils -import sqlite3 -import collections -import urllib.parse -from io import StringIO +import http.server, sqlite3 host = 'http://domain.tld' port = 80 @@ -16,11 +9,11 @@ webpath = '/bantracker' username = 'username' password = 'password' filename = '/home/botaccount/data/networkname/ChanTracker.db' -channels = [] # empty to allows view of all channels recorded, otherwise restrict the views to channels +channels = [] # empty to allow view of all channels recorded, otherwise restrict the views to channels # usage python server.py auth = '%s:%s' % (username,password) -base64string = base64.b64encode(auth.encode('UTF-8')) +base64string = base64.b64encode(auth.encode('utf-8')).decode('utf-8') def timeElapsed(elapsed, short=False, leadingZeroes=False, years=True, weeks=True, days=True, hours=True, minutes=True, seconds=True): @@ -38,7 +31,7 @@ def timeElapsed(elapsed, short=False, leadingZeroes=False, years=True, ret.append(format('%n', (i, s))) elapsed = int(elapsed) assert years or weeks or days or \ - hours or minutes or seconds, 'One flag must be True' + hours or minutes or seconds, 'One flag must be True' if years: (yrs, elapsed) = (elapsed // 31536000, elapsed % 31536000) Format('year', yrs) @@ -65,21 +58,35 @@ def timeElapsed(elapsed, short=False, leadingZeroes=False, years=True, else: return format('%L', ret) -class MyHandler( http.server.BaseHTTPRequestHandler ): - server_version= "Ircd-Seven/1.1" +class MyHandler(http.server.BaseHTTPRequestHandler): if not standalone: def log_request(self, *args): pass # disable logging - def do_GET( self ): - self.page( self.path ) + def do_GET(self): + self.page(self.path) + + def page(self, query): + def write(subtitle, body): + page = [ + '', + '\nBanTracker%s' % (' » %s' % subtitle if subtitle else ''), + '', + '', + '\n' + ] + body + ['\n'] + self.send_response(200) + self.send_header("Content-type","text/html") + full = '\n'.join(page) + print('HTML lines %s' % len(full)) + self.send_header("Content-length",len(full)) + self.end_headers() + self.wfile.write(full.encode('utf-8')) - def page (self,query): if standalone: h = '%s:%s/' % (host,port) else: h = '%s/' % webpath - body = [] if not query: return if query.startswith('/?username='): @@ -93,287 +100,260 @@ class MyHandler( http.server.BaseHTTPRequestHandler ): if aa[0] == 'password': p = aa[1] if u and p: - raw = base64.encodebytes(bytes('%s:%s' % (u,p), 'UTF-8'))[:-1] - if not raw == base64string: + raw = base64.b64encode('%s:%s'.encode('utf-8') % (u,p)).decode('utf-8') + if raw != base64string: query = '' else: - query = '/?hash=%s' % base64string.decode('UTF-8') + query = '/?hash=%s' % base64string if not query.startswith('/?hash='): - body.append('\n\nChanTracker\n') - body.append('\n') - body.append("\n\n") - body.append('
\n' % h) - body.append('

Username:

\n') - body.append('

Password:

\n') - body.append('\n') - body.append("
\n") - body.append("\n\n") - self.send_response(200) - self.send_header("Content-type","text/html") - full = ''.join(body) - self.send_header("Content-length",str(len(full))) - self.end_headers() - self.wfile.write(full.encode('UTF-8')) + subtitle = '' + body = [ + '
' % h, + '

Username:

', + '

Password:

', + '', + '
' + ] + write(subtitle, body) return - if query.startswith('/?hash='): - a = query.split('&')[0] - a = a.replace('/?hash=','') - query = query.replace('%3D','=') - query = query.replace('/?hash=%s' % base64string.decode('UTF-8'),'/') - q = '?hash=%s' % base64string.decode('UTF-8') - query = urllib.parse.unquote( query ) - print(query) - body.append('\n\nBanTracker - %s\n' % query) - body.append('\n') - body.append('\n') -# body.append('\n') - body.append('\n\n') - body.append('
\n') - body.append('
\n' % q) - body.append('
') - body.append('' % base64string.decode('UTF-8')) - body.append('\n') - body.append('\n') - body.append('
\n') - body.append('
\n') + query = query.replace('%3D','=') + query = query.replace('/?hash=%s' % base64string,'') + query = query.lstrip('&') + q = '?hash=%s' % base64string + query = utils.web.urlunquote(query) + subtitle = '' + body = [ + '
', + '
' % q, + '
', + '' % base64string, + '', + '', + '
', + '
' + ] + if not query: + write(subtitle, body) + return + print(query) + subtitle = query db = self._getbandb() c = db.cursor() - if query: - ar = [] - if query.startswith('/&id='): - search = query.split('/&id=')[1] - c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=? ORDER BY id DESC""",(search,)) - r = c.fetchall() + ar = [] + if query.startswith('id='): + search = query.split('=')[1] + si = int(search) + c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(si,)) + r = c.fetchall() + if len(r): + ban = r[0] + (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban + if not channels or channel in channels: + body.extend([ + '

#%d

' % id, + '

#%d by %s' % (id,h,q,utils.web.urlencode({'oper':oper}),oper), + 'in %s:' % (h,q,channel.split('#')[1],channel), + '+%s %s

' % (kind,h,q,utils.web.urlencode({'mask':mask}),mask), + '

Begin at %s

' % time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at))) + ]) + was = float(begin_at) == float(end_at) + if was: + was = 'forever' + else: + was = timeElapsed(float(end_at) - float(begin_at)) + body.append('

Original duration: %s

' % was) + if not removed_at: + if was != 'forever': + body.append('

It will expire in %s

' % timeElapsed(float(end_at) - time.time())) + else: + body.extend(['

Removed after %s' % timeElapsed(float(removed_at)-float(begin_at)), + 'on %s' % time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(removed_at))), + 'by %s

' % (h,q,utils.web.urlencode({'removed_by':removed_by}),removed_by)]) + c.execute("""SELECT full,log FROM nicks WHERE ban_id=?""",(id,)) + r = c.fetchall() + if len(r): + body.append('

Logs

') + for nick in r: + (full,log) = nick + body.append('

for %s

' % full) + if log != '': + body.append('') + c.execute("""SELECT oper,at,comment FROM comments WHERE ban_id=?""",(id,)) + r = c.fetchall() + if len(r): + body.extend(['

Comments

', '') + c.close() + write(subtitle, body) + return + elif query.startswith('channel='): + search = '#'+query.split('=')[1] + c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE channel=? ORDER BY id DESC""",(search,)) + r = c.fetchall() + if len(r): + ar.extend(r) + elif query.startswith('removed_by='): + search = query.split('=')[1] + c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE removed_by=? ORDER BY id DESC""",(search,)) + r = c.fetchall() + if len(r): + ar.extend(r) + elif query.startswith('oper='): + search = query.split('=')[1] + c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE oper=? ORDER BY id DESC""",(search,)) + r = c.fetchall() + if len(r): + ar.extend(r) + elif query.startswith('mask='): + search = query.split('=')[1] + sg = '*%s*' % search + sl = '%%%s%%' % search + c.execute("""SELECT ban_id,full FROM nicks WHERE full GLOB ? OR full LIKE ? OR log GLOB ? OR log LIKE ? ORDER BY ban_id DESC""",(sg,sl,sg,sl)) + r = c.fetchall() + L = [] + a = {} + if len(r): + d = {} + for ban in r: + (id,full) = ban + if id not in d: + d[id] = id + for id in d: + c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(id,)) + r = c.fetchall() + if len(r): + for ban in r: + a[ban[0]] = ban + c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE mask GLOB ? OR mask LIKE ? ORDER BY id DESC""",(sg,sl)) + r = c.fetchall() + if len(r): + for ban in r: + a[ban[0]] = ban + if len(a): + ar = [] + for ban in list(a.keys()): + ar.append(a[ban]) + ar.sort(key=lambda x: x[0], reverse=True) + elif query.startswith('search='): + search = query.split('=')[1] + search = search.replace('+','*') + print(search) + if search: + if not re.match(r'^[0-9]+$', search): + sg = '*%s*' % search + sl = '%%%s%%' % search + si = None + c.execute("""SELECT ban_id,full FROM nicks WHERE full GLOB ? OR full LIKE ? OR log GLOB ? OR log LIKE ? ORDER BY ban_id DESC""",(sg,sl,sg,sl)) + r = c.fetchall() + else: + si = int(search) + r = [] + L = [] + a = {} if len(r): - ban = r[0] - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban - if not len(channels) or channel in channels: - body.append('

#%s

\n' % id) - body.append('

#%s by %s in %s : +%s : %s

\n' % (id,oper,channel,kind,mask)) - body.append('

Begin at %s

\n' % time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at)))) - was = float(begin_at) == float(end_at) - if was: - was = 'forever' - else: - was = timeElapsed(float(end_at) - float(begin_at)) - body.append('

Original duration : %s

\n' % was) - if not removed_at: - if was != 'forever': - body.append('

%s

\n' % 'It will expire in %s' % timeElapsed(float(end_at) - time.time())) - else: - body.append('

%s

\n' % 'Removed after %s on %s by %s' % (timeElapsed(float(removed_at)-float(begin_at)),time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(removed_at))),removed_by)) - c.execute("""SELECT full, log FROM nicks WHERE ban_id=?""",(id,)) - r = c.fetchall() - if len(r): - users = r - body.append('

Logs

\n') - for u in users: - (full,log) = u - body.append('

for %s

\n' % full) - if log != '': - body.append('\n') - c.execute("""SELECT oper, at, comment FROM comments WHERE ban_id=?""",(id,)) - r = c.fetchall() - if len(r): - body.append('

Comments

\n') - body.append('\n') - elif query.startswith('/&channel='): - search = '#'+query.split('/&channel=')[1] - c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE channel=? ORDER BY id DESC""",(search,)) - r = c.fetchall() - if len(r): - bans = r - for ban in bans: - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban - ar.append([int(id),channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by]) - elif query.startswith('/&removed_by='): - search = query.split('/&removed_by=')[1] - c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE removed_by=? ORDER BY id DESC""",(search,)) - r = c.fetchall() - if len(r): - bans = r - for ban in bans: - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban - ar.append([int(id),channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by]) - elif query.startswith('/&oper='): - search = query.split('/&oper=')[1] - c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE oper=? ORDER BY id DESC""",(search,)) - r = c.fetchall() - if len(r): - bans = r - for ban in bans: - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban - ar.append([int(id),channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by]) - elif query.startswith('/&mask='): - search = query.split('/&mask=')[1] - glob = '*%s*' % search - like = '%'+search+'%' - c.execute("""SELECT ban_id, full FROM nicks WHERE full GLOB ? OR full LIKE ? OR log GLOB ? OR log LIKE ? ORDER BY ban_id DESC""",(glob,like,glob,like)) - L = [] - a = {} - r = c.fetchall() - if len(r): - bans = r d = {} - for ban in bans: + for ban in r: (id,full) = ban - if not id in d: + if id not in d: d[id] = id for id in d: - c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=? ORDER BY id DESC""",(int(id),)) + c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(id,)) r = c.fetchall() if len(r): - bans = r - for ban in bans: - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban - a[str(id)] = ban - c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE mask GLOB ? OR mask LIKE ? ORDER BY id DESC""",(glob,like)) + for ban in r: + a[ban[0]] = ban + if not si: + c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE mask GLOB ? OR mask LIKE ? OR channel GLOB ? OR channel LIKE ? OR oper GLOB ? OR oper LIKE ? ORDER BY id DESC""",(sg,sl,sg,sl,sg,sl)) + else: + c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(si,)) r = c.fetchall() if len(r): - bans = r - for ban in bans: - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban - a[str(id)] = ban - if len(a): - ar = [] - for ban in a: - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = a[ban] - ar.append([int(id),channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by]) - def sort_function (item): - return item[0] - ar.sort(key=sort_function) - ar.sort(reverse=True) - elif query.startswith('/&search='): - search = query.split('/&search=')[1] - search = search.replace('+','*') - print(search) - if search: - s = '*%s*' % search - qu = '%'+search+'%' - c.execute("""SELECT ban_id, full FROM nicks WHERE full GLOB ? OR full LIKE ? OR log GLOB ? OR log LIKE ? ORDER BY ban_id DESC""",(s,qu,s,qu)) - L = [] - a = {} + for ban in r: + a[ban[0]] = ban + if not si: + c.execute("""SELECT ban_id, comment FROM comments WHERE comment GLOB ? OR comment LIKE ? ORDER BY ban_id DESC""",(sg,sl)) r = c.fetchall() - if len(r): - bans = r - d = {} - for ban in bans: - (id,full) = ban - if not id in d: - d[id] = id - for id in d: - c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=? ORDER BY id DESC""",(int(id),)) + else: + r = [] + d = {} + if len(r): + for ban in r: + (id,full) = ban + d[id] = id + for id in d: + if id not in a: + c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(id,)) r = c.fetchall() if len(r): - bans = r - for ban in bans: - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban - a[id] = ban - c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE mask GLOB ? OR mask LIKE ? OR channel GLOB ? OR channel LIKE ? OR oper GLOB ? OR oper LIKE ? ORDER BY id DESC""",(s,qu,s,qu,s,qu)) - r = c.fetchall() - if len(r): - bans = r - for ban in bans: - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban - a[id] = ban - c.execute("""SELECT ban_id, comment FROM comments WHERE comment GLOB ? OR comment LIKE ? ORDER BY ban_id DESC""",(s,qu)) - r = c.fetchall() - d = {} - if len(r): - bans = r - for ban in bans: - (id,full) = ban - d[id] = id - for id in d: - if not id in a: - c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=? ORDER BY id DESC LIMIT 1""",(int(id),)) - r = c.fetchall() - if len(r): - bans = r - for ban in bans: - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban - a[id] = ban - if len(a): - ar = [] - for ban in a: - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = a[ban] - ar.append([int(id),channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by]) - def sort_function (item): - return item[0] - ar.sort(key=sort_function) - ar.sort(reverse=True) - else: - body.append('

nothing found

\n') - if len(ar): - print('found %s results' % len(ar)) - i = 0 - body.append('

results %s

' % search) - body.append('
\n') - body.append('\n') - body.append('\n') - while i < len(ar): - (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ar[i] - if not len(channels) or channel in channels: - body.append('\n') - body.append('\n' % (h,q,id,id)) - body.append('\n' % (h,q,channel.split('#')[1],channel)) - body.append('\n' % (h,q,urllib.parse.urlencode({'oper':oper}),oper)) - body.append('\n' % kind) - body.append('\n' % (h,q,urllib.parse.urlencode({'mask':mask}),mask)) - s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at))) - body.append('\n' % s) - if end_at and end_at != begin_at: - s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(end_at))) - body.append( '\n' % s) - else: - body.append( '') - if removed_at: - s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(removed_at))) - body.append( '' % s) - else: - body.append( '\n' ) - if removed_by: - body.append( '\n' % (h,q,urllib.parse.urlencode({'removed_by':removed_by}),removed_by)) - else: - body.append( '\n') + for ban in r: + a[ban[0]] = ban + if len(a): + ar = [] + for ban in list(a.keys()): + ar.append(a[ban]) + ar.sort(key=lambda x: x[0], reverse=True) + if len(ar): + print('Found %s results' % len(ar)) + body.extend([ + '

Results %s

' % search, + '
IDChannelOperatorKindTargetBegin dateEnd dateRemoved dateRemoved by
%s%s%s+%s%s%s%s%s%s
', + '', + '' + ]) + for ban in ar: + (id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban + if not channels or channel in channels: + s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at))) + body.extend([ + '', + '' % (h,q,id,id), + '' % (h,q,channel.split('#')[1],channel), + '' % (h,q,utils.web.urlencode({'oper':oper}),oper), + '' % kind, + '' % (h,q,utils.web.urlencode({'mask':mask}),mask), + '' % s + ]) + if end_at and end_at != begin_at: + s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(end_at))) + body.append('' % s) + else: + body.append('') + if removed_at: + s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(removed_at))) + body.append('' % s) + else: + body.append('') + if removed_by: + body.append('' % (h,q,utils.web.urlencode({'removed_by':removed_by}),removed_by)) + else: + body.append('') +# affected = '' +# try: +# c.execute("""SELECT full, log FROM nicks WHERE ban_id=?""",(id,)) +# affected = len(c.fetchall()) +# except: # affected = '' -# try: -# c.execute("""SELECT full, log FROM nicks WHERE ban_id=?""",(id,)) -# affected = len(c.fetchall()) -# except: -# affected = '' -# body.append( '\n' % affected) - body.append( '\n') - i = i+1 - body.append('\n') - body.append('
IDChannelOperatorTypeMaskBegin dateEnd dateRemovedRemoved by
%d%s%s+%s%s%s%s%s%s%s
\n') - body.append("") - self.send_response(200) - self.send_header("Content-type","text/html") - full = ''.join(body) - print('html lines %s' % len(full)) - self.send_header("Content-length",len(full)) - self.end_headers() - self.wfile.write(full.encode('UTF-8')) +# body.append('%s' % affected) + body.append('') + body.extend(['', '
']) + else: + body.append('

Nothing found

') c.close() + write(subtitle, body) - def _getbandb (self): + def _getbandb(self): if os.path.exists(filename): db = sqlite3.connect(filename,timeout=10) - db.text_factory = str return db db = sqlite3.connect(filename) - db.text_factory = str c = db.cursor() c.execute("""CREATE TABLE bans ( id INTEGER PRIMARY KEY, @@ -387,24 +367,23 @@ class MyHandler( http.server.BaseHTTPRequestHandler ): removed_by VARCHAR(1000) )""") c.execute("""CREATE TABLE nicks ( - ban_id INTEGER, + ban_id INTEGER, ban VARCHAR(1000) NOT NULL, full VARCHAR(1000) NOT NULL, - log TEXT NOT NULL + log TEXT NOT NULL )""") c.execute("""CREATE TABLE comments ( ban_id INTEGER, - oper VARCHAR(1000) NOT NULL, + oper VARCHAR(1000) NOT NULL, at TIMESTAMP NOT NULL, comment TEXT NOT NULL )""") db.commit() return db -def httpd(handler_class=MyHandler, server_address = ('', port), ): +def httpd(handler_class=MyHandler, server_address=('', port)): srvr = http.server.HTTPServer(server_address, handler_class) srvr.serve_forever() if __name__ == "__main__": - httpd( ) - + httpd()