aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cgi/anarkia.py34
-rw-r--r--cgi/anarkia.py.orig489
-rw-r--r--cgi/anarkia.py.rej42
-rw-r--r--cgi/api.py58
-rw-r--r--cgi/database.py46
-rw-r--r--cgi/formatting.py76
-rw-r--r--cgi/framework.py96
-rw-r--r--cgi/img.py40
-rw-r--r--cgi/manage.py285
-rw-r--r--cgi/modapi.py32
-rw-r--r--cgi/oekaki.py19
-rw-r--r--cgi/post.py226
-rw-r--r--cgi/template.py18
-rw-r--r--cgi/templates/home.html5
-rw-r--r--cgi/templates/manage/boardoptions.html34
-rw-r--r--cgi/templates/manage/ipshow.html10
-rw-r--r--cgi/templates/revision.html2
-rw-r--r--cgi/templates/txt_board.html6
-rw-r--r--cgi/templates/txt_thread.html12
-rwxr-xr-xcgi/weabot.py235
20 files changed, 1126 insertions, 639 deletions
diff --git a/cgi/anarkia.py b/cgi/anarkia.py
index de9152f..ad64596 100644
--- a/cgi/anarkia.py
+++ b/cgi/anarkia.py
@@ -18,7 +18,7 @@ def anarkia(self, path_split):
self.output = main()
return
- raise UserError, 'Ya fue, baisano...'
+ raise UserError('Ya fue, baisano...')
if path_split[2] == 'opt':
self.output = boardoptions(self.formdata)
@@ -33,7 +33,7 @@ def anarkia(self, path_split):
elif path_split[2] == 'emojis':
self.output = emojis(self.formdata)
else:
- raise UserError, 'ke?'
+ raise UserError('ke?')
def main():
@@ -90,7 +90,7 @@ def type(formdata):
logAction(msg)
return renderTemplate('anarkia.html', {'mode': 99, 'msg': msg})
else:
- raise UserError, 'Esta acción sólo se puede realizar cada 10 minutos. Faltan: %d mins.' % (10-int(dif/60))
+ raise UserError('Esta acción sólo se puede realizar cada 10 minutos. Faltan: %d mins.' % (10-int(dif/60)))
return renderTemplate('anarkia.html', {'mode': 7, 'type_now': type_now, 'type_do': type_do})
@@ -203,9 +203,9 @@ def bans(formdata):
ban = FetchOne("SELECT * FROM `bans` WHERE id = %d" % unban)
if not ban:
- raise UserError, "Ban inválido."
+ raise UserError("Ban inválido.")
if ban['boards'] != boardpickle:
- raise USerError, "Ban inválido."
+ raise USerError("Ban inválido.")
UpdateDb('DELETE FROM `bans` WHERE id = %s' % ban['id'])
logAction("Usuario %s desbaneado." % ban['ip'][:4])
@@ -255,7 +255,7 @@ def mod(formdata):
logAction(msg)
return renderTemplate('anarkia.html', {'mode': 99, 'msg': msg})
else:
- raise UserError, "jaj no"
+ raise UserError("jaj no")
elif formdata.get('restore'):
postid = int(formdata['restore'])
post = FetchOne('SELECT id, parentid FROM posts WHERE boardid = %s AND id = %d LIMIT 1' % (
@@ -293,7 +293,7 @@ def mod(formdata):
ban = FetchOne("SELECT `id` FROM `bans` WHERE `ip` = '" +
post['ip'] + "' AND `boards` = '" + _mysql.escape_string(where) + "' LIMIT 1")
if ban:
- raise UserError, "Este usuario ya esta baneado."
+ raise UserError("Este usuario ya esta baneado.")
# Blind mode
if formdata.get('blind') == '1':
@@ -339,23 +339,23 @@ def boardoptions(formdata):
board['subject'] = formdata['subject'].replace('script', '')
board['message'] = formdata['message'].replace('script', '')
board['useid'] = formdata['useid']
- if 'disable_name' in formdata.keys():
+ if 'disable_name' in formdata:
board['disable_name'] = '1'
else:
board['disable_name'] = '0'
- if 'disable_subject' in formdata.keys():
+ if 'disable_subject' in formdata:
board['disable_subject'] = '1'
else:
board['disable_subject'] = '0'
- if 'allow_noimage' in formdata.keys():
+ if 'allow_noimage' in formdata:
board['allow_noimage'] = '1'
else:
board['allow_noimage'] = '0'
- if 'allow_images' in formdata.keys():
+ if 'allow_images' in formdata:
board['allow_images'] = '1'
else:
board['allow_images'] = '0'
- if 'allow_image_replies' in formdata.keys():
+ if 'allow_image_replies' in formdata:
board['allow_image_replies'] = '1'
else:
board['allow_image_replies'] = '0'
@@ -364,7 +364,7 @@ def boardoptions(formdata):
UpdateDb("DELETE FROM `boards_filetypes` WHERE `boardid` = %s" %
board['id'])
for filetype in filetypelist():
- if 'filetype'+filetype['ext'] in formdata.keys():
+ if 'filetype'+filetype['ext'] in formdata:
UpdateDb("INSERT INTO `boards_filetypes` VALUES (%s, %s)" %
(board['id'], filetype['id']))
@@ -373,28 +373,28 @@ def boardoptions(formdata):
if board['maxsize'] > 10000:
board['maxsize'] = 10000
except:
- raise UserError, _("Max size must be numeric.")
+ raise UserError(_("Max size must be numeric."))
try:
board['thumb_px'] = int(formdata['thumb_px'])
if board['thumb_px'] > 500:
board['thumb_px'] = 500
except:
- raise UserError, _("Max thumb dimensions must be numeric.")
+ raise UserError(_("Max thumb dimensions must be numeric."))
try:
board['numthreads'] = int(formdata['numthreads'])
if board['numthreads'] > 15:
board['numthreads'] = 15
except:
- raise UserError, _("Max threads shown must be numeric.")
+ raise UserError(_("Max threads shown must be numeric."))
try:
board['numcont'] = int(formdata['numcont'])
if board['numcont'] > 15:
board['numcont'] = 15
except:
- raise UserError, _("Max replies shown must be numeric.")
+ raise UserError(_("Max replies shown must be numeric."))
t = time.time()
updateBoardSettings()
diff --git a/cgi/anarkia.py.orig b/cgi/anarkia.py.orig
new file mode 100644
index 0000000..de9152f
--- /dev/null
+++ b/cgi/anarkia.py.orig
@@ -0,0 +1,489 @@
+# coding=utf-8
+import _mysql
+from database import *
+from framework import *
+from template import *
+from img import *
+from post import *
+from settings import Settings
+
+d_thread = {}
+d_post = {}
+
+
+def anarkia(self, path_split):
+ setBoard('anarkia')
+
+ if len(path_split) <= 2:
+ self.output = main()
+ return
+
+ raise UserError, 'Ya fue, baisano...'
+
+ if path_split[2] == 'opt':
+ self.output = boardoptions(self.formdata)
+ elif path_split[2] == 'mod':
+ self.output = mod(self.formdata)
+ elif path_split[2] == 'bans':
+ self.output = bans(self.formdata)
+ elif path_split[2] == 'css':
+ self.output = css(self.formdata)
+ elif path_split[2] == 'type':
+ self.output = type(self.formdata)
+ elif path_split[2] == 'emojis':
+ self.output = emojis(self.formdata)
+ else:
+ raise UserError, 'ke?'
+
+
+def main():
+ board = Settings._.BOARD
+
+ logs = FetchAll(
+ "SELECT * FROM `logs` WHERE `staff` = 'Anarko' ORDER BY `timestamp` DESC")
+ for log in logs:
+ log['timestamp_formatted'] = formatTimestamp(log['timestamp'])
+
+ return renderTemplate('anarkia.html', {'mode': 0, 'logs': logs})
+
+
+def type(formdata):
+ board = Settings._.BOARD
+
+ if board['board_type'] == '1':
+ (type_now, type_do, do_num) = ('BBS', 'IB', '0')
+ else:
+ (type_now, type_do, do_num) = ('IB', 'BBS', '1')
+
+ if formdata.get('transform') == 'do':
+ t = 0
+ try:
+ with open('anarkia_time') as f:
+ t = int(f.read())
+ except IOError:
+ pass
+
+ dif = time.time() - t
+ if dif > (10 * 60):
+ # if True:
+ import re
+ t = time.time()
+
+ board['board_type'] = do_num
+ board['force_css'] = Settings.HOME_URL + \
+ 'anarkia/style_' + type_do.lower() + '.css'
+ updateBoardSettings()
+
+ # update posts
+ fix_board()
+
+ # regenerate
+ setBoard('anarkia')
+ regenerateBoard(True)
+
+ tf = timeTaken(t, time.time())
+
+ with open('anarkia_time', 'w') as f:
+ t = f.write(str(int(time.time())))
+
+ msg = 'Cambiada estructura de sección a %s. (%s)' % (type_do, tf)
+ logAction(msg)
+ return renderTemplate('anarkia.html', {'mode': 99, 'msg': msg})
+ else:
+ raise UserError, 'Esta acción sólo se puede realizar cada 10 minutos. Faltan: %d mins.' % (10-int(dif/60))
+
+ return renderTemplate('anarkia.html', {'mode': 7, 'type_now': type_now, 'type_do': type_do})
+
+
+def fix_board():
+ board = Settings._.BOARD
+ get_fix_dictionary()
+
+ if board['board_type'] == '1':
+ to_fix = FetchAll(
+ "SELECT * FROM posts WHERE message LIKE '%%anarkia/res/%%' AND boardid = %s" % board['id'])
+ else:
+ to_fix = FetchAll(
+ "SELECT * FROM posts WHERE message LIKE '%%anarkia/read/%%' AND boardid = %s" % board['id'])
+
+ for p in to_fix:
+ try:
+ if board['board_type'] == '1':
+ newmessage = re.sub(
+ r'/anarkia/res/(\d+).html#(\d+)">&gt;&gt;(\d+)', fix_to_bbs, p['message'])
+ else:
+ newmessage = re.sub(
+ r'/anarkia/read/(\d+)/(\d+)">&gt;&gt;(\d+)', fix_to_ib, p['message'])
+
+ UpdateDb("UPDATE posts SET message = '%s' WHERE boardid = %s AND id = %s" %
+ (_mysql.escape_string(newmessage), board['id'], p['id']))
+ except KeyError:
+ pass
+
+ return True
+
+
+def fix_to_bbs(matchobj):
+ threadid = matchobj.group(1)
+ pid = matchobj.group(2)
+ new_thread = d_thread[threadid]
+ new_post = d_post[new_thread][pid]
+ return '/anarkia/read/%s/%s">&gt;&gt;%s' % (new_thread, new_post, new_post)
+
+
+def fix_to_ib(matchobj):
+ threadid = matchobj.group(1)
+ num = int(matchobj.group(2))
+ new_thread = d_thread[threadid]
+ new_post = d_post[new_thread][num]
+ return '/anarkia/res/%s.html#%s">&gt;&gt;%s' % (new_thread, new_post, new_post)
+
+
+def get_fix_dictionary():
+ global d_thread, d_post
+ board = Settings._.BOARD
+ res = FetchAll(
+ "SELECT id, timestamp, parentid FROM posts WHERE boardid = %s ORDER BY CASE parentid WHEN 0 THEN id ELSE parentid END ASC, `id` ASC" % board['id'])
+ num = 1
+ thread = 0
+ for p in res:
+ pid = p['id']
+ if p['parentid'] == '0':
+ num = 1
+
+ time = p['timestamp']
+ if board['board_type'] == '1':
+ d_thread[pid] = time
+ thread = time
+ else:
+ d_thread[time] = pid
+ thread = pid
+
+ d_post[thread] = {}
+
+ if board['board_type'] == '1':
+ d_post[thread][pid] = num
+ else:
+ d_post[thread][num] = pid
+ num += 1
+
+ return
+
+
+def css(formdata):
+ board = Settings._.BOARD
+
+ if board['board_type'] == '1':
+ basename = 'style_bbs.css'
+ else:
+ basename = 'style_ib.css'
+
+ fname = '%sanarkia/%s' % (Settings.HOME_DIR, basename)
+
+ if formdata.get('cssfile'):
+ with open(fname, 'w') as f:
+ cssfile = f.write(formdata['cssfile'])
+
+ msg = 'CSS actualizado.'
+ logAction(msg)
+ return renderTemplate('anarkia.html', {'mode': 99, 'msg': msg})
+
+ with open(fname) as f:
+ cssfile = f.read()
+
+ return renderTemplate('anarkia.html', {'mode': 6, 'basename': basename, 'cssfile': cssfile})
+
+
+def bans(formdata):
+ board = Settings._.BOARD
+
+ if formdata.get('unban'):
+ unban = int(formdata['unban'])
+ boardpickle = pickle.dumps(['anarkia'])
+
+ ban = FetchOne("SELECT * FROM `bans` WHERE id = %d" % unban)
+ if not ban:
+ raise UserError, "Ban inválido."
+ if ban['boards'] != boardpickle:
+ raise USerError, "Ban inválido."
+
+ UpdateDb('DELETE FROM `bans` WHERE id = %s' % ban['id'])
+ logAction("Usuario %s desbaneado." % ban['ip'][:4])
+ regenerateAccess()
+
+ bans = FetchAll('SELECT * FROM `bans` WHERE staff = \'anarko\'')
+ for ban in bans:
+ ban['added'] = formatTimestamp(ban['added'])
+ if ban['until'] == '0':
+ ban['until'] = _('Does not expire')
+ else:
+ ban['until'] = formatTimestamp(ban['until'])
+ return renderTemplate('anarkia.html', {'mode': 5, 'bans': bans})
+
+
+def mod(formdata):
+ board = Settings._.BOARD
+
+ if formdata.get('thread'):
+ parentid = int(formdata['thread'])
+ posts = FetchAll('SELECT * FROM `posts` WHERE (parentid = %d OR id = %d) AND boardid = %s ORDER BY `id` ASC' %
+ (parentid, parentid, board['id']))
+ return renderTemplate('anarkia.html', {'mode': 3, 'posts': posts})
+ elif formdata.get('lock'):
+ postid = int(formdata['lock'])
+ post = FetchOne('SELECT id, locked FROM posts WHERE boardid = %s AND id = %d AND parentid = 0 LIMIT 1' % (
+ board['id'], postid))
+ if post['locked'] == '0':
+ setLocked = 1
+ msg = "Hilo %s cerrado." % post['id']
+ else:
+ setLocked = 0
+ msg = "Hilo %s abierto." % post['id']
+
+ UpdateDb("UPDATE `posts` SET `locked` = %d WHERE `boardid` = '%s' AND `id` = '%s' LIMIT 1" % (
+ setLocked, board["id"], post["id"]))
+ threadUpdated(post['id'])
+ logAction(msg)
+ return renderTemplate('anarkia.html', {'mode': 99, 'msg': msg})
+ elif formdata.get('del'):
+ postid = int(formdata['del'])
+ post = FetchOne('SELECT id, parentid FROM posts WHERE boardid = %s AND id = %d LIMIT 1' % (
+ board['id'], postid))
+ if post['parentid'] != '0':
+ deletePost(post['id'], None, '3', False)
+ msg = "Mensaje %s eliminado." % post['id']
+ logAction(msg)
+ return renderTemplate('anarkia.html', {'mode': 99, 'msg': msg})
+ else:
+ raise UserError, "jaj no"
+ elif formdata.get('restore'):
+ postid = int(formdata['restore'])
+ post = FetchOne('SELECT id, parentid FROM posts WHERE boardid = %s AND id = %d LIMIT 1' % (
+ board['id'], postid))
+
+ UpdateDb('UPDATE `posts` SET `IS_DELETED` = 0 WHERE `boardid` = %s AND `id` = %s LIMIT 1' % (
+ board['id'], post['id']))
+ if post['parentid'] != '0':
+ threadUpdated(post['parentid'])
+ else:
+ regenerateFrontPages()
+ msg = "Mensaje %s recuperado." % post['id']
+ logAction(msg)
+ return renderTemplate('anarkia.html', {'mode': 99, 'msg': msg})
+ elif formdata.get('ban'):
+ postid = int(formdata['ban'])
+ post = FetchOne('SELECT id, ip FROM posts WHERE boardid = %s AND id = %d LIMIT 1' % (
+ board['id'], postid))
+
+ return renderTemplate('anarkia.html', {'mode': 4, 'post': post})
+ elif formdata.get('banto'):
+ postid = int(formdata['banto'])
+ post = FetchOne('SELECT id, message, parentid, ip FROM posts WHERE boardid = %s AND id = %d LIMIT 1' % (
+ board['id'], postid))
+
+ reason = formdata.get('reason').replace(
+ 'script', '').replace('meta', '')
+ if reason is not None:
+ if formdata['seconds'] != '0':
+ until = str(timestamp() + int(formdata['seconds']))
+ else:
+ until = '0'
+ where = pickle.dumps(['anarkia'])
+
+ ban = FetchOne("SELECT `id` FROM `bans` WHERE `ip` = '" +
+ post['ip'] + "' AND `boards` = '" + _mysql.escape_string(where) + "' LIMIT 1")
+ if ban:
+ raise UserError, "Este usuario ya esta baneado."
+
+ # Blind mode
+ if formdata.get('blind') == '1':
+ blind = '1'
+ else:
+ blind = '0'
+
+ InsertDb("INSERT INTO `bans` (`ip`, `netmask`, `boards`, `added`, `until`, `staff`, `reason`, `blind`) VALUES ('" + post['ip'] + "', INET_ATON('255.255.255.255'), '" + _mysql.escape_string(
+ where) + "', " + str(timestamp()) + ", " + until + ", 'anarko', '" + _mysql.escape_string(formdata['reason']) + "', '"+blind+"')")
+
+ newmessage = post['message'] + \
+ '<hr /><span class="banned">A este usuario se le revocó el acceso. Razón: %s</span>' % reason
+
+ UpdateDb("UPDATE posts SET message = '%s' WHERE boardid = %s AND id = %s" % (
+ _mysql.escape_string(newmessage), board['id'], post['id']))
+ if post['parentid'] != '0':
+ threadUpdated(post['parentid'])
+ else:
+ regenerateFrontPages()
+ regenerateAccess()
+
+ msg = "Usuario %s baneado." % post['ip'][:4]
+ logAction(msg)
+ return renderTemplate('anarkia.html', {'mode': 99, 'msg': msg})
+ else:
+ reports = FetchAll("SELECT * FROM `reports` WHERE board = 'anarkia'")
+ threads = FetchAll(
+ 'SELECT * FROM `posts` WHERE boardid = %s AND parentid = 0 ORDER BY `bumped` DESC' % board['id'])
+ return renderTemplate('anarkia.html', {'mode': 2, 'threads': threads, 'reports': reports})
+
+
+def boardoptions(formdata):
+ board = Settings._.BOARD
+
+ if formdata.get('longname'):
+ # submitted
+ board['longname'] = formdata['longname'].replace('script', '')
+ board['postarea_desc'] = formdata['postarea_desc'].replace(
+ 'script', '').replace('meta', '')
+ board['postarea_extra'] = formdata['postarea_extra'].replace(
+ 'script', '').replace('meta', '')
+ board['anonymous'] = formdata['anonymous'].replace('script', '')
+ board['subject'] = formdata['subject'].replace('script', '')
+ board['message'] = formdata['message'].replace('script', '')
+ board['useid'] = formdata['useid']
+ if 'disable_name' in formdata.keys():
+ board['disable_name'] = '1'
+ else:
+ board['disable_name'] = '0'
+ if 'disable_subject' in formdata.keys():
+ board['disable_subject'] = '1'
+ else:
+ board['disable_subject'] = '0'
+ if 'allow_noimage' in formdata.keys():
+ board['allow_noimage'] = '1'
+ else:
+ board['allow_noimage'] = '0'
+ if 'allow_images' in formdata.keys():
+ board['allow_images'] = '1'
+ else:
+ board['allow_images'] = '0'
+ if 'allow_image_replies' in formdata.keys():
+ board['allow_image_replies'] = '1'
+ else:
+ board['allow_image_replies'] = '0'
+
+ # Update file types
+ UpdateDb("DELETE FROM `boards_filetypes` WHERE `boardid` = %s" %
+ board['id'])
+ for filetype in filetypelist():
+ if 'filetype'+filetype['ext'] in formdata.keys():
+ UpdateDb("INSERT INTO `boards_filetypes` VALUES (%s, %s)" %
+ (board['id'], filetype['id']))
+
+ try:
+ board['maxsize'] = int(formdata['maxsize'])
+ if board['maxsize'] > 10000:
+ board['maxsize'] = 10000
+ except:
+ raise UserError, _("Max size must be numeric.")
+
+ try:
+ board['thumb_px'] = int(formdata['thumb_px'])
+ if board['thumb_px'] > 500:
+ board['thumb_px'] = 500
+ except:
+ raise UserError, _("Max thumb dimensions must be numeric.")
+
+ try:
+ board['numthreads'] = int(formdata['numthreads'])
+ if board['numthreads'] > 15:
+ board['numthreads'] = 15
+ except:
+ raise UserError, _("Max threads shown must be numeric.")
+
+ try:
+ board['numcont'] = int(formdata['numcont'])
+ if board['numcont'] > 15:
+ board['numcont'] = 15
+ except:
+ raise UserError, _("Max replies shown must be numeric.")
+
+ t = time.time()
+ updateBoardSettings()
+ setBoard('anarkia')
+ regenerateBoard(True)
+ tf = timeTaken(t, time.time())
+
+ msg = 'Opciones cambiadas. %s' % tf
+ logAction(msg)
+ return renderTemplate('anarkia.html', {'mode': 99, 'msg': msg})
+ else:
+ return renderTemplate('anarkia.html', {'mode': 1, 'boardopts': board, 'filetypes': filetypelist(), 'supported_filetypes': board['filetypes_ext']})
+
+
+def emojis(formdata):
+ board = Settings._.BOARD
+ board_pickle = _mysql.escape_string(pickle.dumps([board['dir']]))
+
+ if formdata.get('new'):
+ import re
+ ext = {'image/jpeg': 'jpg', 'image/gif': 'gif', 'image/png': 'png'}
+
+ if not formdata['name']:
+ raise UserError, 'Ingresa nombre.'
+ if not re.match(r"^[0-9a-zA-Z]+$", formdata['name']):
+ raise UserError, 'Nombre inválido; solo letras/números.'
+
+ name = ":%s:" % formdata['name'][:15]
+ data = formdata['file']
+
+ if not data:
+ raise UserError, 'Ingresa imagen.'
+
+ # check if it exists
+ already = FetchOne("SELECT 1 FROM `filters` WHERE `boards` = '%s' AND `from` = '%s'" % (
+ board_pickle, _mysql.escape_string(name)))
+ if already:
+ raise UserError, 'Este emoji ya existe.'
+
+ # get image information
+ content_type, width, height, size, extra = getImageInfo(data)
+
+ if content_type not in ext.keys():
+ raise UserError, 'Formato inválido.'
+ if width > 500 or height > 500:
+ raise UserError, 'Dimensiones muy altas.'
+ if size > 150000:
+ raise UserError, 'Tamaño muy grande.'
+
+ # create file names
+ thumb_width, thumb_height = getThumbDimensions(width, height, 32)
+
+ file_path = Settings.ROOT_DIR + \
+ board["dir"] + "/e/" + formdata['name'][:15] + \
+ '.' + ext[content_type]
+ file_url = Settings.BOARDS_URL + \
+ board["dir"] + "/e/" + formdata['name'][:15] + \
+ '.' + ext[content_type]
+ to_filter = '<img src="%s" width="%d" height="%d" />' % (
+ file_url, thumb_width, thumb_height)
+
+ # start processing image
+ args = [Settings.CONVERT_PATH, "-", "-limit", "thread", "1", "-resize",
+ "%dx%d" % (thumb_width, thumb_height), "-quality", "80", file_path]
+ p = subprocess.Popen(args, stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ out = p.communicate(input=data)[0]
+
+ # insert into DB
+ sql = "INSERT INTO `filters` (`boards`, `type`, `action`, `from`, `to`, `staff`, `added`) VALUES ('%s', 0, 1, '%s', '%s', 'Anarko', '%s')" % (
+ board_pickle, _mysql.escape_string(name), _mysql.escape_string(to_filter), timestamp())
+ UpdateDb(sql)
+
+ msg = "Emoji %s agregado." % name
+ logAction(msg)
+ return renderTemplate('anarkia.html', {'mode': 99, 'msg': msg})
+ elif formdata.get('del'):
+ return renderTemplate('anarkia.html', {'mode': 99, 'msg': 'Del.'})
+ else:
+ filters = FetchAll(
+ "SELECT * FROM `filters` WHERE `boards` = '%s' ORDER BY `added` DESC" % board_pickle)
+ return renderTemplate('anarkia.html', {'mode': 8, 'emojis': filters})
+
+
+def filetypelist():
+ filetypes = FetchAll('SELECT * FROM `filetypes` ORDER BY `ext` ASC')
+ return filetypes
+
+
+def logAction(action):
+ InsertDb("INSERT INTO `logs` (`timestamp`, `staff`, `action`) VALUES (" +
+ str(timestamp()) + ", 'Anarko', '" + _mysql.escape_string(action) + "')")
diff --git a/cgi/anarkia.py.rej b/cgi/anarkia.py.rej
new file mode 100644
index 0000000..162e98a
--- /dev/null
+++ b/cgi/anarkia.py.rej
@@ -0,0 +1,42 @@
+--- anarkia.py (original)
++++ anarkia.py (refactored)
+@@ -418,31 +418,31 @@
+ ext = {'image/jpeg': 'jpg', 'image/gif': 'gif', 'image/png': 'png'}
+
+ if not formdata['name']:
+- raise UserError, 'Ingresa nombre.'
++ raise UserError('Ingresa nombre.')
+ if not re.match(r"^[0-9a-zA-Z]+$", formdata['name']):
+- raise UserError, 'Nombre inválido; solo letras/números.'
++ raise UserError('Nombre inválido; solo letras/números.')
+
+ name = ":%s:" % formdata['name'][:15]
+ data = formdata['file']
+
+ if not data:
+- raise UserError, 'Ingresa imagen.'
++ raise UserError('Ingresa imagen.')
+
+ # check if it exists
+ already = FetchOne("SELECT 1 FROM `filters` WHERE `boards` = '%s' AND `from` = '%s'" % (
+ board_pickle, _mysql.escape_string(name)))
+ if already:
+- raise UserError, 'Este emoji ya existe.'
++ raise UserError('Este emoji ya existe.')
+
+ # get image information
+ content_type, width, height, size, extra = getImageInfo(data)
+
+- if content_type not in ext.keys():
+- raise UserError, 'Formato inválido.
++ if content_type not in ext:
++ raise UserError('Formato inválido.')
+ if width > 500 or height > 500:
+- raise UserError, 'Dimensiones muy altas.'
++ raise UserError('Dimensiones muy altas.')
+ if size > 150000:
+- raise UserError, 'Tamaño muy grande.'
++ raise UserError('Tamaño muy grande.')
+
+ # create file names
+ thumb_width, thumb_height = getThumbDimensions(width, height, 32)
diff --git a/cgi/api.py b/cgi/api.py
index 6861809..fc44d76 100644
--- a/cgi/api.py
+++ b/cgi/api.py
@@ -12,11 +12,11 @@ def api(self, path_split):
if len(path_split) > 2:
try:
self.output = api_process(self, path_split)
- except APIError, e:
+ except APIError as e:
self.output = api_error("error", e.message)
- except UserError, e:
+ except UserError as e:
self.output = api_error("failed", e.message)
- except Exception, e:
+ except Exception as e:
import sys
import traceback
exc_type, exc_value, exc_traceback = sys.exc_info()
@@ -37,7 +37,7 @@ def api_process(self, path_split):
#bans = ['181.72.116.62']
bans = []
if ip in bans:
- raise APIError, "You have been blacklisted."
+ raise APIError("You have been blacklisted.")
# with open('../api_log.txt', 'a') as f:
# logstr = "[%s] %s: %s\n" % (formatTimestamp(t), ip, repr(path_split))
@@ -66,16 +66,16 @@ def api_process(self, path_split):
try:
limit = int(data_limit)
except ValueError:
- raise APIError, "Limit must be numeric"
+ raise APIError("Limit must be numeric")
if data_since:
try:
since = int(data_since)
except ValueError:
- raise APIError, "Since must be numeric"
+ raise APIError("Since must be numeric")
if limit > 50:
- raise APIError, "Maximum limit is 50"
+ raise APIError("Maximum limit is 50")
sql = "SELECT posts.id, boards.dir, timestamp, timestamp_formatted, posts.name, tripcode, email, posts.subject, posts.message, file, file_size, image_height, image_width, thumb, thumb_width, thumb_height, parentid FROM posts INNER JOIN boards ON boardid = boards.id WHERE timestamp > %d AND IS_DELETED = 0 AND boards.secret = 0 ORDER BY timestamp DESC LIMIT %d" % (
since, limit)
@@ -101,10 +101,10 @@ def api_process(self, path_split):
try:
limit = int(data_limit)
except ValueError:
- raise APIError, "Limit must be numeric"
+ raise APIError("Limit must be numeric")
if limit > 50:
- raise APIError, "Maximum limit is 50"
+ raise APIError("Maximum limit is 50")
threads = getLastAge(limit)
if threads[0]['bumped'] > int(data_time):
@@ -121,28 +121,28 @@ def api_process(self, path_split):
numreplies = 2
if not data_board:
- raise APIError, "Missing parameters"
+ raise APIError("Missing parameters")
if data_limit:
try:
limit = int(data_limit)
except ValueError:
- raise APIError, "Limit must be numeric"
+ raise APIError("Limit must be numeric")
if data_offset:
try:
offset = int(data_offset)
except ValueError:
- raise APIError, "Offset must be numeric"
+ raise APIError("Offset must be numeric")
if data_replies:
try:
numreplies = int(data_replies)
except ValueError:
- raise APIError, "Replies must be numeric"
+ raise APIError("Replies must be numeric")
if data_replies and limit > 30:
- raise APIError, "Maximum limit is 30"
+ raise APIError("Maximum limit is 30")
board = setBoard(data_board)
@@ -207,19 +207,19 @@ def api_process(self, path_split):
limit = 1000
if not data_board or (not data_threadid and not data_threadts):
- raise APIError, "Missing parameters"
+ raise APIError("Missing parameters")
if data_limit:
try:
limit = int(data_limit)
except ValueError:
- raise APIError, "Limit must be numeric"
+ raise APIError("Limit must be numeric")
if data_offset:
try:
offset = int(data_offset)
except ValueError:
- raise APIError, "Offset must be numeric"
+ raise APIError("Offset must be numeric")
if data_striphtml:
if int(data_striphtml) == 1:
@@ -241,13 +241,13 @@ def api_process(self, path_split):
pass
if not search_val:
- raise APIError, "No thread ID"
+ raise APIError("No thread ID")
op_post = FetchOne("SELECT id, timestamp, subject, locked FROM posts WHERE `%s` = '%d' AND boardid = '%s' AND parentid = 0" % (
search_field, search_val, board["id"]))
if not op_post:
- raise APIError, "Not a thread"
+ raise APIError("Not a thread")
values['id'] = int(op_post['id'])
values['timestamp'] = int(op_post['timestamp'])
@@ -297,7 +297,7 @@ def api_process(self, path_split):
data_postnum = formdata.get('num')
if not data_board and (not data_postid or (not data_postnum and not data_parentid)):
- raise APIError, "Missing parameters"
+ raise APIError("Missing parameters")
board = setBoard(data_board)
postid = 0
@@ -308,13 +308,13 @@ def api_process(self, path_split):
try:
postid = int(data_postid)
except ValueError:
- raise APIError, "Post ID must be numeric"
+ raise APIError("Post ID must be numeric")
post = FetchOne("SELECT id, parentid, timestamp, timestamp_formatted, name, tripcode, email, subject, message, file, file_size, image_width, image_height, thumb, thumb_width, thumb_height, IS_DELETED FROM posts WHERE `id`='%d' AND boardid='%s'" % (
postid, board["id"]))
if not post:
- raise APIError, "Post ID cannot be found"
+ raise APIError("Post ID cannot be found")
values['posts'] = []
@@ -345,7 +345,7 @@ def api_process(self, path_split):
data_password = formdata.get('password')
if not data_board or not data_postid or not data_password:
- raise APIError, "Missing parameters"
+ raise APIError("Missing parameters")
imageonly = False
board = setBoard(data_board)
@@ -353,7 +353,7 @@ def api_process(self, path_split):
try:
postid = int(data_postid)
except ValueError:
- raise APIError, "Post ID must be numeric"
+ raise APIError("Post ID must be numeric")
if data_imageonly and data_imageonly == 1:
imageonly = True
@@ -363,7 +363,7 @@ def api_process(self, path_split):
boarddir = formdata.get('board')
if not boarddir:
- raise APIError, "Missing parameters"
+ raise APIError("Missing parameters")
parent = formdata.get('parent')
trap1 = formdata.get('name', '')
@@ -378,7 +378,7 @@ def api_process(self, path_split):
oek_file = formdata.get('oek_file')
password = formdata.get('password', '')
noimage = formdata.get('noimage')
- mobile = ("mobile" in formdata.keys())
+ mobile = ("mobile" in formdata)
# call post function
(post_url, ttaken, postid) = self.make_post(ip, boarddir, parent, trap1, trap2, name,
@@ -394,10 +394,10 @@ def api_process(self, path_split):
try:
limit = int(data_limit)
except ValueError:
- raise APIError, "Limit must be numeric"
+ raise APIError("Limit must be numeric")
if limit > 30:
- raise APIError, "Maximum limit is 30"
+ raise APIError("Maximum limit is 30")
threads = getNewThreads(limit)
values['threads'] = threads
@@ -419,7 +419,7 @@ def api_process(self, path_split):
board['numthreads'] = int(board['numthreads'])
board['maxsize'] = int(board['maxsize'])
else:
- raise APIError, "Invalid method"
+ raise APIError("Invalid method")
values['time'] = int(t)
#values['time_taken'] = time.time() - t
diff --git a/cgi/database.py b/cgi/database.py
index 9b2c1e7..819c253 100644
--- a/cgi/database.py
+++ b/cgi/database.py
@@ -1,7 +1,8 @@
# coding=utf-8
import threading
-import _mysql
+import MySQLdb
+import MySQLdb.cursors
from settings import Settings
database_lock = threading.Lock()
@@ -19,57 +20,58 @@ except ImportError:
def OpenDb():
if Settings._.CONN is None:
- Settings._.CONN = _mysql.connect(host=Settings.DATABASE_HOST,
- user=Settings.DATABASE_USERNAME,
- passwd=Settings.DATABASE_PASSWORD,
- db=Settings.DATABASE_DB)
+ Settings._.CONN = MySQLdb.connect(host=Settings.DATABASE_HOST,
+ user=Settings.DATABASE_USERNAME,
+ passwd=Settings.DATABASE_PASSWORD,
+ db=Settings.DATABASE_DB,
+ cursorclass=MySQLdb.cursors.SSDictCursor)
-def FetchAll(query, method=1):
+def FetchAll(query, params=None):
"""
Query and fetch all results as a list
"""
db = Settings._.CONN
- db.query(query)
- r = db.use_result()
- return r.fetch_row(0, method)
+ c = db.cursor()
+ c.execute(query, params)
+ return c.fetchall()
-def FetchOne(query, method=1):
+def FetchOne(query, params=None):
"""
Query and fetch only the first result
"""
db = Settings._.CONN
- db.query(query)
- r = db.use_result()
- try:
- return r.fetch_row(1, method)[0]
- except:
- return None
+ c = db.cursor()
+ c.execute(query, params)
+ return c.fetchone()
-def UpdateDb(query):
+def UpdateDb(query, params=None):
"""
Update the DB (UPDATE/DELETE) and return # of affected rows
"""
db = Settings._.CONN
- db.query(query)
- return db.affected_rows()
+ c = db.cursor()
+ c.execute(query, params)
+ return c.rowcount
-def InsertDb(query):
+def InsertDb(query, params=None):
"""
Insert into the DB and return the primary key of new row
"""
db = Settings._.CONN
- db.query(query)
- return db.insert_id()
+ c = db.cursor()
+ c.execute(query, params)
+ return c.lastrowid
def CloseDb():
if Settings._.CONN is not None:
Settings._.CONN.close()
+ Settings._.CONN = None
diff --git a/cgi/formatting.py b/cgi/formatting.py
index 8037257..96bb73b 100644
--- a/cgi/formatting.py
+++ b/cgi/formatting.py
@@ -1,11 +1,10 @@
# coding=utf-8
import string
-import cgi
+import html
import os
import re
import pickle
import time
-import _mysql
from database import *
from framework import *
@@ -24,7 +23,7 @@ def format_post(message, ip, parentid, parent_timestamp=0):
# Escape any HTML if user is not using Markdown or HTML
if not Settings.USE_HTML:
- message = cgi.escape(message)
+ message = html.escape(message)
# Strip text
message = message.rstrip()[0:8000]
@@ -63,9 +62,7 @@ def tripcode(name):
return '', ''
board = Settings._.BOARD
-
- name = name.decode('utf-8')
- key = Settings.TRIP_CHAR.decode('utf-8')
+ key = Settings.TRIP_CHAR
# if there's a trip
(namepart, marker, trippart) = name.partition('#')
@@ -89,23 +86,24 @@ def tripcode(name):
# return it if we don't have a normal tripcode
if trippart == '':
- return namepart.encode('utf-8'), trip.encode('utf-8')
+ return namepart, trip
# do normal tripcode
from crypt import crypt
- try:
- trippart = trippart.encode("sjis", "ignore")
- except:
- pass
+ #try:
+ # trippart = trippart.encode("sjis", "ignore")
+ #except:
+ # pass
- trippart = cleanString(trippart, True, True)
+ trippart = html.unescape(trippart)
+ trippart = html.escape(trippart, True)
salt = re.sub(r"[^\.-z]", ".", (trippart + "H..")[1:3])
- salt = salt.translate(string.maketrans(r":;=?@[\]^_`", "ABDFGabcdef"))
+ salt = salt.translate(str.maketrans(r":;=?@[\]^_`", "ABDFGabcdef"))
trip = key + crypt(trippart, salt)[-10:] + trip
- return namepart.encode('utf-8'), trip.encode('utf-8')
+ return namepart, trip
- return name.encode('utf-8'), ''
+ return name, ''
def iphash(ip, post, t, useid, mobile, agent, cap_id, hide_end, has_countrycode):
@@ -113,7 +111,7 @@ def iphash(ip, post, t, useid, mobile, agent, cap_id, hide_end, has_countrycode)
if cap_id:
id = cap_id
- elif 'sage' in post['email'] and useid == '1':
+ elif post['email'] and useid:
id = '???'
elif ip == "127.0.0.1":
id = '???'
@@ -121,7 +119,8 @@ def iphash(ip, post, t, useid, mobile, agent, cap_id, hide_end, has_countrycode)
day = int((current_t + (Settings.TIME_ZONE*3600)) / 86400)
word = ',' + str(day) # IDs change every 24 hours
word += ',' + str(t) # IDs vary depending on thread
- id = hide_data(ip + word, 6, "id", Settings.SECRET)
+
+ id = getb64(getMD5(ip + word + Settings.SECRET))[:8]
if hide_end:
id += '*'
@@ -133,7 +132,7 @@ def iphash(ip, post, t, useid, mobile, agent, cap_id, hide_end, has_countrycode)
id += 'a'
elif 'iPhone' in agent:
id += 'i'
- elif useid == '3':
+ elif useid == 3:
if 'Firefox' in agent:
id += 'F'
elif 'Safari' in agent and not 'Chrome' in agent:
@@ -169,7 +168,7 @@ def iphash(ip, post, t, useid, mobile, agent, cap_id, hide_end, has_countrycode)
def cleanString(string, escape=True, quote=False):
string = string.strip()
if escape:
- string = cgi.escape(string, quote)
+ string = html.escape(string, quote)
return string
@@ -191,8 +190,7 @@ def videoThumbs(message):
matches = __RE.finditer(message)
if matches:
import json
- import urllib
- import urllib2
+ import urllib.request, urllib.parse, urllib.error
v_ids = []
videos = {}
@@ -206,7 +204,7 @@ def videoThumbs(message):
'url': match.group(1),
}
if len(v_ids) >= Settings.VIDEO_THUMBS_LIMIT:
- raise UserError, "Has incluído muchos videos en tu mensaje. El máximo es %d." % Settings.VIDEO_THUMBS_LIMIT
+ raise UserError("Has incluído muchos videos en tu mensaje. El máximo es %d." % Settings.VIDEO_THUMBS_LIMIT)
if videos:
params = {
@@ -215,8 +213,8 @@ def videoThumbs(message):
'id': ','.join(v_ids)
}
r_url = "https://www.googleapis.com/youtube/v3/videos?" + \
- urllib.urlencode(params)
- res = urllib2.urlopen(r_url)
+ urllib.parse.urlencode(params)
+ res = urllib.request.urlopen(r_url)
res_json = json.load(res)
offset = 0
@@ -227,14 +225,14 @@ def videoThumbs(message):
try:
new_url = '<a href="%(url)s" target="_blank" class="yt"><span class="pvw"><img src="%(thumb)s" /></span><b>%(title)s</b> (%(secs)s)<br />%(channel)s</a><br />' \
- % {'title': item['snippet']['title'].encode('utf-8'),
- 'channel': item['snippet']['channelTitle'].encode('utf-8'),
- 'secs': parseIsoPeriod(item['contentDetails']['duration']).encode('utf-8'),
+ % {'title': item['snippet']['title'],
+ 'channel': item['snippet']['channelTitle'],
+ 'secs': parseIsoPeriod(item['contentDetails']['duration']),
'url': videos[v_id]['url'],
- 'id': v_id.encode('utf-8'),
- 'thumb': item['snippet']['thumbnails']['default']['url'].encode('utf-8'), }
+ 'id': v_id,
+ 'thumb': item['snippet']['thumbnails']['default']['url'], }
except UnicodeDecodeError:
- raise UserError, repr(v_id)
+ raise UserError(repr(v_id))
message = message[:start+offset] + new_url + message[end+offset:]
offset += len(new_url) - (end-start)
@@ -323,7 +321,7 @@ def close_html(message):
message = message.encode('utf-8')
soup = BeautifulSoup.BeautifulSoup(message)
- return unicode(soup).replace('&#13;', '').encode('utf-8')
+ return str(soup).replace('&#13;', '').encode('utf-8')
def sanitize_html(message, decode=True):
@@ -353,7 +351,7 @@ def sanitize_html(message, decode=True):
del tag[attr]
# We export the soup into a correct XHTML string
- string = unicode(soup).encode('utf-8')
+ string = str(soup).encode('utf-8')
# We remove some anomalies we don't want
string = string.replace('<br/>', '<br />').replace('&#13;', '')
@@ -374,11 +372,11 @@ def checkWordfilters(message, ip, board):
"SELECT * FROM `filters` WHERE `type` = '0' ORDER BY `id` ASC")
for wordfilter in wordfilters:
if wordfilter["boards"] != "":
- boards = pickle.loads(wordfilter["boards"])
+ boards = pickle.loads(wordfilter["boards"].encode("utf-8"))
if wordfilter["boards"] == "" or board in boards:
if wordfilter['action'] == '0':
if not re.search(wordfilter['from'], message, re.DOTALL | re.IGNORECASE) is None:
- raise UserError, wordfilter['reason']
+ raise UserError(wordfilter['reason'])
elif wordfilter['action'] == '1':
message = re.compile(wordfilter['from'], re.DOTALL | re.IGNORECASE).sub(
wordfilter['to'], message)
@@ -395,10 +393,10 @@ def checkWordfilters(message, ip, board):
"', " + str(timestamp()) + ", " + until + ", 'System', '" + _mysql.escape_string(wordfilter['reason']) +
"', 'Word Auto-ban', '"+_mysql.escape_string(wordfilter['blind'])+"')")
regenerateAccess()
- raise UserError, wordfilter['reason']
+ raise UserError(wordfilter['reason'])
elif wordfilter['action'] == '3':
if not re.search(wordfilter['from'], message, re.DOTALL | re.IGNORECASE) is None:
- raise UserError, '<meta http-equiv="refresh" content="%s;url=%s" />%s' % (wordfilter['redirect_time'], wordfilter['redirect_url'], wordfilter['reason'])
+ raise UserError('<meta http-equiv="refresh" content="%s;url=%s" />%s' % (wordfilter['redirect_time'], wordfilter['redirect_url'], wordfilter['reason']))
return message
@@ -428,7 +426,7 @@ def checkNamefilters(name, tripcode, ip, board):
if match:
# do action
if namefilter['action'] == '0':
- raise UserError, namefilter['reason']
+ raise UserError(namefilter['reason'])
elif namefilter['action'] == '1':
name = namefilter['to']
tripcode = ''
@@ -445,7 +443,7 @@ def checkNamefilters(name, tripcode, ip, board):
"', " + str(timestamp()) + ", " + until + ", 'System', '" + _mysql.escape_string(namefilter['reason']) +
"', 'Name Auto-ban', '"+_mysql.escape_string(namefilter['blind'])+"')")
regenerateAccess()
- raise UserError, namefilter['reason']
+ raise UserError(namefilter['reason'])
elif namefilter['action'] == '3':
- raise UserError, '<meta http-equiv="refresh" content="%s;url=%s" />%s' % (namefilter['redirect_time'], namefilter['redirect_url'], namefilter['reason'])
+ raise UserError('<meta http-equiv="refresh" content="%s;url=%s" />%s' % (namefilter['redirect_time'], namefilter['redirect_url'], namefilter['reason']))
return name, tripcode
diff --git a/cgi/framework.py b/cgi/framework.py
index faea8d9..5277df0 100644
--- a/cgi/framework.py
+++ b/cgi/framework.py
@@ -6,11 +6,11 @@ import time
import hashlib
import pickle
import socket
-import _mysql
-import urllib
+import urllib.request, urllib.parse, urllib.error
import re
import logging
-from Cookie import SimpleCookie
+import base64
+from http.cookies import SimpleCookie
from settings import Settings
from database import *
@@ -22,14 +22,14 @@ def setBoard(dir):
with the data from the db.
"""
if not dir:
- raise UserError, _("The specified board is invalid.")
+ raise UserError(_("The specified board is invalid."))
logging.debug("Seteando el board " + dir)
- board = FetchOne("SELECT * FROM `boards` WHERE `dir` = '%s' LIMIT 1" % _mysql.escape_string(dir))
+ board = FetchOne("SELECT * FROM `boards` WHERE `dir` = %s LIMIT 1", (dir,))
if not board:
- raise UserError, _("The specified board is invalid.")
+ raise UserError(_("The specified board is invalid."))
board["filetypes"] = FetchAll(
- "SELECT * FROM `boards_filetypes` INNER JOIN `filetypes` ON filetypes.id = boards_filetypes.filetypeid WHERE `boardid` = %s ORDER BY `ext` ASC" % _mysql.escape_string(board['id']))
+ "SELECT * FROM `boards_filetypes` INNER JOIN `filetypes` ON filetypes.id = boards_filetypes.filetypeid WHERE `boardid` = %s ORDER BY `ext` ASC", (board['id'],))
board["filetypes_ext"] = [filetype['ext']
for filetype in board['filetypes']]
@@ -145,7 +145,7 @@ def updateBoardSettings():
del board["filetypes"]
del board["filetypes_ext"]
post_values = ["`" + _mysql.escape_string(str(key)) + "` = '" + _mysql.escape_string(
- str(value)) + "'" for key, value in board.iteritems()]
+ str(value)) + "'" for key, value in board.items()]
UpdateDb("UPDATE `boards` SET %s WHERE `id` = '%s' LIMIT 1" %
(", ".join(post_values), board["id"]))
@@ -242,20 +242,11 @@ def getFormData(self):
self.environ["wsgi.post_form"] = post_form
self.environ["wsgi.input"] = new_input
- try:
- formdata = {}
- for key in dict(fs):
- try:
- formdata.update({key: fs[key].value})
- if key == "file":
- formdata.update(
- {"file_original": secure_filename(fs[key].filename)})
- except AttributeError:
- formdata.update({key: fs[key]})
+ formdata = {}
+ for key in dict(fs):
+ formdata.update({key: fs[key].value})
- return formdata
- except TypeError:
- return fs
+ return formdata
class InputProcessed(object):
@@ -273,45 +264,22 @@ def secure_filename(path):
''.join([os.path.sep, os.path.altsep or ''])))
return cgi.escape(split.sub('', path))
-
-def getMD5(data):
+def getMD5b(data):
m = hashlib.md5()
m.update(data)
-
+
return m.hexdigest()
-def nullstr(len): return "\0" * len
-
+def getMD5(data):
+ m = hashlib.md5()
+ m.update(bytes(data, 'utf-8'))
-def hide_data(data, length, key, secret):
- """
- Encrypts data, useful for tripcodes and IDs
- """
- crypt = rc4(nullstr(length), rc4(
- nullstr(32), key + secret) + data).encode('base64')
- return crypt.rstrip('\n')
+ return m.hexdigest()
-def rc4(data, key):
- """
- rc4 implementation
- """
- x = 0
- box = range(256)
- for i in range(256):
- x = (x + box[i] + ord(key[i % len(key)])) % 256
- box[i], box[x] = box[x], box[i]
- x = 0
- y = 0
- out = []
- for char in data:
- x = (x + 1) % 256
- y = (y + box[x]) % 256
- box[x], box[y] = box[y], box[x]
- out.append(chr(ord(char) ^ box[(box[x] + box[y]) % 256]))
-
- return ''.join(out)
+def getb64(data):
+ return base64.b64encode(bytes(data, 'utf-8')).decode('utf-8')
def getRandomLine(filename):
@@ -337,7 +305,7 @@ def N_(message): return message
def getCookie(self, value=""):
try:
- return urllib.unquote_plus(self._cookies[value].value)
+ return urllib.parse.unquote_plus(self._cookies[value].value)
except KeyError:
return None
@@ -353,16 +321,16 @@ def setCookie(self, key, value="", max_age=None, expires=None, path="/", domain=
"""
if self._newcookies is None:
self._newcookies = SimpleCookie()
- self._newcookies[key] = urllib.quote_plus(value)
+ self._newcookies[key] = urllib.parse.quote_plus(value)
if not max_age is None:
self._newcookies[key]["max-age"] = max_age
if not expires is None:
- if isinstance(expires, basestring):
+ if isinstance(expires, str):
self._newcookies[key]["expires"] = expires
expires = None
elif isinstance(expires, datetime):
expires = expires.utctimetuple()
- elif not isinstance(expires, (int, long)):
+ elif not isinstance(expires, int):
expires = datetime.datetime.gmtime(expires)
else:
raise ValueError("Se requiere de un entero o un datetime")
@@ -448,20 +416,20 @@ def inet_ntoa(packed_ip):
def is_bad_proxy(pip):
- import urllib2
+ import urllib.request, urllib.error, urllib.parse
import socket
socket.setdefaulttimeout(3)
try:
- proxy_handler = urllib2.ProxyHandler({'http': pip})
- opener = urllib2.build_opener(proxy_handler)
+ proxy_handler = urllib.request.ProxyHandler({'http': pip})
+ opener = urllib.request.build_opener(proxy_handler)
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
- urllib2.install_opener(opener)
- req = urllib2.Request('http://bienvenidoainternet.org')
- sock = urllib2.urlopen(req)
- except urllib2.HTTPError, e:
+ urllib.request.install_opener(opener)
+ req = urllib.request.Request('http://bienvenidoainternet.org')
+ sock = urllib.request.urlopen(req)
+ except urllib.error.HTTPError as e:
return e.code
- except Exception, detail:
+ except Exception as detail:
return True
return False
diff --git a/cgi/img.py b/cgi/img.py
index 1c57e37..7759b23 100644
--- a/cgi/img.py
+++ b/cgi/img.py
@@ -5,7 +5,7 @@ import math
import os
import subprocess
import logging
-from StringIO import StringIO
+from io import BytesIO
from settings import Settings
from database import *
@@ -35,7 +35,7 @@ def processImage(post, data, t, originalname, spoiler=False):
# check the size is fine
if size > int(board["maxsize"])*1024:
- raise UserError, _("File too big. The maximum file size is: %s") % board['maxsize']
+ raise UserError(_("File too big. The maximum file size is: %s") % board['maxsize'])
# check if file is supported
for filetype in board['filetypes']:
@@ -44,12 +44,12 @@ def processImage(post, data, t, originalname, spoiler=False):
break
if not used_filetype:
- raise UserError, _("File type not supported.")
+ raise UserError(_("File type not supported."))
# check if file is already posted
is_duplicate = checkFileDuplicate(data)
if checkFileDuplicate(data)[0]:
- raise UserError, _("This image has already been posted %s.") % ('<a href="' + Settings.BOARDS_URL + board['dir'] + '/res/' + str(is_duplicate[1]) + '.html#' + str(is_duplicate[2]) + '">' + _("here") + '</a>')
+ raise UserError(_("This image has already been posted %s.") % ('<a href="' + Settings.BOARDS_URL + board['dir'] + '/res/' + str(is_duplicate[1]) + '.html#' + str(is_duplicate[2]) + '">' + _("here") + '</a>'))
# prepare file names
if used_filetype['preserve_name'] == '1':
@@ -142,17 +142,17 @@ def processImage(post, data, t, originalname, spoiler=False):
# generate thumbnails
call_wrap(args)
- except subprocess.CalledProcessError, e:
+ except subprocess.CalledProcessError as e:
os.remove(file_path)
logging.error("Thumbnail creation failure: " + e.output)
- raise UserError, _("Thumbnail creation failure.") + ' ('+str(e.returncode)+')'
+ raise UserError(_("Thumbnail creation failure.") + ' ('+str(e.returncode)+')')
# check if thumbnail was truly created
try:
open(file_thumb_path)
except:
os.remove(file_path)
- raise UserError, _("Thumbnail creation failure.")
+ raise UserError(_("Thumbnail creation failure."))
# create extra thumbnails (catalog/mobile)
subprocess.call([Settings.CONVERT_PATH, file_thumb_path, "-limit", "thread",
@@ -182,7 +182,7 @@ def processImage(post, data, t, originalname, spoiler=False):
post["message"] += extraInfo(content_type, file_name, file_path)
# file md5
- post["file_hex"] = getMD5(data)
+ post["file_hex"] = getMD5b(data)
return post
@@ -233,7 +233,6 @@ def extraInfo(mime, file_name, file_path):
def getImageInfo(data):
- data = str(data)
size = len(data)
height = -1
width = -1
@@ -251,7 +250,7 @@ def getImageInfo(data):
# See PNG 2. Edition spec (http://www.w3.org/TR/PNG/)
# Bytes 0-7 are below, 4-byte chunk length, then 'IHDR'
# and finally the 4-byte width, height
- elif ((size >= 24) and data.startswith("\211PNG\r\n\032\n")
+ elif ((size >= 24) and data.startswith(b"\211PNG\r\n\032\n")
and (data[12:16] == "IHDR")):
content_type = "image/png"
w, h = struct.unpack(">LL", data[16:24])
@@ -259,7 +258,7 @@ def getImageInfo(data):
height = int(h)
# Maybe this is for an older PNG version.
- elif (size >= 16) and data.startswith("\211PNG\r\n\032\n"):
+ elif (size >= 16) and data.startswith(b"\211PNG\r\n\032\n"):
# Check to see if we have the right content type
content_type = "image/png"
w, h = struct.unpack(">LL", data[8:16])
@@ -267,9 +266,9 @@ def getImageInfo(data):
height = int(h)
# handle JPEGs
- elif (size >= 2) and data.startswith("\377\330"):
+ elif (size >= 2) and data.startswith(b"\377\330"):
content_type = "image/jpeg"
- jpeg = StringIO(data)
+ jpeg = BytesIO(data)
jpeg.read(2)
b = jpeg.read(1)
try:
@@ -288,9 +287,9 @@ def getImageInfo(data):
width = int(w)
height = int(h)
except struct.error:
- pass
+ raise
except ValueError:
- pass
+ raise
# handle WebP
if data[:4] == b'RIFF' and data[8:12] == b'WEBP':
@@ -309,7 +308,7 @@ def getImageInfo(data):
content_type = "image/webp"
# handle WebM
- elif (size >= 4) and data.startswith("\x1A\x45\xDF\xA3"):
+ elif (size >= 4) and data.startswith(b"\x1A\x45\xDF\xA3"):
content_type = "video/webm"
info = ffprobe(data)
@@ -333,7 +332,7 @@ def getImageInfo(data):
content_type = "audio/mod"
# handle XM
- elif (size >= 64) and data.startswith("Extended Module:"):
+ elif (size >= 64) and data.startswith(b"Extended Module:"):
content_type = "audio/xm"
# handle S3M
@@ -418,11 +417,10 @@ def checkFileDuplicate(data):
"""
board = Settings._.BOARD
- file_hex = getMD5(data)
- post = FetchOne("SELECT `id`, `parentid` FROM `posts` WHERE `file_hex` = '%s' AND `boardid` = %s AND IS_DELETED = 0 LIMIT 1" % (
- file_hex, board['id']))
+ file_hex = getMD5b(data)
+ post = FetchOne("SELECT `id`, `parentid` FROM `posts` WHERE `file_hex` = %s AND `boardid` = %s AND IS_DELETED = 0 LIMIT 1", (file_hex, board['id']))
if post:
- if int(post["parentid"]) != 0:
+ if post["parentid"]:
return True, post["parentid"], post["id"]
else:
return True, post["id"], post["id"]
diff --git a/cgi/manage.py b/cgi/manage.py
index 0ad2d48..40be3b2 100644
--- a/cgi/manage.py
+++ b/cgi/manage.py
@@ -1,7 +1,7 @@
# coding=utf-8
-import _mysql
import os
import cgi
+import html
import shutil
import datetime
import logging
@@ -24,18 +24,15 @@ def manage(self, path_split):
if 'username' in self.formdata and 'password' in self.formdata:
# If no admin accounts available, create admin:admin
- first_admin = FetchOne("SELECT 1 FROM `staff` WHERE `rights` = 0 LIMIT 1", 0)
+ first_admin = FetchOne("SELECT 1 FROM `staff` WHERE `rights` = 0 LIMIT 1")
if not first_admin:
- InsertDb("INSERT INTO `staff` (`username`, `password`, `added`, `rights`) VALUES ('admin', '" +
- _mysql.escape_string(genPasswdHash("admin")) + "', 0, 0)")
+ InsertDb("INSERT INTO `staff` (`username`, `password`, `added`, `rights`) VALUES ('admin', %s, 0, 0)", (genPasswdHash("admin"),))
- staff_account = verifyPasswd(
- self.formdata['username'], self.formdata['password'])
+ staff_account = verifyPasswd(self.formdata['username'], self.formdata['password'])
if staff_account:
session_uuid = newSession(staff_account['id'])
setCookie(self, 'weabot_manage', session_uuid)
- UpdateDb('DELETE FROM `logs` WHERE `timestamp` < ' +
- str(timestamp() - Settings.MANAGE_LOG_TIME)) # three months
+ UpdateDb("DELETE FROM `logs` WHERE `timestamp` < %s", (timestamp() - Settings.MANAGE_LOG_TIME,))
else:
page += _('Incorrect username/password.')
logAction('', 'Failed log-in. U:'+_mysql.escape_string(self.formdata['username'])+' IP logged.')
@@ -54,12 +51,12 @@ def manage(self, path_split):
if 'session_id' in staff_account:
renewSession(staff_account['session_id'])
- if staff_account['rights'] in ['0', '1', '2']:
+ if staff_account['rights'] in [0, 1, 2]:
administrator = True
- if staff_account['rights'] == '2':
+ if staff_account['rights'] == 2:
moderator = False
- UpdateDb('UPDATE `staff` SET `lastactive` = ' + str(timestamp()
- ) + ' WHERE `id` = ' + staff_account['id'] + ' LIMIT 1')
+ UpdateDb('UPDATE `staff` SET `lastactive` = %s WHERE `id` = %s LIMIT 1',
+ (timestamp(), staff_account['id']))
if not validated:
template_filename = "login.html"
@@ -178,11 +175,11 @@ def manage(self, path_split):
elif len(path_split) > 4:
parentid = int(path_split[4])
# make sure it's the full thread
- check = FetchOne("SELECT `parentid` FROM `posts` WHERE `id` = %s AND `boardid` = %s LIMIT 1" % (parentid, board['id']))
- if check['parentid'] != "0":
+ check = FetchOne("SELECT `parentid` FROM `posts` WHERE `id` = %s AND `boardid` = %s LIMIT 1", (parentid, board['id']))
+ if check['parentid']:
parentid = int(check['parentid'])
- posts = FetchAll('SELECT id, timestamp, timestamp_formatted, name, message, file, thumb, IS_DELETED, locked, subject, length, INET6_NTOA(ip) AS ip FROM `posts` WHERE (parentid = %d OR id = %d) AND boardid = %s ORDER BY `id` ASC' % (parentid, parentid, board['id']))
+ posts = FetchAll('SELECT id, timestamp, timestamp_formatted, name, message, file, thumb, IS_DELETED, locked, subject, length, INET6_NTOA(ip) AS ip FROM `posts` WHERE (parentid = %s OR id = %s) AND boardid = %s ORDER BY `id` ASC', (parentid, parentid, board['id']))
template_filename = "mod.html"
template_values = {"mode": 3, "dir": board["dir"], "posts": posts}
else:
@@ -190,11 +187,11 @@ def manage(self, path_split):
template_filename = "mod.html"
template_values = {"mode": 2, "dir": board["dir"], "threads": threads}
elif path_split[2] == "recent":
- posts = FetchAll("SELECT posts.id, posts.subject, dir, boards.board_type, parentid, file, thumb, timestamp_formatted, timestamp, posts.message, INET6_NTOA(ip) AS ip, posts.name, email, tripcode, boards.name AS board_name FROM posts INNER JOIN boards ON posts.boardid = boards.id WHERE posts.timestamp > UNIX_TIMESTAMP() - 86400 ORDER BY timestamp DESC")
+ posts = FetchAll("SELECT posts.id, posts.subject, dir, boards.board_type, CASE parentid WHEN '0' THEN posts.id ELSE parentid END AS parentid, file, thumb, timestamp_formatted, timestamp, posts.message, INET6_NTOA(ip) AS ip, posts.name, email, tripcode, boards.name AS board_name FROM posts INNER JOIN boards ON posts.boardid = boards.id WHERE posts.timestamp > UNIX_TIMESTAMP() - 86400 ORDER BY timestamp DESC")
template_filename = "recent.html"
template_values = {"posts": posts}
elif path_split[2] == 'staff':
- if staff_account['rights'] != '0':
+ if staff_account['rights'] != 0:
return
action_taken = False
@@ -202,23 +199,23 @@ def manage(self, path_split):
if path_split[3] == 'add' or path_split[3] == 'edit':
member = None
member_username = ''
- member_rights = '3'
+ member_rights = 3
if path_split[3] == 'edit':
if len(path_split) > 4:
- member = FetchOne('SELECT * FROM `staff` WHERE `id` = ' + _mysql.escape_string(path_split[4]) + ' LIMIT 1')
+ member = FetchOne('SELECT * FROM `staff` WHERE `id` = %s LIMIT 1', (path_split[4],))
if member:
member_username = member['username']
member_rights = member['rights']
- action = 'edit/' + member['id']
+ action = 'edit/' + str(member['id'])
try:
if self.formdata.get('user'):
- if self.formdata['rights'] in ['0', '1', '2', '3']:
+ if self.formdata['rights'] in [0, 1, 2, 3]:
action_taken = True
- UpdateDb("UPDATE `staff` SET `username` = '" + _mysql.escape_string(
- self.formdata['user']) + "', `rights` = " + self.formdata['rights'] + " WHERE `id` = " + member['id'] + " LIMIT 1")
+ UpdateDb("UPDATE `staff` SET `username` = %s, `rights` = %s WHERE `id` = LIMIT 1",
+ (self.formdata['user'], self.formdata['rights'], member['id']))
message = _(
'Staff member updated.')
logAction(staff_account['username'], _(
@@ -231,15 +228,15 @@ def manage(self, path_split):
try:
if self.formdata.get('user') and self.formdata.get('pass'):
username_taken = FetchOne(
- 'SELECT * FROM `staff` WHERE `username` = \'' + _mysql.escape_string(self.formdata['user']) + '\' LIMIT 1')
+ 'SELECT * FROM `staff` WHERE `username` = %s LIMIT 1', (self.formdata['user'],))
if not username_taken:
- if self.formdata['rights'] in ['0', '1', '2', '3']:
+ if self.formdata['rights'] in [0, 1, 2, 3]:
action_taken = True
pass_hash = genPasswdHash(
self.formdata['pass'])
- InsertDb("INSERT INTO `staff` (`username`, `password`, `added`, `rights`) VALUES ('" + _mysql.escape_string(
- self.formdata['user']) + "', '" + _mysql.escape_string(pass_hash) + "', " + str(timestamp()) + ", " + self.formdata['rights'] + ")")
+ InsertDb("INSERT INTO `staff` (`username`, `password`, `added`, `rights`) VALUES (%s, %s, %s, %s)",
+ (self.formdata['user'], pass_hash, timestamp(), self.formdata['rights']))
message = _('Staff member added.')
logAction(
staff_account['username'], 'Added staff account for ' + self.formdata['user'])
@@ -301,21 +298,21 @@ def manage(self, path_split):
if not action_taken:
staff = FetchAll('SELECT * FROM `staff` ORDER BY `rights`')
for member in staff:
- if member['rights'] == '0':
+ if member['rights'] == 0:
member['rights'] = _('Super-administrator')
- elif member['rights'] == '1':
+ elif member['rights'] == 1:
member['rights'] = _('Administrator')
- elif member['rights'] == '2':
+ elif member['rights'] == 2:
member['rights'] = _('Developer')
- elif member['rights'] == '3':
+ elif member['rights'] == 3:
member['rights'] = _('Moderator')
- if member['lastactive'] != '0':
+ if member['lastactive']:
member['lastactivestamp'] = member['lastactive']
member['lastactive'] = formatTimestamp(
member['lastactive'])
else:
member['lastactive'] = _('Never')
- member['lastactivestamp'] = '0'
+ member['lastactivestamp'] = 0
template_filename = "staff.html"
template_values = {'mode': 0, 'staff': staff}
elif path_split[2] == 'delete':
@@ -562,7 +559,7 @@ def manage(self, path_split):
num += 1
# fix anchors
- for old, new in refs.iteritems():
+ for old, new in refs.items():
old_url = "/{oldboard}/res/{oldthread}.html#{oldpost}\">&gt;&gt;{oldpost}</a>".format(
oldboard=oldboard, oldthread=oldthread, oldpost=old)
@@ -685,7 +682,7 @@ def manage(self, path_split):
else:
self.error("IP o rango inválido.")
return
- except netaddr.core.AddrFormatError, e:
+ except netaddr.core.AddrFormatError as e:
self.error("Problema con el IP o rango ingresado: {}".format(e))
return
@@ -695,12 +692,12 @@ def manage(self, path_split):
else:
until = '0'
where = ''
- if 'board_all' not in self.formdata.keys():
+ if 'board_all' not in self.formdata:
where = []
boards = FetchAll('SELECT `dir` FROM `boards`')
for board in boards:
keyname = 'board_' + board['dir']
- if keyname in self.formdata.keys():
+ if keyname in self.formdata:
if self.formdata[keyname] == "1":
where.append(board['dir'])
if len(where) > 0:
@@ -710,7 +707,7 @@ def manage(self, path_split):
_("You must select where the ban shall be placed"))
return
- if 'edit' in self.formdata.keys():
+ if 'edit' in self.formdata:
UpdateDb("DELETE FROM `bans` WHERE `id` = '" +
_mysql.escape_string(self.formdata['edit']) + "' LIMIT 1")
"""else: # TODO : Duplicate check
@@ -732,7 +729,7 @@ def manage(self, path_split):
_mysql.escape_string(where) + "', " + str(timestamp()) + ", " + until + ", '" + _mysql.escape_string(staff_account['username']) + "', '" + _mysql.escape_string(self.formdata['reason']) + "', '" + _mysql.escape_string(self.formdata['note']) + "', '"+blind+"')")
regenerateAccess()
- if 'edit' in self.formdata.keys():
+ if 'edit' in self.formdata:
message = _('Ban successfully edited.')
action = 'Edited ban for ' + ip
else:
@@ -753,7 +750,7 @@ def manage(self, path_split):
'seconds': '0',
'blind': '1'}
edit_id = 0
- if 'edit' in self.formdata.keys():
+ if 'edit' in self.formdata:
edit_id = self.formdata['edit']
ban = FetchOne("SELECT `id`, INET6_NTOA(`ip`) AS 'ip', CASE WHEN `netmask` IS NULL THEN '255.255.255.255' ELSE INET_NTOA(`netmask`) END AS 'netmask', boards, added, until, staff, reason, note, blind FROM `bans` WHERE `id` = '" +
_mysql.escape_string(edit_id) + "' ORDER BY `added` DESC")
@@ -812,7 +809,7 @@ def manage(self, path_split):
if ban['boards'] == '':
ban['boards'] = _('All boards')
else:
- where = pickle.loads(ban['boards'])
+ where = pickle.loads(ban['boards'].encode('utf-8'))
if len(where) > 1:
ban['boards'] = '/' + \
'/, /'.join(where) + '/'
@@ -878,48 +875,48 @@ def manage(self, path_split):
board['useid'] = self.formdata['useid']
board['slip'] = self.formdata['slip']
board['countrycode'] = self.formdata['countrycode']
- if 'recyclebin' in self.formdata.keys():
+ if 'recyclebin' in self.formdata:
board['recyclebin'] = '1'
else:
board['recyclebin'] = '0'
- if 'disable_name' in self.formdata.keys():
+ if 'disable_name' in self.formdata:
board['disable_name'] = '1'
else:
board['disable_name'] = '0'
- if 'disable_subject' in self.formdata.keys():
+ if 'disable_subject' in self.formdata:
board['disable_subject'] = '1'
else:
board['disable_subject'] = '0'
- if 'secret' in self.formdata.keys():
+ if 'secret' in self.formdata:
board['secret'] = '1'
else:
board['secret'] = '0'
- if 'locked' in self.formdata.keys():
+ if 'locked' in self.formdata:
board['locked'] = '1'
else:
board['locked'] = '0'
board['postarea_desc'] = self.formdata['postarea_desc']
- if 'allow_noimage' in self.formdata.keys():
+ if 'allow_noimage' in self.formdata:
board['allow_noimage'] = '1'
else:
board['allow_noimage'] = '0'
- if 'allow_images' in self.formdata.keys():
+ if 'allow_images' in self.formdata:
board['allow_images'] = '1'
else:
board['allow_images'] = '0'
- if 'allow_image_replies' in self.formdata.keys():
+ if 'allow_image_replies' in self.formdata:
board['allow_image_replies'] = '1'
else:
board['allow_image_replies'] = '0'
- if 'allow_spoilers' in self.formdata.keys():
+ if 'allow_spoilers' in self.formdata:
board['allow_spoilers'] = '1'
else:
board['allow_spoilers'] = '0'
- if 'allow_oekaki' in self.formdata.keys():
+ if 'allow_oekaki' in self.formdata:
board['allow_oekaki'] = '1'
else:
board['allow_oekaki'] = '0'
- if 'archive' in self.formdata.keys():
+ if 'archive' in self.formdata:
board['archive'] = '1'
else:
board['archive'] = '0'
@@ -930,7 +927,7 @@ def manage(self, path_split):
UpdateDb(
"DELETE FROM `boards_filetypes` WHERE `boardid` = %s" % board['id'])
for filetype in filetypelist():
- if 'filetype'+filetype['ext'] in self.formdata.keys():
+ if 'filetype'+filetype['ext'] in self.formdata:
UpdateDb("INSERT INTO `boards_filetypes` VALUES (%s, %s)" % (
board['id'], filetype['id']))
@@ -938,49 +935,49 @@ def manage(self, path_split):
board['numthreads'] = int(
self.formdata['numthreads'])
except:
- raise UserError, _("Max threads shown must be numeric.")
+ raise UserError(_("Max threads shown must be numeric."))
try:
board['numcont'] = int(self.formdata['numcont'])
except:
- raise UserError, _("Max replies shown must be numeric.")
+ raise UserError(_("Max replies shown must be numeric."))
try:
board['numline'] = int(self.formdata['numline'])
except:
- raise UserError, _("Max lines shown must be numeric.")
+ raise UserError(_("Max lines shown must be numeric."))
try:
board['thumb_px'] = int(self.formdata['thumb_px'])
except:
- raise UserError, _("Max thumb dimensions must be numeric.")
+ raise UserError(_("Max thumb dimensions must be numeric."))
try:
board['maxsize'] = int(self.formdata['maxsize'])
except:
- raise UserError, _("Max size must be numeric.")
+ raise UserError(_("Max size must be numeric."))
try:
board['maxage'] = int(self.formdata['maxage'])
except:
- raise UserError, _("Max age must be numeric.")
+ raise UserError(_("Max age must be numeric."))
try:
board['maxinactive'] = int(
self.formdata['maxinactive'])
except:
- raise UserError, _("Max inactivity must be numeric.")
+ raise UserError(_("Max inactivity must be numeric."))
try:
board['threadsecs'] = int(
self.formdata['threadsecs'])
except:
- raise UserError, _("Time between new threads must be numeric.")
+ raise UserError(_("Time between new threads must be numeric."))
try:
board['postsecs'] = int(self.formdata['postsecs'])
except:
- raise UserError, _("Time between replies must be numeric.")
+ raise UserError(_("Time between replies must be numeric."))
updateBoardSettings()
message = _('Board options successfully updated.') + ' <a href="' + \
@@ -1043,10 +1040,10 @@ def manage(self, path_split):
logAction(staff_account['username'], message + ' desde papelera. Contenido: ' + post['message'] + ' IP: ' + post['ip'])
# Delete more than 1 post
- if 'deleteall' in self.formdata.keys():
+ if 'deleteall' in self.formdata:
return # TODO
deleted = 0
- for key in self.formdata.keys():
+ for key in self.formdata:
if key[:2] == '!i':
# Board where the post is
dir = key[2:].split('/')[0]
@@ -1081,7 +1078,7 @@ def manage(self, path_split):
currentpage = 0
skip = False
- if 'type' in self.formdata.keys():
+ if 'type' in self.formdata:
type = int(self.formdata["type"])
else:
type = 0
@@ -1090,7 +1087,7 @@ def manage(self, path_split):
boards = FetchAll(
'SELECT `name`, `dir` FROM `boards` ORDER BY `dir`')
for board in boards:
- if 'board' in self.formdata.keys() and self.formdata['board'] == board['dir']:
+ if 'board' in self.formdata and self.formdata['board'] == board['dir']:
board['checked'] = True
else:
board['checked'] = False
@@ -1102,7 +1099,7 @@ def manage(self, path_split):
type_condition = "!= 0"
# Table
- if 'board' in self.formdata.keys() and self.formdata['board'] != 'all':
+ if 'board' in self.formdata and self.formdata['board'] != 'all':
cboard = self.formdata['board']
posts = FetchAll("SELECT posts.id, posts.timestamp, timestamp_formatted, IS_DELETED, INET6_NTOA(posts.ip) AS ip, posts.message, dir, boardid FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE `dir` = '%s' AND IS_DELETED %s ORDER BY `timestamp` DESC LIMIT %d, %d" % (
_mysql.escape_string(self.formdata['board']), _mysql.escape_string(type_condition), currentpage*pagesize, pagesize))
@@ -1130,7 +1127,7 @@ def manage(self, path_split):
pages = int(math.ceil(total / pagesize))
# Create delete form
- if 'board' in self.formdata.keys():
+ if 'board' in self.formdata:
board = self.formdata['board']
else:
board = None
@@ -1318,7 +1315,7 @@ def manage(self, path_split):
with open(fname) as f:
thread = json.load(f)
thread['posts'] = [
- dict(zip(thread['keys'], row)) for row in thread['posts']]
+ dict(list(zip(thread['keys'], row))) for row in thread['posts']]
template_fname = "txt_archive.html"
post_preview = cut_home_msg(
@@ -1433,9 +1430,9 @@ def manage(self, path_split):
elif path_split[2] == 'filters':
action_taken = False
if len(path_split) > 3 and path_split[3] == 'add':
- if "add" in self.formdata.keys():
+ if "add" in self.formdata:
edit_id = 0
- if 'edit' in self.formdata.keys():
+ if 'edit' in self.formdata:
edit_id = int(self.formdata['edit'])
# We decide what type of filter it is.
@@ -1447,12 +1444,12 @@ def manage(self, path_split):
# I don't like pickles... oh well.
where = ''
- if 'board_all' not in self.formdata.keys():
+ if 'board_all' not in self.formdata:
where = []
boards = FetchAll('SELECT `dir` FROM `boards`')
for board in boards:
keyname = 'board_' + board['dir']
- if keyname in self.formdata.keys():
+ if keyname in self.formdata:
if self.formdata[keyname] == "1":
where.append(board['dir'])
if len(where) > 0:
@@ -1466,8 +1463,7 @@ def manage(self, path_split):
if filter_type == 0:
# Word filter
if len(self.formdata["word"]) > 0:
- filter_from = _mysql.escape_string(
- cgi.escape(self.formdata["word"]))
+ filter_from = html.escape(self.formdata["word"])
else:
self.error(_("You must enter a word."))
return
@@ -1475,12 +1471,10 @@ def manage(self, path_split):
# Name/trip filter
can_add = False
if len(self.formdata["name"]) > 0:
- filter_from = _mysql.escape_string(
- self.formdata["name"])
+ filter_from = self.formdata["name"]
can_add = True
if len(self.formdata["trip"]) > 0:
- filter_tripcode = _mysql.escape_string(
- self.formdata["trip"])
+ filter_tripcode = self.formdata["trip"]
can_add = True
if not can_add:
self.error(
@@ -1491,21 +1485,18 @@ def manage(self, path_split):
sql_query = ''
filter_reason = ''
if len(self.formdata["reason"]) > 0:
- filter_reason = _mysql.escape_string(
- self.formdata["reason"])
+ filter_reason = self.formdata["reason"]
if filter_action == 0:
# Cancel post
- sql_query = "INSERT INTO `filters` (`id`, `boards`, `type`, `action`, `from`, `from_trip`, `reason`, `added`, `staff`) VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')" % \
- (edit_id, where, str(filter_type), str(filter_action), filter_from, filter_tripcode, filter_reason, str(
- timestamp()), _mysql.escape_string(staff_account['username']))
+ sql_query = "INSERT INTO `filters` (`id`, `boards`, `type`, `action`, `from`, `from_trip`, `reason`, `added`, `staff`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)"
+ sql_params = (edit_id, where, filter_type, filter_action, filter_from, filter_tripcode, filter_reason, timestamp(), staff_account['username'])
elif filter_action == 1:
# Change to
if len(self.formdata["changeto"]) > 0:
filter_to = _mysql.escape_string(
self.formdata["changeto"])
- sql_query = "INSERT INTO `filters` (`id`, `boards`, `type`, `action`, `from`, `from_trip`, `reason`, `to`, `added`, `staff`) VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')" % \
- (edit_id, where, str(filter_type), str(filter_action), filter_from, filter_tripcode, filter_reason, filter_to, str(
- timestamp()), _mysql.escape_string(staff_account['username']))
+ sql_query = "INSERT INTO `filters` (`id`, `boards`, `type`, `action`, `from`, `from_trip`, `reason`, `to`, `added`, `staff`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
+ sql_params = (edit_id, where, filter_type, filter_action, filter_from, filter_tripcode, filter_reason, filter_to, timestamp(), staff_account['username'])
else:
self.error(
_("You must enter a word to change to."))
@@ -1516,7 +1507,7 @@ def manage(self, path_split):
if len(self.formdata["seconds"]) > 0:
filter_seconds = _mysql.escape_string(
self.formdata["seconds"])
- if "blind" in self.formdata.keys() and self.formdata["blind"] == '1':
+ if "blind" in self.formdata and self.formdata["blind"] == '1':
filter_blind = '1'
else:
filter_blind = '2'
@@ -1564,7 +1555,7 @@ def manage(self, path_split):
else:
# Create add form
edit_id = 0
- if 'edit' in self.formdata.keys() and int(self.formdata['edit']) > 0:
+ if 'edit' in self.formdata and int(self.formdata['edit']) > 0:
# Load values
edit_id = int(self.formdata['edit'])
filt = FetchOne(
@@ -1627,21 +1618,20 @@ def manage(self, path_split):
filters = FetchAll(
"SELECT * FROM `filters` ORDER BY `added` DESC")
for filter in filters:
- if filter['boards'] == '':
+ if not filter['boards']:
filter['boards'] = _('All boards')
else:
- where = pickle.loads(filter['boards'])
+ where = pickle.loads(filter['boards'].encode('utf-8'))
if len(where) > 1:
filter['boards'] = '/' + \
'/, /'.join(where) + '/'
else:
filter['boards'] = '/' + where[0] + '/'
- if filter['type'] == '0':
- filter['type_formatted'] = _(
- 'Word:') + ' <b>' + cgi.escape(filter['from']) + '</b>'
- elif filter['type'] == '1':
+ if filter['type'] == 0:
+ filter['type_formatted'] = _('Word:') + ' <b>' + html.escape(filter['from']) + '</b>'
+ elif filter['type'] == 1:
filter['type_formatted'] = _('Name/Tripcode:')+' '
- if filter['from'] != '':
+ if filter['from']:
filter['type_formatted'] += '<b class="name">' + \
filter['from'] + '</b>'
if filter['from_trip'] != '':
@@ -1649,20 +1639,20 @@ def manage(self, path_split):
filter['from_trip'] + '</span>'
else:
filter['type_formatted'] = '?'
- if filter['action'] == '0':
+ if filter['action'] == 0:
filter['action_formatted'] = _('Abort post')
- elif filter['action'] == '1':
+ elif filter['action'] == 1:
filter['action_formatted'] = _(
- 'Change to:') + ' <b>' + cgi.escape(filter['to']) + '</b>'
- elif filter['action'] == '2':
- if filter['blind'] == '1':
+ 'Change to:') + ' <b>' + html.escape(filter['to']) + '</b>'
+ elif filter['action'] == 2:
+ if filter['blind'] == 1:
blind = _('Yes')
else:
blind = _('No')
filter['action_formatted'] = _('Autoban:') + '<br />' + \
(_('Length:')+' <i>%s</i><br />'+_('Blind:') +
' <i>%s</i>') % (filter['seconds'], blind)
- elif filter['action'] == '3':
+ elif filter['action'] == 3:
filter['action_formatted'] = (_('Redirect to:')+' %s ('+_('in %s secs')+')') % (
filter['redirect_url'], filter['redirect_time'])
else:
@@ -1672,7 +1662,7 @@ def manage(self, path_split):
template_filename = "filters.html"
template_values = {'mode': 0, 'filters': filters}
elif path_split[2] == 'logs':
- if staff_account['rights'] != '0' and staff_account['rights'] != '2':
+ if staff_account['rights'] not in [0, 2]:
return
logs = FetchAll(
@@ -1690,7 +1680,7 @@ def manage(self, path_split):
template_filename = "message.html"
elif path_split[2] == 'quotes':
# Quotes for the post screen
- if "save" in self.formdata.keys():
+ if "save" in self.formdata:
try:
f = open('quotes.conf', 'w')
f.write(self.formdata["data"])
@@ -1731,7 +1721,7 @@ def manage(self, path_split):
type = int(self.formdata['type'])
if type > 2:
- raise UserError, "Tipo no soportado"
+ raise UserError("Tipo no soportado")
# canal del home
if len(path_split) > 3:
@@ -1748,7 +1738,7 @@ def manage(self, path_split):
title = self.formdata["title"]
# Post anonimo
- if 'anonymous' in self.formdata.keys() and self.formdata['anonymous'] == '1':
+ if 'anonymous' in self.formdata and self.formdata['anonymous'] == '1':
to_name = "Staff ★"
else:
to_name = "%s ★" % staff_account['username']
@@ -1758,7 +1748,8 @@ def manage(self, path_split):
else:
timestamp_formatted = re.sub(r"\(...\)", " ", timestamp_formatted)
- UpdateDb("INSERT INTO `news` (type, staffid, staff_name, title, message, name, timestamp, timestamp_formatted) VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%d', '%s')" % (type, staff_account['id'], staff_account['username'], _mysql.escape_string(title), _mysql.escape_string(message), to_name, timestamp(t), timestamp_formatted))
+ UpdateDb("INSERT INTO `news` (type, staffid, staff_name, title, message, name, timestamp, timestamp_formatted) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
+ (type, staff_account['id'], staff_account['username'], title, message, to_name, timestamp(t), timestamp_formatted))
regenerateNews()
regenerateHome()
@@ -1804,14 +1795,14 @@ def manage(self, path_split):
pass
# If it's preferred to remain anonymous...
- if 'anonymous' in self.formdata.keys() and self.formdata['anonymous'] == '1':
+ if 'anonymous' in self.formdata and self.formdata['anonymous'] == '1':
to_name = "Staff ★"
else:
to_name = "%s ★" % staff_account['username']
timestamp_formatted = formatDate(t)
- UpdateDb("INSERT INTO `news` (type, staffid, staff_name, title, message, name, timestamp, timestamp_formatted) VALUES ('0', '%s', '%s', '%s', '%s', '%s', '%d', '%s')" % (
- staff_account['id'], staff_account['username'], _mysql.escape_string(self.formdata['title']), _mysql.escape_string(message), to_name, timestamp(t), timestamp_formatted))
+ UpdateDb("INSERT INTO `news` (type, staffid, staff_name, title, message, name, timestamp, timestamp_formatted) VALUES (0, %s, %s, %s, %s, %s, %s, %s)",
+ (staff_account['id'], staff_account['username'], self.formdata['title'], message, to_name, timestamp(t), timestamp_formatted))
message = _("Added successfully.")
template_filename = "message.html"
@@ -1832,7 +1823,8 @@ def manage(self, path_split):
if administrator:
posts = FetchAll("SELECT * FROM `news` WHERE type = '0' ORDER BY `timestamp` DESC")
else:
- posts = FetchAll("SELECT * FROM `news` WHERE staffid = '" + staff_account['id']+"' AND type = '0' ORDER BY `timestamp` DESC")
+ posts = FetchAll("SELECT * FROM `news` WHERE staffid = %s AND type = 0 ORDER BY `timestamp` DESC",
+ (staff_account['id'],))
template_filename = "news.html"
template_values = {'action': 'newschannel', 'posts': posts}
@@ -1858,9 +1850,9 @@ def manage(self, path_split):
UpdateDb("DELETE FROM `reports` WHERE `id` = '" +
_mysql.escape_string(path_split[5])+"'")
message = _('Report %s ignored.') % path_split[5]
- if 'ignore' in self.formdata.keys():
+ if 'ignore' in self.formdata:
ignored = 0
- if 'board' in self.formdata.keys() and self.formdata['board'] != 'all':
+ if 'board' in self.formdata and self.formdata['board'] != 'all':
reports = FetchAll("SELECT `id` FROM `reports` WHERE `board` = '%s' ORDER BY `timestamp` DESC LIMIT %d, %d" % (
_mysql.escape_string(self.formdata['board']), currentpage*pagesize, pagesize))
else:
@@ -1869,7 +1861,7 @@ def manage(self, path_split):
for report in reports:
keyname = 'i' + report['id']
- if keyname in self.formdata.keys():
+ if keyname in self.formdata:
# Ignore here
UpdateDb("DELETE FROM `reports` WHERE `id` = '" +
_mysql.escape_string(report['id'])+"'")
@@ -1881,20 +1873,20 @@ def manage(self, path_split):
boards = FetchAll(
'SELECT `name`, `dir` FROM `boards` ORDER BY `dir`')
for board in boards:
- if 'board' in self.formdata.keys() and self.formdata['board'] == board['dir']:
+ if 'board' in self.formdata and self.formdata['board'] == board['dir']:
board['checked'] = True
else:
board['checked'] = False
# Tabla
- if 'board' in self.formdata.keys() and self.formdata['board'] != 'all':
+ if 'board' in self.formdata and self.formdata['board'] != 'all':
reports = FetchAll("SELECT id, timestamp, timestamp_formatted, postid, parentid, link, board, INET6_NTOA(ip) AS ip, reason, INET6_NTOA(repip) AS repip FROM `reports` WHERE `board` = '%s' ORDER BY `timestamp` DESC LIMIT %d, %d" % (
_mysql.escape_string(self.formdata['board']), currentpage*pagesize, pagesize))
else:
reports = FetchAll("SELECT id, timestamp, timestamp_formatted, postid, parentid, link, board, INET6_NTOA(ip) AS ip, reason, INET6_NTOA(repip) AS repip FROM `reports` ORDER BY `timestamp` DESC LIMIT %d, %d" % (
currentpage*pagesize, pagesize))
- if 'board' in self.formdata.keys():
+ if 'board' in self.formdata:
curboard = self.formdata['board']
else:
curboard = None
@@ -1937,11 +1929,11 @@ def manage(self, path_split):
if not moderator:
return
- if 'ip' in self.formdata.keys():
+ if 'ip' in self.formdata:
# If an IP was given...
if self.formdata['ip'] != '':
ip = self.formdata['ip']
- posts = FetchAll("SELECT posts.*, boards.dir, boards.board_type, boards.subject AS default_subject FROM `posts` JOIN `boards` ON boards.id = posts.boardid WHERE ip = INET6_ATON('%s') ORDER BY posts.timestamp DESC" % _mysql.escape_string(ip))
+ posts = FetchAll("SELECT posts.*, boards.dir, boards.board_type, boards.subject AS default_subject FROM `posts` JOIN `boards` ON boards.id = posts.boardid WHERE ip = INET6_ATON(%s) ORDER BY posts.timestamp DESC", (ip,))
template_filename = "ipshow.html"
template_values = {"mode": 1, "ip": ip, "host": getHost(
ip), "country": getCountry(ip), "tor": addressIsTor(ip), "posts": posts}
@@ -1956,17 +1948,17 @@ def manage(self, path_split):
return
# Delete by IP
- if 'ip' in self.formdata.keys():
+ if 'ip' in self.formdata:
# If an IP was given...
if self.formdata['ip'] != '':
where = []
- if 'board_all' not in self.formdata.keys():
+ if 'board_all' not in self.formdata:
# If he chose boards separately, add them to a list
boards = FetchAll(
'SELECT `id`, `dir` FROM `boards`')
for board in boards:
keyname = 'board_' + board['dir']
- if keyname in self.formdata.keys():
+ if keyname in self.formdata:
if self.formdata[keyname] == "1":
where.append(board)
else:
@@ -2032,7 +2024,7 @@ def manage(self, path_split):
try:
pid = int(path_split[4])
except ValueError:
- raise UserError, "ID no válida."
+ raise UserError("ID no válida.")
if board_type == '1':
first = get_parent_post(pid, board['id'])
@@ -2094,8 +2086,8 @@ def manage(self, path_split):
template_values = {'search': search_logs}
else:
# Main page.
- reports = FetchOne("SELECT COUNT(1) FROM `reports`", 0)[0]
- posts = FetchAll("SELECT * FROM `news` WHERE type = '0' ORDER BY `timestamp` DESC")
+ reports = FetchOne("SELECT COUNT(1) AS 'count' FROM `reports`")["count"]
+ posts = FetchAll("SELECT * FROM `news` WHERE type = 0 ORDER BY `timestamp` DESC")
template_filename = "manage.html"
template_values = {'reports': reports, 'posts': posts}
@@ -2134,12 +2126,12 @@ def switchBoard(new_type):
kako_dir = os.path.join(Settings.ROOT_DIR, board['dir'], 'kako')
res_dir = os.path.join(Settings.ROOT_DIR, board['dir'], 'res')
- if new_type == '0':
+ if new_type == 0:
# Switching to Imageboard
# Delete kako if empty
if os.path.exists(kako_dir) and not os.listdir(kako_dir):
os.rmdir(kako_dir)
- elif new_type == '1':
+ elif new_type == 1:
# Switching to Textboard
# Make kako dir
if not os.path.exists(kako_dir):
@@ -2152,13 +2144,10 @@ def switchBoard(new_type):
def newSession(staff_id):
import uuid
session_uuid = uuid.uuid4().hex
+ expires = timestamp() + Settings.SESSION_TIME
- param_session_id = _mysql.escape_string(session_uuid)
- param_expires = timestamp() + Settings.SESSION_TIME
- param_staff_id = int(staff_id)
-
- InsertDb("INSERT INTO `session` (`session_id`, `expires`, `staff_id`) VALUES (UNHEX('%s'), %d, %d)" %
- (param_session_id, param_expires, param_staff_id))
+ InsertDb("INSERT INTO `session` (`session_id`, `expires`, `staff_id`) VALUES (UNHEX(%s), %s, %s)",
+ (session_uuid, expires, staff_id))
return session_uuid
@@ -2166,13 +2155,11 @@ def newSession(staff_id):
def validateSession(session_id):
cleanSessions()
- param_session_id = _mysql.escape_string(session_id)
- param_now = timestamp()
session = FetchOne(
"SELECT HEX(session_id) as session_id, id, username, rights, added FROM `session` "
"INNER JOIN `staff` ON `session`.`staff_id` = `staff`.`id` "
- "WHERE `session_id` = UNHEX('%s')" %
- (param_session_id))
+ "WHERE `session_id` = UNHEX(%s)",
+ (session_id,))
if session:
return session
@@ -2181,29 +2168,22 @@ def validateSession(session_id):
def renewSession(session_id):
- param_session_id = _mysql.escape_string(session_id)
- param_expires = timestamp() + Settings.SESSION_TIME
+ expires = timestamp() + Settings.SESSION_TIME
- UpdateDb("UPDATE `session` SET expires = %d WHERE session_id = UNHEX('%s')" %
- (param_expires, param_session_id))
+ UpdateDb("UPDATE `session` SET expires = %s WHERE session_id = UNHEX(%s)", (expires, session_id))
def deleteSession(session_id):
- param_session_id = _mysql.escape_string(session_id)
-
- UpdateDb("DELETE FROM `session` WHERE session_id = UNHEX('%s')" %
- param_session_id)
+ UpdateDb("DELETE FROM `session` WHERE session_id = UNHEX(%s)", (param_session_id,))
def cleanSessions():
- param_now = timestamp()
-
- UpdateDb("DELETE FROM `session` WHERE expires <= %d" % param_now)
+ UpdateDb("DELETE FROM `session` WHERE expires <= %s", (timestamp(),))
def logAction(staff, action):
- InsertDb("INSERT INTO `logs` (`timestamp`, `staff`, `action`) VALUES (" + str(timestamp()) +
- ", '" + _mysql.escape_string(staff) + "\', \'" + _mysql.escape_string(action) + "\')")
+ InsertDb("INSERT INTO `logs` (`timestamp`, `staff`, `action`) VALUES (%s, %s, %s)",
+ (timestamp(), staff, action))
def genPasswdHash(string):
@@ -2217,9 +2197,8 @@ def verifyPasswd(username, passwd):
import argon2
ph = argon2.PasswordHasher()
- param_username = _mysql.escape_string(username)
staff_account = FetchOne(
- "SELECT * FROM staff WHERE username = '%s'" % param_username)
+ "SELECT * FROM staff WHERE username = %s", (username,))
if not staff_account:
return None
@@ -2228,7 +2207,7 @@ def verifyPasswd(username, passwd):
except argon2.exceptions.VerifyMismatchError:
return None
except argon2.exceptions.InvalidHash:
- raise UserError, "Hash obsoleto o inválido. Por favor contacte al administrador."
+ raise UserError("Hash obsoleto o inválido. Por favor contacte al administrador.")
if ph.check_needs_rehash(staff_account['password']):
param_new_hash = ph.hash(staff_acount['password'])
diff --git a/cgi/modapi.py b/cgi/modapi.py
index 7bb63fb..499535e 100644
--- a/cgi/modapi.py
+++ b/cgi/modapi.py
@@ -12,11 +12,11 @@ def api(self, path_split):
if len(path_split) > 2:
try:
self.output = api_process(self, path_split)
- except APIError, e:
+ except APIError as e:
self.output = api_error("error", e.message)
- except UserError, e:
+ except UserError as e:
self.output = api_error("failed", e.message)
- except Exception, e:
+ except Exception as e:
import sys
import traceback
exc_type, exc_value, exc_traceback = sys.exc_info()
@@ -65,11 +65,11 @@ def api_process(self, path_split):
else:
logAction('', 'Failed log-in. Username:'+_mysql.escape_string(
self.formdata['username'])+' IP:'+self.environ["REMOTE_ADDR"])
- raise APIError, "Incorrect username/password."
+ raise APIError("Incorrect username/password.")
else:
- raise APIError, "Bad request"
+ raise APIError("Bad request")
else:
- raise APIError, "Not authenticated"
+ raise APIError("Not authenticated")
else:
if method == 'news':
news = FetchAll(
@@ -77,7 +77,7 @@ def api_process(self, path_split):
values['news'] = news
elif method == 'post':
board = setBoard(formdata.get("board"))
- if 'id' in formdata.keys():
+ if 'id' in formdata:
id = formdata.get('id')
post = FetchOne("SELECT `id`, `boardid`, `parentid`,`timestamp`, `name`, `tripcode`, `email` ,`subject`,`message`,`file`,`thumb`, INET6_NTOA(`ip`) as ip,`IS_DELETED` AS `deleted`, `bumped`, `last`, `locked` FROM `posts` WHERE `id` = '" +
_mysql.escape_string(id) + "' AND `boardid` = '" + _mysql.escape_string(board["id"]) + "'")
@@ -90,7 +90,7 @@ def api_process(self, path_split):
post['timestamp'] = int(post['timestamp'])
post['boardid'] = int(post['boardid'])
values['post'] = post
- if 'parentid' in formdata.keys():
+ if 'parentid' in formdata:
id = formdata.get('parentid')
posts = FetchAll("SELECT `id`, `boardid`, `parentid`,`timestamp`, `name`, `tripcode`, `email` ,`subject`,`message`,`file`,`thumb`, INET6_NTOA(`ip`) as ip,`IS_DELETED` AS `deleted`, `bumped`, `last`, `locked` FROM `posts` WHERE `parentid` = '" +
_mysql.escape_string(id) + "' AND `boardid` = '" + _mysql.escape_string(board["id"]) + "'")
@@ -114,28 +114,28 @@ def api_process(self, path_split):
numreplies = 2
if not data_board:
- raise APIError, "Missing parameters"
+ raise APIError("Missing parameters")
if data_limit:
try:
limit = int(data_limit)
except ValueError:
- raise APIError, "Limit must be numeric"
+ raise APIError("Limit must be numeric")
if data_offset:
try:
offset = int(data_offset)
except ValueError:
- raise APIError, "Offset must be numeric"
+ raise APIError("Offset must be numeric")
if data_replies:
try:
numreplies = int(data_replies)
except ValueError:
- raise APIError, "Replies must be numeric"
+ raise APIError("Replies must be numeric")
if data_replies and limit > 30:
- raise APIError, "Maximum limit is 30"
+ raise APIError("Maximum limit is 30")
board = setBoard(data_board)
@@ -221,10 +221,10 @@ def api_process(self, path_split):
values['stats'] = out
except ValueError:
values['stats'] = None
- raise APIError, "Stats error"
+ raise APIError("Stats error")
values['stats']['reportCount'] = report_count['COUNT(id)']
else:
- raise APIError, "Invalid method"
+ raise APIError("Invalid method")
values['time'] = int(t)
return json.dumps(values, sort_keys=True, separators=(',', ':'))
@@ -313,7 +313,7 @@ def verifyPasswd(username, passwd):
except argon2.exceptions.VerifyMismatchError:
return None
except argon2.exceptions.InvalidHash:
- raise UserError, "Hash obsoleto o inválido. Por favor contacte al administrador."
+ raise UserError("Hash obsoleto o inválido. Por favor contacte al administrador.")
if ph.check_needs_rehash(staff_account['password']):
param_new_hash = ph.hash(staff_acount['password'])
diff --git a/cgi/oekaki.py b/cgi/oekaki.py
index e063c61..b5fddf3 100644
--- a/cgi/oekaki.py
+++ b/cgi/oekaki.py
@@ -1,5 +1,4 @@
# coding=utf-8
-import _mysql
import os
import cgi
import random
@@ -42,7 +41,7 @@ def oekaki(self, path_split):
board = setBoard(self.formdata['board'])
if board['allow_oekaki'] != '1':
- raise UserError, 'Esta sección no soporta oekaki.'
+ raise UserError('Esta sección no soporta oekaki.')
# Veamos a quien le estamos respondiendo
try:
@@ -51,7 +50,7 @@ def oekaki(self, path_split):
parentid = 0
# Vemos si el usuario quiere una animacion
- if 'oek_animation' in self.formdata.keys():
+ if 'oek_animation' in self.formdata:
animation = True
animation_str = 'animation'
else:
@@ -63,8 +62,8 @@ def oekaki(self, path_split):
width = int(self.formdata['oek_x'])
height = int(self.formdata['oek_y'])
except:
- raise UserError, 'Valores de tamaño inválidos (%s)' % repr(
- self.formdata)
+ raise UserError('Valores de tamaño inválidos (%s)' % repr(
+ self.formdata))
params = {
'dir_resource': Settings.BOARDS_URL + 'oek_temp/',
@@ -88,7 +87,7 @@ def oekaki(self, path_split):
'compress_level': '4'
}
- if 'oek_edit' in self.formdata.keys():
+ if 'oek_edit' in self.formdata:
# Si hay que editar, cargar la imagen correspondiente en el canvas
pid = int(self.formdata['oek_edit'])
post = FetchOne(
@@ -106,7 +105,7 @@ def oekaki(self, path_split):
params['image_width'] = str(width)
params['image_height'] = str(height)
- if 'canvas' in self.formdata.keys():
+ if 'canvas' in self.formdata:
editfile = self.formdata['canvas']
# Darle las dimensiones al exit script
@@ -142,7 +141,7 @@ def oekaki(self, path_split):
parentid = None
if board['allow_oekaki'] != '1':
- raise UserError, 'Esta sección no soporta oekaki.'
+ raise UserError('Esta sección no soporta oekaki.')
ts = int(time.time())
ip = inet_aton(self.environ["REMOTE_ADDR"])
@@ -168,7 +167,7 @@ def oekaki(self, path_split):
board = setBoard(path_split[3])
file = int(path_split[4])
except:
- raise UserError, 'Board o archivo de animación inválido.'
+ raise UserError('Board o archivo de animación inválido.')
params = {
'pch_file': Settings.BOARDS_URL + board['dir'] + '/src/' + str(file) + '.pch',
@@ -186,7 +185,7 @@ def oekaki(self, path_split):
'<table cellpadding="0" cellspacing="0" class="full"><tr><td class="full">\n'
page += '<applet name="pch" code="pch2.PCHViewer.class" archive="' + \
Settings.BOARDS_URL + 'oek_temp/PCHViewer123.jar" width="100%" height="100%">'
- for key in params.keys():
+ for key in params:
page += '<param name="' + key + '" value="' + \
cleanString(params[key]) + '" />' + "\n"
page += '<div align="center">Java must be installed and enabled to use this applet. Please refer to our Java setup tutorial for more information.</div>'
diff --git a/cgi/post.py b/cgi/post.py
index 3b836bb..6f7ff03 100644
--- a/cgi/post.py
+++ b/cgi/post.py
@@ -4,8 +4,7 @@ import os
import shutil
import time
import threading
-import Queue
-import _mysql
+import queue
import formatting
import logging
@@ -34,10 +33,16 @@ class Post(object):
"thumb_width": 0,
"thumb_height": 0,
"ip": "",
+ "IS_DELETED": 0,
"timestamp_formatted": "",
"timestamp": 0,
"bumped": 0,
"locked": 0,
+ "last": 0,
+ "expires": 0,
+ "expires_formatted": "",
+ "expires_alert": 0,
+ "length": 0,
}
def __getitem__(self, key):
@@ -51,20 +56,25 @@ class Post(object):
def insert(self):
logging.info("Insertando Post")
+ post_keys = []
+ post_templates = []
post_values = []
- for key, value in self.post.iteritems():
+ for key, value in self.post.items():
+ post_keys.append(key)
+
if key == 'ip':
- template = "INET6_ATON('%s')"
+ template = "INET6_ATON(%s)"
else:
- template = "'%s'"
+ template = "%s"
- post_values.append(template % _mysql.escape_string(str(value)))
+ post_templates.append(template)
+ post_values.append(value)
return InsertDb("INSERT INTO `posts` (`%s`) VALUES (%s)" % (
- "`, `".join(self.post.keys()),
- ", ".join(post_values)
- ))
+ "`, `".join(post_keys),
+ ", ".join(post_templates)
+ ), post_values)
class RegenerateThread(threading.Thread):
def __init__(self, threadid, request_queue):
@@ -89,15 +99,17 @@ def threadNumReplies(post):
"""
board = Settings._.BOARD
- num = FetchOne("SELECT COUNT(1) FROM `posts` WHERE `parentid` = '%s' AND `boardid` = '%s'" % (post, board['id']), 0)
- return int(num[0])+1
+ num = FetchOne("SELECT COUNT(1) AS 'count' FROM `posts` WHERE `parentid` = %s AND `boardid` = %s" % (post, board['id']))
+ return int(num["count"]) + 1
def get_parent_post(post_id, board_id):
- post = FetchOne("SELECT `id`, `email`, `message`, `locked`, `subject`, `timestamp`, `bumped`, `last`, `length` FROM `posts` WHERE `id` = %s AND `parentid` = 0 AND `IS_DELETED` = 0 AND `boardid` = %s LIMIT 1" % (post_id, board_id))
+ post = FetchOne("SELECT `id`, `email`, `message`, `locked`, `subject`, `timestamp`, `bumped`, `last`, `length` FROM `posts` WHERE `id` = %s AND `parentid` = 0 AND `IS_DELETED` = 0 AND `boardid` = %s LIMIT 1",
+ (post_id, board_id))
+
if post:
return post
else:
- raise UserError, _("The ID of the parent post is invalid.")
+ raise UserError(_("The ID of the parent post is invalid."))
def getThread(postid=0, mobile=False, timestamp=0):
board = Settings._.BOARD
@@ -106,11 +118,10 @@ def getThread(postid=0, mobile=False, timestamp=0):
database_lock.acquire()
try:
if timestamp:
- cond = "`timestamp` = %s" % str(timestamp)
+ op_post = FetchOne("SELECT * FROM `posts` WHERE `timestamp` = %s AND `boardid` = %s AND parentid = 0 LIMIT 1", (timestamp, board["id"]))
else:
- cond = "`id` = %s" % str(postid)
+ op_post = FetchOne("SELECT * FROM `posts` WHERE `id` = %s AND `boardid` = %s AND parentid = 0 LIMIT 1", (postid, board["id"]))
- op_post = FetchOne("SELECT IS_DELETED, email, file, file_size, id, image_height, image_width, message, name, subject, thumb, thumb_height, thumb_width, timestamp_formatted, tripcode, parentid, locked, expires, expires_alert, expires_formatted, timestamp FROM `posts` WHERE %s AND `boardid` = %s AND parentid = 0 LIMIT 1" % (cond, board["id"]))
if op_post:
op_post['num'] = 1
if mobile:
@@ -119,7 +130,7 @@ def getThread(postid=0, mobile=False, timestamp=0):
#thread = {"id": op_post["id"], "posts": [op_post], "omitted": 0, "omitted_img": 0}
total_bytes += len(op_post["message"])+80
- replies = FetchAll("SELECT IS_DELETED, email, file, file_size, id, image_height, image_width, message, name, subject, thumb, thumb_height, thumb_width, timestamp_formatted, tripcode, parentid, locked, expires, expires_alert, expires_formatted, timestamp FROM `posts` WHERE `parentid` = %s AND `boardid` = %s ORDER BY `id` ASC" % (op_post["id"], board["id"]))
+ replies = FetchAll("SELECT IS_DELETED, email, file, file_size, id, image_height, image_width, message, name, subject, thumb, thumb_height, thumb_width, timestamp_formatted, tripcode, parentid, locked, expires, expires_alert, expires_formatted, timestamp FROM `posts` WHERE `parentid` = %s AND `boardid` = %s ORDER BY `id` ASC", (op_post["id"], board["id"]))
thread["length"] = 1
if replies:
for reply in replies:
@@ -132,7 +143,7 @@ def getThread(postid=0, mobile=False, timestamp=0):
total_bytes += len(reply["message"])+57
# An imageboard needs subject
- if board["board_type"] in ['1', '5']:
+ if board["board_type"] in [1, 5]:
thread["timestamp"] = op_post["timestamp"]
thread["subject"] = op_post["subject"]
thread["message"] = op_post["message"]
@@ -141,6 +152,7 @@ def getThread(postid=0, mobile=False, timestamp=0):
#threads = [thread]
else:
+ raise Exception(postid)
return None
finally:
database_lock.release()
@@ -152,7 +164,7 @@ def getID(threadid, postnum):
database_lock.acquire()
try:
- posts = FetchAll("SELECT id FROM `posts` WHERE `parentid`=%s AND `boardid`=%s ORDER BY `id` ASC" % (thread["id"], board["id"]))
+ posts = FetchAll("SELECT id FROM `posts` WHERE `parentid`= %s AND `boardid`= %s ORDER BY `id` ASC", (thread["id"], board["id"]))
if posts:
post = posts[int(postnum)-1]
postid = post["id"]
@@ -181,10 +193,6 @@ def shortenMsg(message, elid='0', elboard='0'):
message_shortened += message_exploded[i] + '<br />'
- #try:
- message_shortened = message_shortened.decode('utf-8', 'replace')
- #except:
-
if len(message_shortened) > limit:
message_shortened = message_shortened[:limit]
@@ -202,7 +210,7 @@ def threadUpdated(postid):
"""
# Use queues only if multithreading is enabled
if Settings.USE_MULTITHREADING:
- request_queue = Queue.Queue()
+ request_queue = queue.Queue()
threads = [RegenerateThread(i, request_queue) for i in range(2)]
for t in threads:
t.start()
@@ -226,7 +234,7 @@ def regenerateFrontPages():
"""
board = Settings._.BOARD
threads = []
- if board['board_type'] == '1':
+ if board['board_type'] == 1:
threads_to_fetch = int(board['numthreads'])
threads_to_limit = threads_to_fetch + 50
else:
@@ -238,18 +246,16 @@ def regenerateFrontPages():
database_lock.acquire()
try:
# fetch necessary threads and calculate how many posts we need
- allthreads_query = "SELECT id, timestamp, subject, locked, length FROM `posts` WHERE `boardid` = '%s' AND parentid = 0 AND IS_DELETED = 0 ORDER BY `bumped` DESC, `id` ASC LIMIT %d" % \
- (board["id"], threads_to_limit)
- allthreads = FetchAll(allthreads_query)
+ allthreads_query = "SELECT id, timestamp, subject, locked, length FROM `posts` WHERE `boardid` = %s AND parentid = 0 AND IS_DELETED = 0 ORDER BY `bumped` DESC, `id` ASC LIMIT %s"
+ allthreads = FetchAll(allthreads_query, (board["id"], threads_to_limit))
posts_to_fetch = 0
for t in allthreads[:threads_to_fetch]:
posts_to_fetch += int(t["length"])
more_threads = allthreads[threads_to_fetch:50]
# get the needed posts for the front page and order them
- posts_query = "SELECT * FROM `posts` WHERE `boardid` = '%s' ORDER BY `bumped` DESC, CASE parentid WHEN 0 THEN id ELSE parentid END ASC, `id` ASC LIMIT %d" % \
- (board["id"], posts_to_fetch)
- posts = FetchAll(posts_query)
+ posts_query = "SELECT * FROM `posts` WHERE `boardid` = %s ORDER BY `bumped` DESC, CASE parentid WHEN 0 THEN id ELSE parentid END ASC, `id` ASC LIMIT %s"
+ posts = FetchAll(posts_query, (board["id"], posts_to_fetch))
threads = []
if posts:
@@ -257,9 +263,9 @@ def regenerateFrontPages():
post_num = 0
for post in posts:
- if post["parentid"] == '0':
+ if not post["parentid"]:
skipThread = False
- if post["IS_DELETED"] == '0':
+ if not post["IS_DELETED"]:
# OP; Make new thread
if thread is not None:
thread["length"] = post_num
@@ -284,7 +290,7 @@ def regenerateFrontPages():
is_omitted = False
if len(threads) > 0:
# Todo : Make this better
- if board['board_type'] == '1':
+ if board['board_type'] == 1:
page_count = 1 # Front page only
threads_per_page = int(board['numthreads'])
else:
@@ -299,7 +305,7 @@ def regenerateFrontPages():
page_count = int(math.ceil(float(len(threads)) / float(int(board['numthreads']))))
threads_per_page = int(board['numthreads'])
- for i in xrange(page_count):
+ for i in range(page_count):
pages.append([])
start = i * threads_per_page
end = start + threads_per_page
@@ -351,7 +357,7 @@ def regeneratePage(page_num, page_count, threads, is_omitted=False, more_threads
else:
file_name = str(page_num)
- if board['board_type'] == '1':
+ if board['board_type'] == 1:
templatename = "txt_board.html"
else:
templatename = "board.html"
@@ -384,7 +390,7 @@ def threadList(mode=0):
maxthreads = 1000
cutFactor = 70
- if board['board_type'] == '1':
+ if board['board_type'] == 1:
filename = "txt_threadlist.html"
full_threads = FetchAll("SELECT id, timestamp, timestamp_formatted, subject, length, last FROM `posts` WHERE parentid = 0 AND boardid = %(board)s AND IS_DELETED = 0 ORDER BY `bumped` DESC LIMIT %(limit)s" \
% {'board': board["id"], 'limit': maxthreads})
@@ -397,7 +403,7 @@ def threadList(mode=0):
# Generate threadlist
timestamps = []
for thread in full_threads:
- if board['board_type'] == '1':
+ if board['board_type'] == 1:
thread["timestamp_formatted"] = thread["timestamp_formatted"].split(" ")[0]
timestamps.append([thread["last"], formatTimestamp(thread["last"])])
if mobile:
@@ -443,8 +449,8 @@ def threadList(mode=0):
def catalog(sort=''):
board = Settings._.BOARD
- if board['board_type'] != '0':
- raise UserError, "No hay catálogo disponible para esta sección."
+ if board['board_type'] != 0:
+ raise UserError("No hay catálogo disponible para esta sección.")
cutFactor = 500
@@ -484,7 +490,7 @@ def regenerateThreadPage(postid):
thread = getThread(postid)
- if board['board_type'] in ['1', '5']:
+ if board['board_type'] in [1, 5]:
template_filename = "txt_thread.html"
outname = Settings.ROOT_DIR + board["dir"] + "/res/" + str(thread["timestamp"]) + ".html"
title_matome = thread['subject']
@@ -510,14 +516,14 @@ def regenerateThreadPage(postid):
def threadPage(postid, mobile=False, timestamp=0):
board = Settings._.BOARD
- if board['board_type'] in ['1', '5']:
+ if board['board_type'] in [1, 5]:
template_filename = "txt_thread.html"
else:
template_filename = "board.html"
thread = getThread(postid, mobile, timestamp)
if not thread:
- raise UserError, "El hilo no existe."
+ raise UserError("El hilo no existe.")
return renderTemplate(template_filename, {"threads": [thread], "replythread": postid}, mobile)
@@ -525,8 +531,8 @@ def dynamicRead(parentid, ranges, mobile=False):
import re
board = Settings._.BOARD
- if board['board_type'] != '1':
- raise UserError, "Esta sección no es un BBS y como tal no soporta lectura dinámica."
+ if board['board_type'] != 1:
+ raise UserError("Esta sección no es un BBS y como tal no soporta lectura dinámica.")
# get entire thread
template_fname = "txt_thread.html"
@@ -539,10 +545,10 @@ def dynamicRead(parentid, ranges, mobile=False):
import json
with open(fname) as f:
thread = json.load(f)
- thread['posts'] = [dict(zip(thread['keys'], row)) for row in thread['posts']]
+ thread['posts'] = [dict(list(zip(thread['keys'], row))) for row in thread['posts']]
template_fname = "txt_archive.html"
else:
- raise UserError, 'El hilo no existe.'
+ raise UserError('El hilo no existe.')
filtered_thread = {
"id": thread['id'],
@@ -645,7 +651,7 @@ def dynamicRead(parentid, ranges, mobile=False):
filtered_thread["posts"].insert(0, thread["posts"][0])
if not filtered_thread["posts"]:
- raise UserError, "No hay posts que mostrar."
+ raise UserError("No hay posts que mostrar.")
post_preview = cut_home_msg(filtered_thread["posts"][0]["message"], 0)
@@ -663,7 +669,7 @@ def regenerateBoard(everything=False):
# Use queues only if multithreading is enabled
if Settings.USE_MULTITHREADING:
- request_queue = Queue.Queue()
+ request_queue = queue.Queue()
threads = [RegenerateThread(i, request_queue) for i in range(Settings.MAX_PROGRAM_THREADS)]
for t in threads:
t.start()
@@ -694,61 +700,62 @@ def deletePost(postid, password, deltype='0', imageonly=False, quick=False):
postid = int(postid)
# get post
- post = FetchOne("SELECT `id`, `timestamp`, `parentid`, `file`, `thumb`, `password`, `length` FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1" % (board["id"], str(postid)))
+ post = FetchOne("SELECT `id`, `timestamp`, `parentid`, `file`, `thumb`, `password`, `length` FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1", (board["id"], postid))
# abort if the post doesn't exist
if not post:
- raise UserError, _("There isn't a post with this ID. It was probably deleted.")
+ raise UserError(_("There isn't a post with this ID. It was probably deleted."))
if password:
if password != post['password']:
- raise UserError, "No tienes permiso para eliminar este mensaje."
- elif post["parentid"] == '0' and int(post["length"]) >= Settings.DELETE_FORBID_LENGTH:
- raise UserError, "No puedes eliminar un hilo con tantas respuestas."
- elif (int(time.time()) - int(post["timestamp"])) > 86400:
- raise UserError, "No puedes eliminar un post tan viejo."
+ raise UserError("No tienes permiso para eliminar este mensaje.")
+ elif not post["parentid"] and post["length"] >= Settings.DELETE_FORBID_LENGTH:
+ raise UserError("No puedes eliminar un hilo con tantas respuestas.")
+ elif (int(time.time()) - post["timestamp"]) > 86400:
+ raise UserError("No puedes eliminar un post tan viejo.")
# just update the DB if deleting only the image, otherwise delete whole post
if imageonly:
if post["file"]:
deleteFile(post)
- UpdateDb("UPDATE `posts` SET `file` = '', `file_hex` = '', `thumb` = '', `thumb_width` = 0, `thumb_height` = 0 WHERE `boardid` = %s AND `id` = %s LIMIT 1" % (board["id"], str(post['id'])))
+ UpdateDb("UPDATE `posts` SET `file` = '', `file_hex` = '', `thumb` = '', `thumb_width` = 0, `thumb_height` = 0 WHERE `boardid` = %s AND `id` = %s LIMIT 1", (board["id"], post['id']))
else:
- if int(post["parentid"]) == 0:
+ if not post["parentid"]:
deleteReplies(post)
logging.info("Deleting post " + str(postid))
- if deltype != '0' and post["parentid"] != '0':
+ if deltype != 0 and post["parentid"]:
# Soft delete (recycle bin)
- UpdateDb("UPDATE `posts` SET `IS_DELETED` = %s WHERE `boardid` = %s AND `id` = %s LIMIT 1" % (deltype, board["id"], post["id"]))
+ UpdateDb("UPDATE `posts` SET `IS_DELETED` = %s WHERE `boardid` = %s AND `id` = %s LIMIT 1", (deltype, board["id"], post["id"]))
else:
# Hard delete
if post["file"]:
deleteFile(post)
- if post['parentid'] != '0':
+ if post['parentid']:
numreplies = threadNumReplies(post["parentid"])
if numreplies > 2:
- newlast = FetchOne('SELECT timestamp, email FROM posts WHERE boardid = %s AND parentid = %s AND timestamp < %d ORDER BY timestamp DESC LIMIT 1' % (board['id'], post['parentid'], int(post['timestamp'])))
+ newlast = FetchOne('SELECT timestamp, email FROM posts WHERE boardid = %s AND parentid = %s AND timestamp < %s ORDER BY timestamp DESC LIMIT 1', (board['id'], post['parentid'], post['timestamp']))
else:
- newlast = FetchOne('SELECT timestamp FROM posts WHERE boardid = %s AND id = %s LIMIT 1' % (board['id'], post['parentid']))
+ newlast = FetchOne('SELECT timestamp FROM posts WHERE boardid = %s AND id = %s LIMIT 1', (board['id'], post['parentid']))
- UpdateDb("DELETE FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1" % (board["id"], post["id"]))
+ UpdateDb("DELETE FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1", (board["id"], post["id"]))
- if post['parentid'] != '0':
- UpdateDb("UPDATE `posts` SET last = %s, length = %d WHERE `id` = '%s' AND `boardid` = '%s'" % (newlast["timestamp"], threadNumReplies(post["parentid"]), post["parentid"], board["id"]))
+ if post['parentid']:
+ UpdateDb("UPDATE `posts` SET last = %s, length = %s WHERE `id` = %s AND `boardid` = %s",
+ (newlast["timestamp"], threadNumReplies(post["parentid"]), post["parentid"], board["id"]))
- if post['parentid'] == '0':
- if board['board_type'] == '1':
- os.unlink(Settings.ROOT_DIR + board["dir"] + "/res/" + post["timestamp"] + ".html")
+ if not post['parentid']:
+ if board['board_type'] == 1:
+ os.unlink(Settings.ROOT_DIR + str(board["dir"]) + "/res/" + str(post["timestamp"]) + ".html")
else:
- os.unlink(Settings.ROOT_DIR + board["dir"] + "/res/" + post["id"] + ".html")
+ os.unlink(Settings.ROOT_DIR + str(board["dir"]) + "/res/" + str(post["id"]) + ".html")
regenerateHome()
# rebuild thread and fronts if reply; rebuild only fronts if not
- if post["parentid"] != '0':
+ if post["parentid"]:
threadUpdated(post["parentid"])
else:
regenerateFrontPages()
@@ -757,12 +764,12 @@ def deleteReplies(thread):
board = Settings._.BOARD
# delete files first
- replies = FetchAll("SELECT `parentid`, `file`, `thumb` FROM `posts` WHERE `boardid` = %s AND `parentid` = %s AND `file` != ''" % (board["id"], thread["id"]))
+ replies = FetchAll("SELECT `parentid`, `file`, `thumb` FROM `posts` WHERE `boardid` = %s AND `parentid` = %s AND `file` != ''", (board["id"], thread["id"]))
for post in replies:
deleteFile(post)
# delete all replies from DB
- UpdateDb("DELETE FROM `posts` WHERE `boardid` = %s AND `parentid` = %s" % (board["id"], thread["id"]))
+ UpdateDb("DELETE FROM `posts` WHERE `boardid` = %s AND `parentid` = %s", (board["id"], thread["id"]))
def deleteFile(post):
"""
@@ -804,7 +811,7 @@ def trimThreads():
archived = False
# Use limit of the board type
- if board['board_type'] == '1':
+ if board['board_type'] == 1:
limit = Settings.TXT_MAX_THREADS
else:
limit = Settings.MAX_THREADS
@@ -815,36 +822,36 @@ def trimThreads():
alert_time = int(round(int(board['maxage']) * Settings.MAX_AGE_ALERT))
time_limit = t + (alert_time * 86400)
- old_ops = FetchAll("SELECT `id`, `timestamp`, `expires`, `expires_alert`, `length` FROM `posts` WHERE `boardid` = %s AND `parentid` = 0 AND IS_DELETED = 0 AND `expires` > 0 AND `expires` < %s LIMIT 50" % (board['id'], time_limit))
+ old_ops = FetchAll("SELECT `id`, `timestamp`, `expires`, `expires_alert`, `length` FROM `posts` WHERE `boardid` = %s AND `parentid` = 0 AND IS_DELETED = 0 AND `expires` > 0 AND `expires` < %s LIMIT 50", (board['id'], time_limit))
for op in old_ops:
if t >= int(op['expires']):
# Trim old threads
- if board['archive'] == '1' and int(op["length"]) >= Settings.ARCHIVE_MIN_LENGTH:
+ if board['archive'] and op["length"] >= Settings.ARCHIVE_MIN_LENGTH:
archiveThread(op["id"])
archived = True
deletePost(op["id"], None)
else:
# Add alert to threads approaching deletion
- UpdateDb("UPDATE `posts` SET expires_alert = 1 WHERE `boardid` = %s AND `id` = %s" % (board['id'], op['id']))
+ UpdateDb("UPDATE `posts` SET expires_alert = 1 WHERE `boardid` = %s AND `id` = %s", (board['id'], op['id']))
# trim inactive threads next
- if board['maxinactive'] != '0':
+ if board['maxinactive'] > 0:
t = time.time()
oldest_last = t - (int(board['maxinactive']) * 86400)
- old_ops = FetchAll("SELECT `id`, `length` FROM `posts` WHERE `boardid` = %s AND `parentid` = 0 AND IS_DELETED = 0 AND `last` < %d LIMIT 50" % (board['id'], oldest_last))
+ old_ops = FetchAll("SELECT `id`, `length` FROM `posts` WHERE `boardid` = %s AND `parentid` = 0 AND IS_DELETED = 0 AND `last` < %s LIMIT 50", (board['id'], oldest_last))
for op in old_ops:
- if board['archive'] == '1' and int(op["length"]) >= Settings.ARCHIVE_MIN_LENGTH:
+ if board['archive'] and op["length"] >= Settings.ARCHIVE_MIN_LENGTH:
archiveThread(op["id"])
archived = True
deletePost(op["id"], None)
# select trim type by board
- if board['board_type'] == '1':
+ if board['board_type'] == 1:
trim_method = Settings.TXT_TRIM_METHOD
else:
trim_method = Settings.TRIM_METHOD
@@ -862,7 +869,7 @@ def trimThreads():
if len(op_posts) > limit:
posts = op_posts[limit:]
for post in posts:
- if board['archive'] == '1' and int(op["length"]) >= Settings.ARCHIVE_MIN_LENGTH:
+ if board['archive'] and op["length"] >= Settings.ARCHIVE_MIN_LENGTH:
archiveThread(post["id"])
archived = True
@@ -879,7 +886,7 @@ def autoclose_thread(parentid, t, replies):
board = Settings._.BOARD
# decide the replylimit
- if board['board_type'] == '1' and Settings.TXT_CLOSE_THREAD_ON_REPLIES > 0:
+ if board['board_type'] == 1 and Settings.TXT_CLOSE_THREAD_ON_REPLIES > 0:
replylimit = Settings.TXT_CLOSE_THREAD_ON_REPLIES
elif Settings.CLOSE_THREAD_ON_REPLIES > 0:
replylimit = Settings.CLOSE_THREAD_ON_REPLIES
@@ -929,7 +936,7 @@ def pageNavigator(page_num, page_count, is_omitted=False):
pagenav += "</span><span>"
- for i in xrange(page_count):
+ for i in range(page_count):
if i == page_num:
pagenav += "[<strong>%d</strong>]" % i
else:
@@ -965,18 +972,20 @@ def flood_check(t,post,boardid):
#lastpost = FetchOne("SELECT COUNT(*) FROM `posts` WHERE `ip` = INET6_ATON('%s') and `parentid` = 0 and `boardid` = '%s' and IS_DELETED = 0 AND timestamp > %d" % (str(post["ip"]), boardid, int(maxtime)), 0)
# NO MATTER THE IP
- lastpost = FetchOne("SELECT COUNT(*) FROM `posts` WHERE `parentid` = 0 and `boardid` = '%s' and IS_DELETED = 0 AND timestamp > %d" % (boardid, int(maxtime)), 0)
+ lastpost = FetchOne("SELECT `timestamp` FROM `posts` WHERE `parentid` = 0 and `boardid` = %s and IS_DELETED = 0 AND timestamp > %s",
+ (boardid, maxtime))
else:
maxtime = round(t - int(board['postsecs']))
- lastpost = FetchOne("SELECT COUNT(*) FROM `posts` WHERE `ip` = INET6_ATON('%s') and `parentid` != 0 and `boardid` = '%s' and IS_DELETED = 0 AND timestamp > %d" % (str(post["ip"]), boardid, int(maxtime)), 0)
+ lastpost = FetchOne("SELECT `timestamp` FROM `posts` WHERE `ip` = INET6_ATON(%s) and `parentid` != 0 and `boardid` = %s and IS_DELETED = 0 AND timestamp > %s",
+ (post["ip"], boardid, maxtime))
- if int(lastpost[0]):
+ if lastpost:
if post["parentid"]:
- raise UserError, _("Flood detected. Please wait a moment before posting again.")
+ raise UserError(_("Flood detected. Please wait a moment before posting again."))
else:
- lastpost = FetchOne("SELECT `timestamp` FROM `posts` WHERE `parentid`=0 and `boardid`='%s' and IS_DELETED = 0 ORDER BY `timestamp` DESC" % (boardid), 0)
- wait = int(int(board['threadsecs']) - (t - int(lastpost[0])))
- raise UserError, "Espera " + str(wait) + " segundos antes de crear otro hilo."
+ lastpost = FetchOne("SELECT `timestamp` FROM `posts` WHERE `parentid`= 0 and `boardid`= %s and IS_DELETED = 0 ORDER BY `timestamp` DESC", (boardid,))
+ wait = int(int(board['threadsecs']) - (t - int(lastpost["timestamp"])))
+ raise UserError("Espera " + str(wait) + " segundos antes de crear otro hilo.")
def cut_home_msg(message, boardlength=0):
short_message = message.replace("<br />", " ")
@@ -985,17 +994,15 @@ def cut_home_msg(message, boardlength=0):
limit = Settings.HOME_LASTPOSTS_LENGTH - boardlength
if len(short_message) > limit:
- if isinstance(short_message, unicode):
- short_message = short_message[:limit].encode('utf-8') + "…"
- else:
- short_message = short_message.decode('utf-8')[:limit].encode('utf-8') + "…"
+ if isinstance(short_message, str):
+ short_message = short_message[:limit] + "…"
short_message = re.compile(r"&(.(?!;))*$", re.DOTALL | re.IGNORECASE).sub("", short_message) # Removes incomplete HTML
return short_message
def getLastAge(board_type, limit):
threads = []
- sql = "SELECT posts.id, boards.name AS board_fulln, boards.subname AS board_name, board_type, boards.dir, timestamp, bumped, last, length, thumb, CASE WHEN posts.subject = boards.subject THEN posts.message ELSE posts.subject END AS content FROM posts INNER JOIN boards ON boardid = boards.id WHERE parentid = 0 AND IS_DELETED = 0 AND boards.secret = 0 AND posts.locked < 3 AND boards.board_type = %d ORDER BY bumped DESC LIMIT %d" % (board_type, limit)
- threads = FetchAll(sql)
+ sql = "SELECT posts.id, boards.name AS board_fulln, boards.subname AS board_name, board_type, boards.dir, timestamp, bumped, last, length, thumb, CASE WHEN posts.subject = boards.subject THEN posts.message ELSE posts.subject END AS content FROM posts INNER JOIN boards ON boardid = boards.id WHERE parentid = 0 AND IS_DELETED = 0 AND boards.secret = 0 AND posts.locked < 3 AND boards.board_type = %s ORDER BY bumped DESC LIMIT %s"
+ threads = FetchAll(sql, (board_type, limit))
for post in threads:
post['id'] = int(post['id'])
@@ -1015,8 +1022,8 @@ def getLastAge(board_type, limit):
def getNewThreads(limit):
threads = []
- sql = "SELECT posts.id, boards.name AS board_fulln, boards.subname AS board_name, board_type, boards.dir, timestamp, thumb, CASE WHEN posts.subject = boards.subject THEN posts.message ELSE posts.subject END AS content FROM posts INNER JOIN boards ON boardid = boards.id WHERE parentid = 0 AND IS_DELETED = 0 AND boards.secret = 0 AND boards.id <> 34 AND boards.id <> 13 AND posts.locked = 0 ORDER BY timestamp DESC LIMIT %d" % (limit)
- threads = FetchAll(sql)
+ sql = "SELECT posts.id, boards.name AS board_fulln, boards.subname AS board_name, board_type, boards.dir, timestamp, thumb, CASE WHEN posts.subject = boards.subject THEN posts.message ELSE posts.subject END AS content FROM posts INNER JOIN boards ON boardid = boards.id WHERE parentid = 0 AND IS_DELETED = 0 AND boards.secret = 0 AND boards.id <> 34 AND boards.id <> 13 AND posts.locked = 0 ORDER BY timestamp DESC LIMIT %s"
+ threads = FetchAll(sql, (limit,))
for post in threads:
post['id'] = int(post['id'])
@@ -1092,13 +1099,16 @@ def regenerateAccess():
if not Settings.HTACCESS_GEN:
return False
- bans = FetchAll("SELECT `ipstr`, `boards` FROM `bans` WHERE `blind` = '1' ORDER BY `ipstart` ASC")
boards = FetchAll('SELECT `dir` FROM `boards`')
global_boards = [board['dir'] for board in boards if board['dir'] not in Settings.EXCLUDE_GLOBAL_BANS]
+ bans = []
global_bans = []
board_bans = {}
+
+ if Settings.ENABLE_BANS:
+ bans = FetchAll("SELECT `ipstr`, `boards` FROM `bans` WHERE `blind` = '1' ORDER BY `ipstart` ASC")
for ban in bans:
if ban["boards"]:
@@ -1159,14 +1169,14 @@ def make_url(postid, post, parent_post, noko, mobile):
if mobile:
if not noko:
url = Settings.CGI_URL + 'mobile/' + board["dir"]
- elif board["board_type"] == '1':
+ elif board["board_type"] == 1:
url = "%s/mobileread/%s/%s/l10#form" % (Settings.CGI_URL, board["dir"], parent_post['timestamp'])
else:
url = "%s/mobileread/%s/%s#%s" % (Settings.CGI_URL, board["dir"], parentid, postid)
else:
if not noko:
url = Settings.BOARDS_URL + board["dir"] + "/"
- elif board["board_type"] == '1':
+ elif board["board_type"] == 1:
url = "%s/read/%s/l50#bottom" % (Settings.BOARDS_URL + board["dir"], str(parent_post['timestamp']))
else:
url = "%s/res/%s.html#%s" % (Settings.BOARDS_URL + board["dir"], str(parentid), postid)
@@ -1192,7 +1202,7 @@ def latestAdd(post, postnum, postid, parent_post):
timestamp_formatted = datetime.datetime.fromtimestamp(post['timestamp']).strftime('%Y-%m-%dT%H:%M:%S%Z')
parentid = parent_post['id'] if post['parentid'] else postid
- if board['board_type'] == '1':
+ if board['board_type'] == 1:
url = '/%s/read/%s/%d' % (board['dir'], (parent_post['timestamp'] if post['parentid'] else post['timestamp']), (postnum if postnum else 1))
else:
url = '/%s/res/%s.html#%s' % (board['dir'], parentid, postid)
@@ -1221,19 +1231,19 @@ def archiveThread(postid):
with open(Settings.ROOT_DIR + board["dir"] + "/kako/" + str(thread['timestamp']) + ".json", "w") as f:
json.dump(thread, f, indent=0)
except:
- raise UserError, "Can't archive: %s" % thread['timestamp']
+ raise UserError("Can't archive: %s" % thread['timestamp'])
UpdateDb("REPLACE INTO archive (id, boardid, timestamp, subject, length) VALUES ('%s', '%s', '%s', '%s', '%s')" % (thread['id'], board['id'], thread['timestamp'], _mysql.escape_string(thread['subject']), thread['length']))
def throw_dice(dice):
qty = int(dice[0][1:])
if qty == 0:
- raise UserError, "No tienes dados para lanzar."
+ raise UserError("No tienes dados para lanzar.")
if qty > 100:
qty = 100
sides = int(dice[1][1:]) if dice[1] else 6
if sides == 0:
- raise UserError, "Tus dados no tienen caras."
+ raise UserError("Tus dados no tienen caras.")
if sides > 100:
sides = 100
@@ -1262,7 +1272,7 @@ def discord_hook(post, url):
if not Settings.DISCORD_HOOK_URL:
return
- import urllib2
+ import urllib.request, urllib.error, urllib.parse
import json
board = Settings._.BOARD
@@ -1283,7 +1293,7 @@ def discord_hook(post, url):
data = {"content": "test"}
jsondata = json.dumps(data, separators=(',',':'))
- opener = urllib2.build_opener()
+ opener = urllib.request.build_opener()
#opener.addheaders = [('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0')]
response = opener.open(Settings.DISCORD_HOOK_URL, jsondata, 6)
the_page = response.read()
diff --git a/cgi/template.py b/cgi/template.py
index f58e17f..8c3fc19 100644
--- a/cgi/template.py
+++ b/cgi/template.py
@@ -69,19 +69,19 @@ def renderTemplate(template, template_values={}, mobile=False, noindex=False):
"board_long": board["longname"],
"board_type": board["board_type"],
"oek_finish": 0,
- "disable_name": (board["disable_name"] == '1'),
- "disable_subject": (board["disable_subject"] == '1'),
+ "disable_name": board["disable_name"],
+ "disable_subject": board["disable_subject"],
"default_subject": board["subject"],
"postarea_desc": board["postarea_desc"],
"postarea_extra": board["postarea_extra"],
- "allow_images": (board["allow_images"] == '1'),
- "allow_image_replies": (board["allow_image_replies"] == '1'),
- "allow_noimage": (board["allow_noimage"] == '1'),
- "allow_spoilers": (board["allow_spoilers"] == '1'),
- "allow_oekaki": (board["allow_oekaki"] == '1'),
- "archive": (board["archive"] == '1'),
+ "allow_images": board["allow_images"],
+ "allow_image_replies": board["allow_image_replies"],
+ "allow_noimage": board["allow_noimage"],
+ "allow_spoilers": board["allow_spoilers"],
+ "allow_oekaki": board["allow_oekaki"],
+ "archive": board["archive"],
"force_css": board["force_css"],
- "noindex": (board["secret"] == '1'),
+ "noindex": board["secret"],
"useid": board["useid"],
"maxsize": board["maxsize"],
"maxage": board["maxage"],
diff --git a/cgi/templates/home.html b/cgi/templates/home.html
index bc37431..04fbf72 100644
--- a/cgi/templates/home.html
+++ b/cgi/templates/home.html
@@ -8,9 +8,9 @@
<title>Bienvenido a Internet BBS/IB</title>
<link rel="shortcut icon" href="favicon.ico" />
<link rel="stylesheet" type="text/css" href="/home.css?v=9998" />
- <link rel="stylesheet" type="text/css" href="/home_1.css?v=9999" title="main" />
+ <link rel="stylesheet" type="text/css" href="/home_moracrema.css?v=3" title="main" />
<link rel="alternate stylesheet" type="text/css" href="/home_2.css" title="alt" />
- <script type="text/javascript" src="#{static_url}js/home.js?v=9999"></script>
+ <script type="text/javascript" src="#{static_url}js/home.js?v=100"></script>
</head>
<body class="home">
<table id="all">
@@ -54,7 +54,6 @@
<div id="extra">
<a href="/bai.html" target="_top">Frame</a> ·
<a href="/movil.html">BaI Móvil</a> ·
- <a href="http://bai5vj7eniqkrau5.onion/" target="_top">Onion</a> ·
<a href="//tv.bienvenidoainternet.org/" target="_blank">BaI TV</a> ·
<a href="//radio.bienvenidoainternet.org/" target="_blank">BaI Radio</a> ·
<a href="/cgi/stats" target="_blank">Estadísticas</a>
diff --git a/cgi/templates/manage/boardoptions.html b/cgi/templates/manage/boardoptions.html
index 436b036..fcd3bb8 100644
--- a/cgi/templates/manage/boardoptions.html
+++ b/cgi/templates/manage/boardoptions.html
@@ -38,7 +38,7 @@
<td>
<select style="width:100%;" name="type">
<option value="0">Imageboard</option>
- <option value="1"#{selected(boardopts['board_type'] == '1')}>Textboard</option>
+ <option value="1"#{selected(boardopts['board_type'] == 1)}>Textboard</option>
</select>
</td>
</tr>
@@ -74,9 +74,9 @@
<td>
<select name="useid" style="width:100%;">
<option value="0">Desactivado</option>
- <option value="1"#{selected(boardopts['useid'] == '1')}>Activado</option>
- <option value="2"#{selected(boardopts['useid'] == '2')}>Activado siempre</option>
- <option value="3"#{selected(boardopts['useid'] == '3')}>Activado siempre, detallado</option>
+ <option value="1"#{selected(boardopts['useid'] == 1)}>Activado</option>
+ <option value="2"#{selected(boardopts['useid'] == 2)}>Activado siempre</option>
+ <option value="3"#{selected(boardopts['useid'] == 3)}>Activado siempre, detallado</option>
</select>
</td>
</tr>
@@ -85,9 +85,9 @@
<td>
<select name="slip" style="width:100%;">
<option value="0">Desactivado</option>
- <option value="1"#{selected(boardopts['slip'] == '1')}>Activado</option>
- <option value="2"#{selected(boardopts['slip'] == '2')}>Sólo dominio</option>
- <option value="3"#{selected(boardopts['slip'] == '3')}>Todo</option>
+ <option value="1"#{selected(boardopts['slip'] == 1)}>Activado</option>
+ <option value="2"#{selected(boardopts['slip'] == 2)}>Sólo dominio</option>
+ <option value="3"#{selected(boardopts['slip'] == 3)}>Todo</option>
</select>
</td>
</tr>
@@ -96,45 +96,45 @@
<td>
<select name="countrycode" style="width:100%;">
<option value="0">Desactivado</option>
- <option value="1"#{selected(boardopts['countrycode'] == '1')}>Activado</option>
+ <option value="1"#{selected(boardopts['countrycode'] == 1)}>Activado</option>
</select>
</td>
</tr>
<tr>
<td class="postblock">Desactivar nombre</td>
-<td><input type="checkbox" name="disable_name" id="noname" value="1"#{checked(boardopts['disable_name'] == '1')} /><label for="noname"></label></td>
+<td><input type="checkbox" name="disable_name" id="noname" value="1"#{checked(boardopts['disable_name'])} /><label for="noname"></label></td>
</tr>
<tr>
<td class="postblock">Desactivar asunto</td>
-<td><input type="checkbox" name="disable_subject" id="nosub" value="1"#{checked(boardopts['disable_subject'] == '1')} /><label for="nosub"></label></td>
+<td><input type="checkbox" name="disable_subject" id="nosub" value="1"#{checked(boardopts['disable_subject'])} /><label for="nosub"></label></td>
</tr>
<tr>
<td class="postblock">Papelera de reciclaje</td>
-<td><input type="checkbox" name="recyclebin" id="bin" value="1"#{checked(boardopts['recyclebin'] == '1')} /><label for="bin"></label></td>
+<td><input type="checkbox" name="recyclebin" id="bin" value="1"#{checked(boardopts['recyclebin'])} /><label for="bin"></label></td>
</tr>
<tr>
<td class="postblock">Cerrado</td>
-<td><input type="checkbox" name="locked" id="locked" value="1"#{checked(boardopts['locked'] == '1')} /><label for="locked"></label></td>
+<td><input type="checkbox" name="locked" id="locked" value="1"#{checked(boardopts['locked'])} /><label for="locked"></label></td>
</tr>
<tr>
<td class="postblock">Secreto</td>
-<td><input type="checkbox" name="secret" id="secret" value="1"#{checked(boardopts['secret'] == '1')} /><label for="secret"></label></td>
+<td><input type="checkbox" name="secret" id="secret" value="1"#{checked(boardopts['secret'])} /><label for="secret"></label></td>
</tr>
<tr>
<td class="postblock">Permitir spoilers</td>
-<td><input type="checkbox" name="allow_spoilers" id="spoil" value="1"#{checked(boardopts['allow_spoilers'] == '1')} /><label for="spoil"></label></td>
+<td><input type="checkbox" name="allow_spoilers" id="spoil" value="1"#{checked(boardopts['allow_spoilers'])} /><label for="spoil"></label></td>
</tr>
<tr>
<td class="postblock">Permitir oekaki</td>
-<td><input type="checkbox" name="allow_oekaki" id="oek" value="1"#{checked(boardopts['allow_oekaki'] == '1')} /><label for="oek"></label></td>
+<td><input type="checkbox" name="allow_oekaki" id="oek" value="1"#{checked(boardopts['allow_oekaki'])} /><label for="oek"></label></td>
</tr>
<tr>
<td class="postblock">Permitir crear hilos sin imagen</td>
-<td><input type="checkbox" name="allow_noimage" id="noimgallow" value="1"#{checked(boardopts['allow_noimage'] == '1')} /><label for="noimgallow"></label></td>
+<td><input type="checkbox" name="allow_noimage" id="noimgallow" value="1"#{checked(boardopts['allow_noimage'])} /><label for="noimgallow"></label></td>
</tr>
<tr>
<td class="postblock">Permitir subida</td>
-<td><input type="checkbox" name="allow_images" id="img" value="1"#{checked(boardopts['allow_images'] == '1')} /><label for="img">Al crear un hilo</label><br /><input type="checkbox" name="allow_image_replies" id="imgres" value="1"#{checked(boardopts['allow_image_replies'] == '1')} /><label for="imgres">Al responder</label></td>
+<td><input type="checkbox" name="allow_images" id="img" value="1"#{checked(boardopts['allow_images'])} /><label for="img">Al crear un hilo</label><br /><input type="checkbox" name="allow_image_replies" id="imgres" value="1"#{checked(boardopts['allow_image_replies'])} /><label for="imgres">Al responder</label></td>
</tr>
<tr>
<td class="postblock">Tipos de archivo</td>
diff --git a/cgi/templates/manage/ipshow.html b/cgi/templates/manage/ipshow.html
index 6937a0e..4e9de17 100644
--- a/cgi/templates/manage/ipshow.html
+++ b/cgi/templates/manage/ipshow.html
@@ -49,14 +49,14 @@
<td></td>
<?py #endif ?>
<td>
- <?py if post['IS_DELETED'] == '0': ?>
- <a href="#{cgi_url}manage/delete/#{post['dir']}/#{post['id']}">Eliminar</a>
- <?py elif post['IS_DELETED'] == '1': ?>
- <a href="#{cgi_url}manage/recyclebin/0/restore/#{post['dir']}/#{post['id']}">Rec</a>
+ <?py if post['IS_DELETED'] == 1: ?>
+ <a href="#{cgi_url}manage/recyclebin/0/restore/#{post['dir']}/#{post['id']}">Rec</a>
<abbr title="Eliminado por usuario">[1]</abbr>
- <?py else: ?>
+ <?py elif post['IS_DELETED'] == 2: ?>
<a href="#{cgi_url}manage/recyclebin/0/restore/#{post['dir']}/#{post['id']}">Rec</a>
<abbr title="Eliminado por staff">[2]</abbr>
+ <?py else: ?>
+ <a href="#{cgi_url}manage/delete/#{post['dir']}/#{post['id']}">Eliminar</a>
<?py #endif ?>
</td>
</tr>
diff --git a/cgi/templates/revision.html b/cgi/templates/revision.html
index ef50561..78bc1ab 100644
--- a/cgi/templates/revision.html
+++ b/cgi/templates/revision.html
@@ -1 +1 @@
-0.8.10
+0.10.0
diff --git a/cgi/templates/txt_board.html b/cgi/templates/txt_board.html
index 7b0207b..375613a 100644
--- a/cgi/templates/txt_board.html
+++ b/cgi/templates/txt_board.html
@@ -45,9 +45,9 @@
<div class="threadnav"><a href="#menu" title="Lista de hilos">&#9632;</a><a href="##{(titer-1) if titer>1 else len(threads)}" title="Hilo anterior">&#9650;</a><a href="##{(titer+1) if titer<len(threads) else '1'}" title="Hilo siguiente">&#9660;</a></div>
<h2><span>[#{titer}:#{thread['length']}]</span><a href="#{boards_url}#{board}/read/#{thread['timestamp']}/#{'l50' if thread['length'] > 50 else ''}">#{thread['posts'][0]['subject']}</a></h2>
<?py for post in thread['posts']: ?>
-<?py if post['IS_DELETED'] == '1': ?>
+<?py if post['IS_DELETED'] == 1: ?>
<div class="reply deleted" id="p#{post['id']}" data-n="#{post['num']}"><h4>#{post['num']} : Mensaje eliminado por usuario.</h4></div>
-<?py elif post['IS_DELETED'] == '2': ?>
+<?py elif post['IS_DELETED'] == 2: ?>
<div class="reply deleted" id="p#{post['id']}" data-n="#{post['num']}"><h4>#{post['num']} : Mensaje eliminado por staff.</h4></div>
<?py else: ?>
<div class="reply#{' first' if post['num'] == 1 else ''}" id="p#{post['id']}" data-n="#{post['num']}">
@@ -77,7 +77,7 @@
</div>
<?py #endif ?>
<?py #endfor ?>
-<?py if thread['locked'] != '1': ?>
+<?py if not thread['locked']: ?>
<form id="postform#{thread['id']}" class="postform" action="#{cgi_url}post" method="post" enctype="multipart/form-data">
<input type="hidden" name="board" value="#{board}" /><input type="hidden" name="parent" value="#{thread['id']}" /><input type="hidden" name="password" value="" />
<div style="display:none"><input type="text" name="name" size="15" /> <input type="text" name="email" size="15" /></div>
diff --git a/cgi/templates/txt_thread.html b/cgi/templates/txt_thread.html
index c8d70d1..d2d3224 100644
--- a/cgi/templates/txt_thread.html
+++ b/cgi/templates/txt_thread.html
@@ -9,7 +9,7 @@
<?py if thread['length'] > 100: ?>
<a href="#{boards_url}#{board}/read/#{thread['timestamp']}/1-100">Primeros 100</a>
<?py #endif ?>
- <?py for i in range(thread['length'] / 100): ?>
+ <?py for i in range(thread['length'] // 100): ?>
<a href="#{boards_url}#{board}/read/#{thread['timestamp']}/#{(i+1)*100+1}-#{(i+2)*100}">#{(i+1)*100+1}-</a>
<?py #endfor ?>
<?py if thread['length'] > 51: ?>
@@ -28,9 +28,9 @@
<div class="thread" data-length="#{thread['length']}">
<h3>#{thread['subject']} <span>(${(str(thread['length'])+" respuestas") if thread['length']>1 else "Una respuesta"})</span></h3>
<?py for post in thread['posts']: ?>
- <?py if post['IS_DELETED'] == '1': ?>
+ <?py if post['IS_DELETED'] == 1: ?>
<div class="reply deleted" id="p#{post['id']}" data-n="#{post['num']}"><h4>#{post['num']} : Mensaje eliminado por usuario.</h4></div>
- <?py elif post['IS_DELETED'] == '2': ?>
+ <?py elif post['IS_DELETED'] == 2: ?>
<div class="reply deleted" id="p#{post['id']}" data-n="#{post['num']}"><h4>#{post['num']} : Mensaje eliminado por staff.</h4></div>
<?py else: ?>
<div class="reply#{' first' if post['num'] == 1 else ''}" id="p#{post['id']}" data-n="#{post['num']}">
@@ -59,7 +59,7 @@
<div id="size">#{thread['size']}</div>
</div>
<hr />
-<?py if thread['locked'] != '1': ?>
+<?py if thread['locked'] != 1: ?>
<div class="lastposts"><a href="#{boards_url}#{board}/read/#{thread['timestamp']}/#{thread['length']}-n" id="n">Ver nuevos posts</a></div>
<hr />
<?py #endif ?>
@@ -84,7 +84,7 @@
<a href="#top">&#9650;Subir&#9650;</a>
</div>
<input type="hidden" name="board" value="#{board}" /><input type="hidden" name="parent" value="#{thread['id']}" /><input type="hidden" name="password" value="" />
- <?py if thread['locked'] != '1': ?>
+ <?py if thread['locked'] != 1: ?>
<div style="display:none"><input type="text" name="name" size="13" /> <input type="text" name="email" size="13" /></div>
<input type="submit" value="Responder" accesskey="z" /> <input type="button" name="preview" value="Previsualizar" /> <span><span>Nombre: </span><input type="text" name="fielda" size="13" accesskey="n" /> <span>E-mail: </span><input type="text" name="fieldb" size="13" accesskey="e" /></span><br />
<textarea name="message" cols="80" rows="7" accesskey="m"></textarea><br />
@@ -103,4 +103,4 @@
</div>
<a name="bottom"></a>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/cgi/weabot.py b/cgi/weabot.py
index 38efa7c..720916d 100755
--- a/cgi/weabot.py
+++ b/cgi/weabot.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
# coding=utf-8
# Remove the first line to use the env command to locate python
@@ -9,8 +9,8 @@ import datetime
import random
import cgi
import logging
-import _mysql
-from Cookie import SimpleCookie
+from MySQLdb import _mysql
+from http.cookies import SimpleCookie
import tenjin
import manage
@@ -23,7 +23,7 @@ from formatting import *
from post import *
from img import *
-__version__ = "0.8.10"
+__version__ = "0.10.0"
# Set to True to disable weabot's exception routing and enable profiling
_DEBUG = False
@@ -35,7 +35,7 @@ class weabot(object):
def __init__(self, environ, start_response):
global _DEBUG
- logging.basicConfig(filename='weabot.log', format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO)
+ logging.basicConfig(filename='weabot.log', format='%(asctime)s %(levelname)s %(message)s', level=logging.DEBUG)
self.environ = environ
if self.environ["PATH_INFO"].startswith("/weabot.py/"):
@@ -61,9 +61,9 @@ class weabot(object):
else:
try:
self.run()
- except UserError, message:
+ except UserError as message:
self.error(message)
- except Exception, inst:
+ except Exception as inst:
logging.exception(inst)
import sys
@@ -74,11 +74,12 @@ class weabot(object):
self.exception(type(inst), inst, detail)
# close database and finish
- CloseDb()
+ #CloseDb()
def __iter__(self):
self.handleResponse()
self.start("200 OK", self.headers)
+ self.output = self.output.encode('utf-8')
yield self.output
def error(self, message):
@@ -115,7 +116,7 @@ class weabot(object):
def handleResponse(self):
if self._newcookies:
- for newcookie in self._newcookies.values():
+ for newcookie in list(self._newcookies.values()):
self.headers.append(
("Set-Cookie", newcookie.output(header="")))
@@ -129,8 +130,8 @@ class weabot(object):
caught = False
if Settings.FULL_MAINTENANCE:
- raise UserError, _(
- "%s is currently under maintenance. We'll be back.") % Settings.SITE_TITLE
+ raise UserError(_(
+ "%s is currently under maintenance. We'll be back.") % Settings.SITE_TITLE)
if len(path_split) > 1:
if path_split[1] == "post":
@@ -138,7 +139,7 @@ class weabot(object):
caught = True
if 'password' not in self.formdata:
- raise UserError, "El request está incompleto."
+ raise UserError("El request está incompleto.")
# let's get all the POST data we need
ip = self.environ["REMOTE_ADDR"]
@@ -156,7 +157,7 @@ class weabot(object):
oek_file = self.formdata.get('oek_file')
password = self.formdata.get('password', '')
noimage = self.formdata.get('noimage')
- mobile = ("mobile" in self.formdata.keys())
+ mobile = ("mobile" in self.formdata)
# call post function
(post_url, ttaken, unused) = self.make_post(ip, boarddir, parent, trap1, trap2, name,
@@ -202,7 +203,7 @@ class weabot(object):
board = setBoard(path_split[2])
caught = True
if board['board_type'] != '1':
- raise UserError, "No disponible para esta sección."
+ raise UserError("No disponible para esta sección.")
self.output = threadList(0)
elif path_split[1] == "mobile":
OpenDb()
@@ -261,18 +262,15 @@ class weabot(object):
caught = True
# Redirect to ban page if user is banned
- if addressIsBanned(self.environ['REMOTE_ADDR'], board["dir"], blind_only=True):
- raise UserError, '<meta http-equiv="refresh" content="0; url=/cgi/banned/%s">' % board["dir"]
+ if Settings.ENABLE_BANS and addressIsBanned(self.environ['REMOTE_ADDR'], board["dir"], blind_only=True):
+ raise UserError('<meta http-equiv="refresh" content="0; url=/cgi/banned/%s">' % board["dir"])
if len(path_split) > 4 and path_split[4] and board['board_type'] == '1':
- # try:
self.output = dynamicRead(int(path_split[3]), path_split[4], True)
- # except:
- # self.output = threadPage(path_split[3], True)
- elif board['board_type'] == '1':
- self.output = threadPage(0, True, path_split[3])
+ elif board['board_type'] == 1:
+ self.output = threadPage(0, True, int(path_split[3]))
else:
- self.output = threadPage(path_split[3], True)
+ self.output = threadPage(int(path_split[3]), True)
elif path_split[1] == "catalog":
OpenDb()
board = setBoard(path_split[2])
@@ -334,15 +332,15 @@ class weabot(object):
bans = FetchAll("SELECT * FROM `bans` WHERE INET6_ATON('"+self.environ["REMOTE_ADDR"]+"') BETWEEN `ipstart` AND `ipend`")
if bans:
for ban in bans:
- if ban["boards"] != "":
+ if ban["boards"]:
boards = pickle.loads(ban["boards"])
- if ban["boards"] == "" or path_split[2] in boards:
+ if ban["boards"] or path_split[2] in boards:
caught = True
if ban["boards"]:
boards_str = '/' + '/, /'.join(boards) + '/'
else:
boards_str = 'todas'
- if ban["until"] != "0":
+ if ban["until"]:
expire = formatTimestamp(ban["until"])
else:
expire = ""
@@ -374,8 +372,8 @@ class weabot(object):
board = setBoard(path_split[2])
# Redirect to ban page if user is banned
- if addressIsBanned(self.environ['REMOTE_ADDR'], board["dir"], blind_only=True):
- raise UserError, '<meta http-equiv="refresh" content="0; url=/cgi/banned/%s">' % board["dir"]
+ if Settings.ENABLE_BANS and addressIsBanned(self.environ['REMOTE_ADDR'], board["dir"], blind_only=True):
+ raise UserError('<meta http-equiv="refresh" content="0; url=/cgi/banned/%s">' % board["dir"])
self.output = dynamicRead(int(path_split[3]), path_split[4])
elif path_split[1] == "preview":
@@ -386,7 +384,7 @@ class weabot(object):
message = format_post(
self.formdata["message"], self.environ["REMOTE_ADDR"], self.formdata["parentid"])
self.output = message
- except Exception, messagez:
+ except Exception as messagez:
self.output = "Error: " + \
str(messagez) + " : " + str(self.formdata)
elif path_split[1] == "mod":
@@ -400,11 +398,16 @@ class weabot(object):
# Redirect the user back to the front page
self.output += '<html xmlns="http://www.w3.org/1999/xhtml"><body><meta http-equiv="refresh" content="0;url=%s" /><p>--&gt; --&gt; --&gt;</p></body></html>' % Settings.HOME_URL
+ CloseDb()
+
def make_post(self, ip, boarddir, parent, trap1, trap2, name, email, subject, message, file, file_original, spoil, oek_file, password, noimage, mobile):
- _STARTTIME = time.clock() # Comment if not debug
+ _STARTTIME = time.process_time() # Comment if not debug
- if hostIsBanned(ip):
- raise UserError, "Sufijo de host en lista negra."
+ if Settings.PROXY_BANS and ip not in Settings.PROXY_WHITELIST and (boarddir not in Settings.EXCLUDE_GLOBAL_BANS):
+ if addressIsTor(ip) or addressIsProxy(ip) or addressIsBannedCountry(ip) or not addressIsES(ip):
+ raise UserError("Proxy prohibido.")
+ if hostIsBanned(ip):
+ raise UserError("Sufijo de host en lista negra.")
# open database
OpenDb()
@@ -412,25 +415,21 @@ class weabot(object):
# set the board
board = setBoard(boarddir)
- if Settings.PROXY_BAN and (board["dir"] not in Settings.EXCLUDE_GLOBAL_BANS):
- if addressIsTor(ip) or addressIsProxy(ip) or addressIsBannedCountry(ip) or not addressIsES(ip):
- raise UserError, "Proxy prohibido."
-
# check length of fields
if len(name) > 50:
- raise UserError, "El campo de nombre es muy largo."
+ raise UserError("El campo de nombre es muy largo.")
if len(email) > 50:
- raise UserError, "El campo de e-mail es muy largo."
+ raise UserError("El campo de e-mail es muy largo.")
if len(subject) > 100:
- raise UserError, "El campo de asunto es muy largo."
+ raise UserError("El campo de asunto es muy largo.")
if len(message) > 8000:
- raise UserError, "El campo de mensaje es muy largo."
+ raise UserError("El campo de mensaje es muy largo.")
if message.count('\n') > 50:
- raise UserError, "El mensaje tiene muchos saltos de línea."
+ raise UserError("El mensaje tiene muchos saltos de línea.")
# anti-spam trap
if trap1 or trap2:
- raise UserError, "Te quedan tres días de vida."
+ raise UserError("Te quedan tres días de vida.")
# Create a single datetime now so everything syncs up
t = time.time()
@@ -441,14 +440,14 @@ class weabot(object):
regenerateAccess()
# Redirect to ban page if user is banned
- if addressIsBanned(ip, board["dir"]):
- raise UserError, '<meta http-equiv="refresh" content="0; url=/cgi/banned/%s">' % board["dir"]
+ if Settings.ENABLE_BANS and addressIsBanned(ip, board["dir"]):
+ raise UserError('<meta http-equiv="refresh" content="0; url=/cgi/banned/%s">' % board["dir"])
# Disallow posting if the site OR board is in maintenance
if Settings.MAINTENANCE and board["dir"] != 'polka':
- raise UserError, _("%s is currently under maintenance. We'll be back.") % Settings.SITE_TITLE
+ raise UserError(_("%s is currently under maintenance. We'll be back.") % Settings.SITE_TITLE)
if board["locked"] == '1':
- raise UserError, _("This board is closed. You can't post in it.")
+ raise UserError(_("This board is closed. You can't post in it."))
# create post object
post = Post(board["id"])
@@ -465,7 +464,7 @@ class weabot(object):
post["parentid"] = parent_post['id']
post["bumped"] = parent_post['bumped']
if parent_post['locked'] == '1':
- raise UserError, _("The thread is closed. You can't post in it.")
+ raise UserError(_("The thread is closed. You can't post in it."))
# check if the user is flooding
flood_check(t, post, board["id"])
@@ -550,10 +549,10 @@ class weabot(object):
# compatibility : old id function
if 'id' in parent_post["email"]:
- board["useid"] = '3'
+ board["useid"] = 3
if 'id' in post["email"]:
- board["useid"] = '3'
+ board["useid"] = 3
if extend:
try:
@@ -591,35 +590,35 @@ class weabot(object):
tim = post["timestamp"]
# make ID hash
- if board["useid"] != '0':
+ if board["useid"]:
post["timestamp_formatted"] += ' ID:' + iphash(ip, post, tim, board["useid"], mobile,
self.environ["HTTP_USER_AGENT"], cap_id, hide_end, (board["countrycode"] in ['1', '2']))
# use for future file checks
- xfile = (file or oek_file)
+ xfile = (file is not None or oek_file)
# textboard inforcements (change it to settings maybe?)
- if board['board_type'] == '1':
+ if board['board_type'] == 1:
if not post["parentid"] and not post["subject"]:
- raise UserError, _(
- "You must enter a title to create a thread.")
+ raise UserError(_(
+ "You must enter a title to create a thread."))
if not post["message"]:
- raise UserError, _("Please enter a message.")
+ raise UserError(_("Please enter a message."))
else:
if not post["parentid"] and not xfile and not noimage:
- raise UserError, _(
- "You must upload an image first to create a thread.")
+ raise UserError(_(
+ "You must upload an image first to create a thread."))
if not xfile and not post["message"]:
- raise UserError, _(
- "Please enter a message or upload an image to reply.")
+ raise UserError(_(
+ "Please enter a message or upload an image to reply."))
# check if this post is allowed
if post["parentid"]:
- if file and board['allow_image_replies'] == '0':
- raise UserError, _("Image replies not allowed.")
+ if file and not board['allow_image_replies']:
+ raise UserError(_("Image replies not allowed."))
else:
- if file and board['allow_images'] == '0':
- raise UserError, _("No images allowed.")
+ if file and not board['allow_images']:
+ raise UserError(_("No images allowed."))
# use default values when missing / remove sage from wrong fields
if (not post["name"] and not post["tripcode"]) or (post["name"].lower() == 'sage'):
@@ -637,7 +636,7 @@ class weabot(object):
with open(fname, 'rb') as f:
file = f.read()
except:
- raise UserError, "Imposible leer la imagen oekaki."
+ raise UserError("Imposible leer la imagen oekaki.")
if file and not noimage:
post = processImage(post, file, t, file_original,
@@ -700,7 +699,7 @@ class weabot(object):
host = getHost(ip)
if host:
- for k, v in isps.iteritems():
+ for k, v in isps.items():
if k in host:
host_nick = v
break
@@ -708,7 +707,7 @@ class weabot(object):
slips.append(host_nick)
# hash
- if board["slip"] in ['1', '3']:
+ if board["slip"] in [1, 3]:
if hide_end:
slips.append(
'-'.join((getMD5(ip + post["name"])[:4], '****')))
@@ -720,7 +719,7 @@ class weabot(object):
'-'.join((getMD5(ip)[:4], getMD5(self.environ["HTTP_USER_AGENT"])[:4])))
# host
- if board["slip"] == '2':
+ if board["slip"] == 2:
if hide_end:
host = '★'
else:
@@ -743,7 +742,7 @@ class weabot(object):
slips.append(host)
# IP
- if board["slip"] == '3':
+ if board["slip"] == 3:
if hide_end:
host = '[*.*.*.*]'
else:
@@ -755,7 +754,7 @@ class weabot(object):
post["tripcode"] += " (%s)" % ' '.join(slips)
# country code
- if board["countrycode"] == '1':
+ if board["countrycode"] == 1:
if hide_end or addressIsTor(ip):
country = '??'
else:
@@ -763,7 +762,7 @@ class weabot(object):
post["name"] += " <em>[%s]</em>" % country
# set expiration date if necessary
- if board["maxage"] != '0' and not post["parentid"]:
+ if board["maxage"] != 0 and not post["parentid"]:
if board["dir"] == '2d':
date_format = '%m月%d日'
date_format_y = '%Y年%m月'
@@ -784,7 +783,7 @@ class weabot(object):
if board["dir"] == 'noticias':
# check if there's at least one link
if "<a href" not in post["message"]:
- raise UserError, "Al momento de crear un hilo en esta sección necesitas incluír al menos 1 link como fuente en tu mensaje."
+ raise UserError("Al momento de crear un hilo en esta sección necesitas incluír al menos 1 link como fuente en tu mensaje.")
# insert icon if needed
img_src = '<img src="%s" alt="ico" /><br />' % getRandomIco()
@@ -797,11 +796,11 @@ class weabot(object):
trimThreads()
# fix null references when creating thread
- if board["board_type"] == '1' and not post["parentid"]:
+ if board["board_type"] == 1 and not post["parentid"]:
post["message"] = re.compile(r'<a href="/(\w+)/res/0.html/(.+)"').sub(
r'<a href="/\1/res/'+str(postid)+r'.html/\2"', post["message"])
- UpdateDb("UPDATE `posts` SET message = '%s' WHERE boardid = '%s' AND id = '%s'" % (_mysql.escape_string(
- post["message"]), _mysql.escape_string(board["id"]), _mysql.escape_string(str(postid))))
+ UpdateDb("UPDATE `posts` SET message = %s WHERE boardid = %s AND id = %s",
+ (post["message"], board["id"], postid))
# do operations if replying to a thread (bump, autoclose, update cache)
logging.debug("Updating thread")
@@ -812,13 +811,15 @@ class weabot(object):
# bump if not saged
if 'sage' not in post["email"].lower() and parent_post['locked'] != '2':
- UpdateDb("UPDATE `posts` SET bumped = %d WHERE (`id` = '%s' OR `parentid` = '%s') AND `boardid` = '%s'" % (post["timestamp"], post["parentid"], post["parentid"], board["id"]))
+ UpdateDb("UPDATE `posts` SET bumped = %s WHERE (`id` = %s OR `parentid` = %s) AND `boardid` = %s",
+ (post["timestamp"], post["parentid"], post["parentid"], board["id"]))
# check if thread must be closed
autoclose_thread(post["parentid"], t, thread_length)
# update final attributes (length and last post)
- UpdateDb("UPDATE `posts` SET length = %d, last = %d WHERE `id` = '%s' AND `boardid` = '%s'" % (thread_length, post["timestamp"], post["parentid"], board["id"]))
+ UpdateDb("UPDATE `posts` SET length = %s, last = %s WHERE `id` = %s AND `boardid` = %s",
+ (thread_length, post["timestamp"], post["parentid"], board["id"]))
# update cache
threadUpdated(post["parentid"])
@@ -829,13 +830,13 @@ class weabot(object):
regenerateHome()
# make page redirect
- ttaken = timeTaken(_STARTTIME, time.clock())
- noko = 'noko' in email.lower() or (board["board_type"] == '1')
+ ttaken = timeTaken(_STARTTIME, time.process_time())
+ noko = 'noko' in email.lower() or (board["board_type"] == 1)
# get new post url
post_url = make_url(postid, post, parent_post or post, noko, mobile)
- if board['secret'] == '0':
+ if not board['secret']:
# add to recent posts
if Settings.ENABLE_RSS:
latestAdd(post, thread_length, postid, parent_post)
@@ -854,33 +855,33 @@ class weabot(object):
board = setBoard(boarddir)
if board["dir"] == '0':
- raise UserError, "No se pueden eliminar mensajes en esta sección."
+ raise UserError("No se pueden eliminar mensajes en esta sección.")
# check if we have a post id and check it's numeric
if not postid:
- raise UserError, "Selecciona uno o más mensajes a eliminar."
+ raise UserError("Selecciona uno o más mensajes a eliminar.")
# make sure we have a password
if not password:
- raise UserError, _("Please enter a password.")
+ raise UserError(_("Please enter a password."))
to_delete = []
if isinstance(postid, list):
- to_delete = [n.value for n in postid]
+ to_delete = [int(n.value) for n in postid]
else:
- to_delete = [postid]
+ to_delete = [int(postid)]
# delete posts
- if board['board_type'] == '1' and len(to_delete) == 1:
+ if board['board_type'] == 1 and len(to_delete) == 1:
# we should be deleting only one (textboard)
# check if it's the last post and delete permanently if so
- deltype = '0'
+ deltype = 0
post = FetchOne("SELECT `id`, `timestamp`, `parentid` FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1" % (
board["id"], str(to_delete[0])))
- if post['parentid'] != '0':
+ if post['parentid']:
op = get_parent_post(post['parentid'], board['id'])
if op['last'] != post['timestamp']:
- deltype = '1'
+ deltype = 1
deletePost(to_delete[0], password, deltype, imageonly)
latestRemove(post['id'])
@@ -896,10 +897,10 @@ class weabot(object):
deletePost(pid, password, board['recyclebin'], imageonly)
latestRemove(pid)
deleted += 1
- msgs.append('No.%s: Eliminado' % pid)
- except UserError, message:
+ msgs.append('No.%d: Eliminado' % pid)
+ except UserError as message:
errors += 1
- msgs.append('No.%s: %s' % (pid, message))
+ msgs.append('No.%d: %s' % (pid, message))
# regenerate home
if deleted:
@@ -907,8 +908,8 @@ class weabot(object):
# show errors, if any
if errors:
- raise UserError, 'No todos los mensajes pudieron ser eliminados.<br />' + \
- '<br />'.join(msgs)
+ raise UserError('No todos los mensajes pudieron ser eliminados.<br />' + \
+ '<br />'.join(msgs))
# redirect
if imageonly:
@@ -921,7 +922,7 @@ class weabot(object):
def report(self, ip, boarddir, postid, reason, txt, postshow):
# don't allow if the report system is off
if not Settings.REPORTS_ENABLE:
- raise UserError, _('Report system is deactivated.')
+ raise UserError(_('Report system is deactivated.'))
# if there's not a reason, show the report page
if reason is None:
@@ -931,9 +932,9 @@ class weabot(object):
# check reason
if not reason:
- raise UserError, _("Enter a reason.")
+ raise UserError(_("Enter a reason."))
if len(reason) > 100:
- raise UserError, _("Text too long.")
+ raise UserError(_("Text too long."))
# open database
OpenDb()
@@ -942,14 +943,14 @@ class weabot(object):
board = setBoard(boarddir)
# check if he's banned
- if addressIsBanned(ip, board["dir"]):
- raise UserError, _("You're banned.")
+ if Settings.ENABLE_BANS and addressIsBanned(ip, board["dir"]):
+ raise UserError(_("You're banned."))
# check if post exists
post = FetchOne("SELECT `id`, `parentid`, `ip` FROM `posts` WHERE `id` = '%s' AND `boardid` = '%s'" % (
_mysql.escape_string(str(postid)), _mysql.escape_string(board['id'])))
if not post:
- raise UserError, _("Post doesn't exist.")
+ raise UserError(_("Post doesn't exist."))
# generate link
if board["board_type"] == '1':
@@ -989,33 +990,33 @@ class weabot(object):
OpenDb()
# 1 week = 604800
- query_day = FetchAll("SELECT DATE_FORMAT(FROM_UNIXTIME(FLOOR((timestamp-10800)/86400)*86400+86400), \"%Y-%m-%d\"), COUNT(1), COUNT(IF(parentid=0, 1, NULL)) "
+ query_day = FetchAll("SELECT DATE_FORMAT(FROM_UNIXTIME(FLOOR((timestamp-10800)/86400)*86400+86400), \"%Y-%m-%d\") AS date, COUNT(1) AS count, COUNT(IF(parentid=0, 1, NULL)) AS threads "
"FROM posts "
"WHERE (timestamp-10800) > (UNIX_TIMESTAMP()-604800) AND (IS_DELETED = 0) "
"GROUP BY FLOOR((timestamp-10800)/86400) "
- "ORDER BY FLOOR((timestamp-10800)/86400)", 0)
+ "ORDER BY FLOOR((timestamp-10800)/86400)")
- query_count = FetchOne("SELECT COUNT(1), COUNT(NULLIF(file, '')), VERSION() FROM posts", 0)
- total = int(query_count[0])
- total_files = int(query_count[1])
- mysql_ver = query_count[2]
+ query_count = FetchOne("SELECT COUNT(1) AS posts, COUNT(NULLIF(file, '')) AS files, VERSION() AS version FROM posts")
+ total = query_count["posts"]
+ total_files = query_count["files"]
+ mysql_ver = query_count["version"]
- archive_count = FetchOne("SELECT SUM(length) FROM archive", 0)
- total_archived = int(archive_count[0])
+ archive_count = FetchOne("SELECT SUM(length) AS sum FROM archive")
+ total_archived = int(archive_count["sum"])
days = []
- for date, count, threads in query_day[1:]:
- days.append((date, count, threads))
+ for r in query_day[1:]:
+ days.append((r["date"], r["count"], r["threads"]))
- query_b = FetchAll("SELECT id, dir, name FROM boards WHERE boards.secret = 0", 0)
+ query_b = FetchAll("SELECT id, dir, name FROM boards WHERE boards.secret = 0")
boards = []
totalp = 0
- for id, dir, longname in query_b:
- bposts = FetchOne("SELECT COUNT(1) FROM posts "
- "WHERE '"+str(id)+"' = posts.boardid AND timestamp > ( UNIX_TIMESTAMP(DATE(NOW())) - 2419200 )", 0)
- boards.append((dir, longname, int(bposts[0])))
- totalp += int(bposts[0])
+ for b in query_b:
+ bposts = FetchOne("SELECT COUNT(1) AS count FROM posts "
+ "WHERE posts.boardid = %s AND timestamp > ( UNIX_TIMESTAMP(DATE(NOW())) - 2419200 )", (b['id'],))
+ boards.append((b['dir'], b['name'], bposts["count"]))
+ totalp += bposts["count"]
boards = sorted(boards, key=lambda boards: boards[2], reverse=True)
@@ -1069,7 +1070,7 @@ class weabot(object):
if __name__ == "__main__":
- from fcgi import WSGIServer
+ from flup.server.fcgi import WSGIServer
# Psyco is not required, however it will be used if available
try:
@@ -1084,4 +1085,6 @@ if __name__ == "__main__":
except:
pass
- WSGIServer(weabot).run()
+ app = WSGIServer(weabot, debug=True, forceCGI=False)
+ app.run()
+