# coding=utf-8
import os
import cgi
import html
import shutil
import datetime
import logging
from database import *
from settings import Settings
from framework import *
from formatting import *
from template import *
from post import *
def manage(self, path_split):
page = ''
validated = False
administrator = False
moderator = True
skiptemplate = False
staff_account = None
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")
if not first_admin:
InsertDb("INSERT INTO `staff` (`username`, `password`, `added`, `rights`) VALUES ('admin', %s, 0, 0)", (genPasswdHash("admin"),))
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` < %s", (timestamp() - Settings.MANAGE_LOG_TIME,))
else:
page += _('Incorrect username/password.')
logAction('', 'Failed login. U:'+self.formdata['username']+' IP logged.')
logging.warn("Failed login. U:{} IP:{}".format(self.formdata['username'], self.ip))
else:
# Validate existing session
manage_cookie = getCookie(self, 'weabot_manage')
if manage_cookie:
staff_account = validateSession(manage_cookie)
if not staff_account:
page += "La sesión ha expirado. Por favor ingresa tus credenciales nuevamente."
deleteCookie(self, 'weabot_manage')
if staff_account:
validated = True
if 'session_id' in staff_account:
renewSession(staff_account['session_id'])
if staff_account['rights'] in [0, 1, 2]:
administrator = True
if staff_account['rights'] == 2:
moderator = False
UpdateDb('UPDATE `staff` SET `lastactive` = %s WHERE `id` = %s LIMIT 1',
(timestamp(), staff_account['id']))
if not validated:
template_filename = "login.html"
template_values = {}
else:
if len(path_split) > 2:
if path_split[2] == 'rebuild':
if not administrator:
return
try:
board_dir = path_split[3]
except:
board_dir = ''
if board_dir == '':
template_filename = "rebuild.html"
template_values = {'boards': boardlist()}
else:
everything = ("everything" in self.formdata)
if board_dir == '!ALL':
t1 = time.time()
boards = FetchAll(
'SELECT `dir` FROM `boards` WHERE secret = 0')
for board in boards:
board = setBoard(board['dir'])
regenerateBoard(everything)
message = _('Rebuilt %(board)s in %(time)s seconds.') % {
'board': _('all boards'), 'time': timeTaken(t1, time.time())}
logAction(staff_account['username'], _(
'Rebuilt %s') % _('all boards'))
elif board_dir == '!BBS':
t1 = time.time()
boards = FetchAll(
'SELECT `dir` FROM `boards` WHERE `board_type` = 1')
for board in boards:
board = setBoard(board['dir'])
regenerateBoard(everything)
message = _('Rebuilt %(board)s in %(time)s seconds.') % {
'board': _('all boards'), 'time': timeTaken(t1, time.time())}
logAction(staff_account['username'], _(
'Rebuilt %s') % _('all boards'))
elif board_dir == '!IB':
t1 = time.time()
boards = FetchAll(
'SELECT `dir` FROM `boards` WHERE `board_type` = 1')
for board in boards:
board = setBoard(board['dir'])
regenerateBoard(everything)
message = _('Rebuilt %(board)s in %(time)s seconds.') % {
'board': _('all boards'), 'time': timeTaken(t1, time.time())}
logAction(staff_account['username'], _(
'Rebuilt %s') % _('all boards'))
elif board_dir == '!HOME':
t1 = time.time()
regenerateHome()
message = _('Rebuilt %(board)s in %(time)s seconds.') % {
'board': _('home'), 'time': timeTaken(t1, time.time())}
logAction(staff_account['username'], _(
'Rebuilt %s') % _('home'))
elif board_dir == '!NEWS':
t1 = time.time()
regenerateNews()
message = _('Rebuilt %(board)s in %(time)s seconds.') % {
'board': _('news'), 'time': timeTaken(t1, time.time())}
logAction(staff_account['username'], _(
'Rebuilt %s') % _('news'))
elif board_dir == '!TRASH':
t1 = time.time()
regenerateTrash()
message = _('Rebuilt %(board)s in %(time)s seconds.') % {
'board': _('trash'), 'time': timeTaken(t1, time.time())}
logAction(staff_account['username'], _(
'Rebuilt %s') % _('trash'))
elif board_dir == '!KAKO':
t1 = time.time()
boards = FetchAll(
'SELECT `dir` FROM `boards` WHERE archive = 1')
for board in boards:
board = setBoard(board['dir'])
regenerateKako()
message = _('Rebuilt %(board)s in %(time)s seconds.') % {
'board': 'kako', 'time': timeTaken(t1, time.time())}
logAction(staff_account['username'], _(
'Rebuilt %s') % 'kako')
elif board_dir == '!HTACCESS':
t1 = time.time()
if regenerateAccess():
message = _('Rebuilt %(board)s in %(time)s seconds.') % {
'board': _('htaccess'), 'time': timeTaken(t1, time.time())}
logAction(staff_account['username'], _(
'Rebuilt %s') % _('htaccess'))
else:
message = _(
'htaccess regeneration deactivated by sysop.')
else:
t1 = time.time()
board = setBoard(board_dir)
regenerateBoard(everything)
message = _('Rebuilt %(board)s in %(time)s seconds.') % {
'board': '/' + board['dir'] + '/', 'time': timeTaken(t1, time.time())}
logAction(staff_account['username'],
'Rebuilt /' + board['dir'] + '/')
template_filename = "message.html"
elif path_split[2] == 'mod':
if not moderator:
return
try:
board = setBoard(path_split[3])
except:
board = ""
if not board:
template_filename = "mod.html"
template_values = {"mode": 1, 'boards': boardlist()}
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']:
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 = %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:
threads = FetchAll("SELECT * FROM `posts` WHERE boardid = %s AND parentid = 0 ORDER BY `bumped` DESC" % board["id"])
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, 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, posts.IS_DELETED, 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:
return
action_taken = False
if len(path_split) > 3:
if path_split[3] == 'add' or path_split[3] == 'edit':
member = None
member_username = ''
member_rights = 3
if path_split[3] == 'edit':
if len(path_split) > 4:
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/' + str(member['id'])
try:
if self.formdata.get('user'):
if self.formdata['rights'] in [0, 1, 2, 3]:
action_taken = True
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'], _(
'Updated staff account for %s') % self.formdata['user'])
template_filename = "message.html"
except:
pass
else:
action = 'add'
if self.formdata.get('user') and self.formdata.get('pass'):
username_taken = FetchOne(
'SELECT COUNT(1) as count FROM `staff` WHERE `username` = %s LIMIT 1', (self.formdata['user'],))
if not username_taken['count']:
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 (%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'])
template_filename = "message.html"
else:
action_taken = True
message = _(
'That username is already in use.')
template_filename = "message.html"
if not action_taken:
action_taken = True
if action == 'add':
submit = 'Agregar'
else:
submit = 'Editar'
template_filename = "staff.html"
template_values = {'mode': 1,
'action': action,
'member': member,
'member_username': member_username,
'member_rights': member_rights,
'submit': submit}
elif path_split[3] == 'delete':
if not moderator:
return
action_taken = True
message = '' + _('Click here to confirm the deletion of that staff member') + ''
template_filename = "message.html"
elif path_split[3] == 'delete_confirmed':
if not moderator:
return
action_taken = True
member = FetchOne('SELECT `username` FROM `staff` WHERE `id` = %s LIMIT 1', (path_split[4],))
if member:
UpdateDb('DELETE FROM `staff` WHERE `id` = %s LIMIT 1', (path_split[4],))
message = 'Staff member deleted.'
template_filename = "message.html"
logAction(staff_account['username'], _(
'Deleted staff account for %s') % member['username'])
else:
message = _(
'Unable to locate a staff account with that ID.')
template_filename = "message.html"
if not action_taken:
staff = FetchAll('SELECT * FROM `staff` ORDER BY `rights`')
for member in staff:
if member['rights'] == 0:
member['rights'] = _('Super-administrator')
elif member['rights'] == 1:
member['rights'] = _('Administrator')
elif member['rights'] == 2:
member['rights'] = _('Developer')
elif member['rights'] == 3:
member['rights'] = _('Moderator')
if member['lastactive']:
member['lastactivestamp'] = member['lastactive']
member['lastactive'] = formatTimestamp(
member['lastactive'])
else:
member['lastactive'] = _('Never')
member['lastactivestamp'] = 0
template_filename = "staff.html"
template_values = {'mode': 0, 'staff': staff}
elif path_split[2] == 'delete':
if not moderator:
return
do_ban = False
try:
if self.formdata['ban'] == 'true':
do_ban = True
except:
pass
board = setBoard(path_split[3])
post = FetchOne('SELECT id, message, thumb, name, tripcode FROM posts WHERE boardid = %s AND id = %s LIMIT 1' % (board['id'], path_split[4]))
template_filename = "delete.html"
template_values = {'do_ban': do_ban,
'curboard': board,
'post': post }
elif path_split[2] == 'delete_confirmed':
if not moderator:
return
do_ban = self.formdata.get('ban')
permanently = self.formdata.get('perma')
imageonly = self.formdata.get('imageonly')
board = setBoard(path_split[3])
postid = int(path_split[4])
post = FetchOne('SELECT id, message, parentid, INET6_NTOA(ip) AS ip FROM posts WHERE boardid = %s AND id = %s' % (board['id'], postid))
if not permanently:
deletePost(path_split[4], None, 2, imageonly)
regenerateTrash()
else:
deletePost(path_split[4], None, 0, imageonly)
regenerateHome()
# Borrar denuncias
UpdateDb("DELETE FROM `reports` WHERE `postid` = %s",
(int(path_split[4]),))
boards = FetchAll('SELECT `name`, `dir` FROM `boards` ORDER BY `dir`')
if imageonly:
message = 'Archivo de post /%s/%s eliminado.' % (
board['dir'], post['id'])
elif permanently or post["parentid"] == 0:
message = 'Post /%s/%s eliminado permanentemente.' % (
board['dir'], post['id'])
else:
message = 'Post /%s/%s enviado a la papelera.' % (
board['dir'], post['id'])
template_filename = "message.html"
logAction(staff_account['username'], message +
' Contenido: ' + post['message'] + ' IP: ' + post['ip'])
if do_ban:
message = _('Redirecting to ban page...') + ''
template_filename = "message.html"
elif path_split[2] == 'lock':
setLocked = 0
# Nos vamos al board y ubicamos el post
board = setBoard(path_split[3])
postid = int(path_split[4])
post = FetchOne('SELECT `parentid`, `locked` FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1', (board['id'], pid) )
if not post:
message = _('Unable to locate a post with that ID.')
template_filename = "message.html"
else:
if not post['parentid']:
message = _('Post is not a thread opener.')
template_filename = "message.html"
else:
if post['locked'] == 0:
# Cerrar si esta abierto
setLocked = 1
else:
# Abrir si esta cerrado
setLocked = 0
UpdateDb("UPDATE `posts` SET `locked` = %s WHERE `boardid` = %s AND `id` = %s LIMIT 1",
(setLocked, board["id"], post["id"]))
threadUpdated(pid)
if setLocked == 1:
message = _('Thread successfully closed.')
logAction(staff_account['username'], _('Closed thread %s') % ('/' + board['dir'] + '/' + pid) )
else:
message = _('Thread successfully opened.')
logAction(staff_account['username'], _('Opened thread %s') % ('/' + board['dir'] + '/' + pid) )
template_filename = "message.html"
elif path_split[2] == 'permasage':
setPermasaged = 0
# Nos vamos al board y ubicamos el post
board = setBoard(path_split[3])
pid = int(path_split[4])
post = FetchOne('SELECT `parentid`, `locked` FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1', (board['id'], pid) )
if not post:
message = 'No se encuentra un hilo con ese ID.'
template_filename = "message.html"
elif post['locked'] == 1:
message = 'Solo se puede aplicar permasage en un hilo abierto.'
template_filename = "message.html"
else:
if post['parentid']:
message = 'El post indicado es una respuesta a un hilo.'
template_filename = "message.html"
else:
if post['locked'] == 2:
# Sacar permasage
setPermasaged = 0
else:
# Colocar permasage
setPermasaged = 2
UpdateDb("UPDATE `posts` SET `locked` = %s WHERE `boardid` = '%s' AND `id` = '%s' LIMIT 1" % (setPermasaged, board["id"], pid) )
regenerateFrontPages()
threadUpdated(pid)
if setPermasaged == 2:
message = 'Thread successfully permasaged.'
logAction(staff_account['username'], 'Activado permasage en el hilo /%s/%s' % (board['dir'], pid) )
else:
message = 'Thread successfully un-permasaged.'
logAction(staff_account['username'], 'Desactivado permasage en el hilo /%s/%s' % (board['dir'], pid) )
template_filename = "message.html"
elif path_split[2] == 'move':
raise NotImplementedError
#if not moderator:
if not administrator:
return
oldboardid = ""
oldthread = ""
newboardid = ""
try:
oldboardid = path_split[3]
oldthread = path_split[4]
newboardid = path_split[5]
except:
pass
try:
oldboardid = self.formdata['oldboardid']
oldthread = self.formdata['oldthread']
newboardid = self.formdata['newboardid']
except:
pass
if oldboardid and oldthread and newboardid:
message = "import"
import shutil
message += "ok"
board = setBoard(oldboardid)
oldboard = board['dir']
oldboardsubject = board['subject']
oldboardname = random.choice(board["anonymous"].split('|'))
# get old posts
posts = FetchAll(
"SELECT * FROM `posts` WHERE (`id` = {0} OR `parentid` = {0}) AND `boardid` = {1} ORDER BY id ASC".format(oldthread, board['id']))
# switch to new board
board = setBoard(newboardid)
newboard = board['dir']
refs = {}
moved_files = []
moved_thumbs = []
moved_cats = []
newthreadid = 0
newthread = 0
num = 1
message = "from total: %s
" % len(posts)
template_filename = "message.html"
for p in posts:
# save old post ID
old_id = p['id']
is_op = bool(p['parentid'] == '0')
# copy post object but without ID and target boardid
post = Post()
post.post = p
post.post.pop("id")
post["boardid"] = board['id']
post["parentid"] = newthreadid
# save the files we need to move if any
if post['IS_DELETED'] == '0':
if post['file']:
moved_files.append(post['file'])
if post['thumb']:
moved_thumbs.append(post['thumb'])
if is_op:
moved_cats.append(post['thumb'])
# fix subject if necessary
if post['subject'] and post['subject'] == oldboardsubject:
post['subject'] = board['subject']
# fix new default name
if post['name'] == oldboardname:
post['name'] = board['anonymous']
# fix date and (re)add post ID if necessary
post['timestamp_formatted'] = formatTimestamp(post['timestamp'])
if board["useid"] != '0':
if post["parentid"]:
tym = parent_time
else:
tym = post["timestamp"]
post['timestamp_formatted'] += ' ID:' + iphash(post['ip'], post, tym, board["useid"], False, '', False, False, (board["countrycode"] in [1, 2]))
# insert new post and get its new ID
new_id = post.insert()
# save the reference (BBS = post number, IB = new ID)
refs[old_id] = num if board['board_type'] == 1 else new_id
# this was an OP
message += "newthread = %s parentid = %s
" % (
newthreadid, p['parentid'])
if is_op:
oldthread = old_id
newthreadid = new_id
oldbumped = post["bumped"]
# BBS = new thread timestamp, IB = new thread ID
newthread = post['timestamp'] if board['board_type'] == 1 else new_id
parent_time = post['timestamp']
# log it
message += "%s -> %s
" % (old_id, new_id)
num += 1
# fix anchors
for old, new in refs.items():
old_url = "/{oldboard}/res/{oldthread}.html#{oldpost}\">>>{oldpost}".format(
oldboard=oldboard, oldthread=oldthread, oldpost=old)
if board['board_type'] == 1:
new_url = "/{newboard}/read/{newthread}/{newpost}\">>>{newpost}".format(
newboard=newboard, newthread=newthread, newpost=new)
else:
new_url = "/{newboard}/res/{newthread}.html#{newpost}\">>>{newpost}".format(
newboard=newboard, newthread=newthread, newpost=new)
sql = "UPDATE `posts` SET `message` = REPLACE(message, '{old}', '{new}') WHERE `boardid` = {newboardid} AND (`id` = {newthreadid} OR `parentid` = {newthreadid})".format(
old=old_url, new=new_url, newboardid=board['id'], newthreadid=newthreadid)
message += sql + "
"
UpdateDb(sql)
# copy files
for file in moved_files:
if not os.path.isfile(Settings.IMAGES_DIR + newboard + "/src/" + file):
shutil.copyfile(Settings.IMAGES_DIR + oldboard + "/src/" +
file, Settings.IMAGES_DIR + newboard + "/src/" + file)
for thumb in moved_thumbs:
if not os.path.isfile(Settings.IMAGES_DIR + newboard + "/thumb/" + thumb):
shutil.copyfile(Settings.IMAGES_DIR + oldboard + "/thumb/" +
thumb, Settings.IMAGES_DIR + newboard + "/thumb/" + thumb)
if not os.path.isfile(Settings.IMAGES_DIR + newboard + "/mobile/" + thumb):
shutil.copyfile(Settings.IMAGES_DIR + oldboard + "/mobile/" +
thumb, Settings.IMAGES_DIR + newboard + "/mobile/" + thumb)
for cat in moved_cats:
try:
if not os.path.isfile(Settings.IMAGES_DIR + newboard + "/cat/" + thumb):
shutil.copyfile(Settings.IMAGES_DIR + oldboard + "/cat/" +
thumb, Settings.IMAGES_DIR + newboard + "/cat/" + thumb)
except:
pass
# lock original, set expiration to 1 day
exp = timestamp()+86400
exp_format = datetime.datetime.fromtimestamp(exp).strftime("%d/%m")
sql = "UPDATE `posts` SET `locked`=1, `expires`={exp}, `expires_formatted`=\"{exp_format}\" WHERE `boardid`=\"{oldboard}\" AND id=\"{oldthread}\"".format(exp=exp, exp_format=exp_format, oldboard=oldboardid, oldthread=oldthread)
UpdateDb(sql)
# insert notice message
if 'msg' in self.formdata:
leavemsg = True
board = setBoard(oldboard)
if board['board_type'] == 1:
thread_url = "/{newboard}/read/{newthread}".format(newboard=newboard, newthread=newthread)
else:
thread_url = "/{newboard}/res/{newthread}.html".format(newboard=newboard, newthread=newthread)
notice_post = Post(board["id"])
notice_post["parentid"] = oldthread
if board['board_type'] == "0":
notice_post["subject"] = "Aviso"
notice_post["name"] = "Sistema"
notice_post["message"] = "El hilo ha sido movido a /{newboard}/{newthread}.".format(
url=thread_url, newboard=newboard, newthread=newthread)
notice_post["timestamp"] = timestamp()+1
notice_post["timestamp_formatted"] = "Hilo movido"
notice_post["bumped"] = oldbumped
notice_post.insert()
regenerateFrontPages()
regenerateThreadPage(oldthread)
# regenerate again (fix?)
board = setBoard(newboardid)
regenerateFrontPages()
regenerateThreadPage(newthreadid)
message += "done"
logAction(staff_account['username'], "Movido hilo %s/%s a %s/%s." % (oldboard, oldthread, newboard, newthread))
else:
template_filename = "move.html"
template_values = {'boards': boardlist(
), 'oldboardid': oldboardid, 'oldthread': oldthread}
elif path_split[2] == 'ban':
if not moderator:
return
if len(path_split) > 4:
board = setBoard(path_split[3])
post = FetchOne('SELECT INET6_NTOA(`ip`) AS `ip` FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1' % (board['id'], int(path_split[4])) )
if not post:
message = _('Unable to locate a post with that ID.')
template_filename = "message.html"
else:
message = 'Espere...'
template_filename = "message.html"
else:
reason = self.formdata.get('reason')
if reason is not None:
# Start ban process
import netaddr
ip = self.formdata['ip']
# Parse CIDR or IP glob
try:
if netaddr.valid_ipv4(ip) or netaddr.valid_ipv6(ip):
ipaddress = netaddr.IPAddress(ip)
ipstart = ipend = ipstr = str(ipaddress)
elif netaddr.valid_glob(ip):
iprange = netaddr.glob_to_iprange(ip)
ipstart, ipend = str(iprange[0]), str(iprange[-1])
cidrs = iprange.cidrs()
if len(cidrs) == 1:
ipstr = str(cidrs[0])
else:
ipstr = str(iprange)
elif '/' in ip:
# Try with CIDR
ipnetwork = netaddr.IPNetwork(ip)
ipstart, ipend = str(ipnetwork[0]), str(ipnetwork[-1])
ipstr = str(ipnetwork)
else:
self.error("IP o rango inválido.")
return
except netaddr.core.AddrFormatError as e:
self.error("Problema con el IP o rango ingresado: {}".format(e))
return
if self.formdata['seconds'] != '0':
until = timestamp() + int(self.formdata['seconds'])
else:
until = 0
where = ''
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:
if self.formdata[keyname] == "1":
where.append(board['dir'])
if len(where) > 0:
where = boards2str(where)
else:
self.error(
_("You must select where the ban shall be placed"))
return
if 'edit' in self.formdata:
UpdateDb("DELETE FROM `bans` WHERE `id` = '" + _mysql.escape_string(self.formdata['edit']) + "' LIMIT 1")
"""else: # TODO : Duplicate check
ban = FetchOne("SELECT `id` FROM `bans` WHERE `ip` = '" + _mysql.escape_string(
ip) + "' AND `boards` = '" + _mysql.escape_string(where) + "' LIMIT 1")
if ban:
self.error(_('There is already an identical ban for this IP.') + '' + _('Edit') + '')
return"""
# Blind mode
blind = self.formdata.get('blind', 0)
#raise UserError, "{} {} {}".format(ipstart, ipend, ipstr)
# Banear sin mensaje
InsertDb("INSERT INTO `bans` (`ipstart`, `ipend`, `ipstr`, `boards`, `added`, `until`, `staff`, `reason`, `note`, `blind`) VALUES "
"(INET6_ATON(%s), INET6_ATON(%s), %s, %s, %s, %s, %s, %s, %s, %s)",
(ipstart, ipend, ipstr, where, timestamp(), until, staff_account['username'], self.formdata['reason'], self.formdata['note'], blind))
regenerateAccess()
if 'edit' in self.formdata:
message = _('Ban successfully edited.')
action = 'Edited ban for ' + ip
else:
message = _('Ban successfully placed.')
action = 'Banned ' + ip
if until != 0:
action += ' until ' + \
formatTimestamp(until)
else:
action += ' permanently'
logAction(staff_account['username'], action)
template_filename = 'message.html'
else:
startvalues = {'where': [],
'reason': '',
'note': '',
'message': '(GET OUT)',
'seconds': 0,
'blind': 1}
edit_id = 0
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` = %s ORDER BY `added` DESC", (edit_id) )
if ban:
if ban['boards'] == '':
where = ''
else:
where = boards2str(ban['boards'])
if ban['until'] == '0':
until = 0
else:
until = int(ban['until']) - timestamp()
startvalues = {'where': where,
'reason': ban['reason'],
'note': ban['note'],
'seconds': str(until),
'blind': ban['blind']
}
else:
edit_id = 0
template_filename = "bans.html"
template_values = {'mode': 1,
'boards': boardlist(),
'ip': self.formdata.get('ip'),
'startvalues': startvalues,
'edit_id': edit_id}
elif path_split[2] == 'bans':
if not moderator:
return
action_taken = False
if len(path_split) > 4:
if path_split[3] == 'delete':
ip = FetchOne("SELECT ipstr FROM `bans` WHERE `id` = %s LIMIT 1",
(path_split[4],))
if ip:
# Delete ban
UpdateDb('DELETE FROM `bans` WHERE `id` = %s LIMIT 1',
(path_split[4],))
regenerateAccess()
message = _('Ban successfully deleted.')
template_filename = "message.html"
logAction(staff_account['username'], _(
'Deleted ban for %s') % ip)
else:
message = _(
'There was a problem while deleting that ban. It may have already been removed, or recently expired.')
template_filename = "message.html"
if not action_taken:
bans = FetchAll(
"SELECT `id`, `ipstr` AS 'ip', boards, added, until, staff, reason, note, blind FROM `bans` ORDER BY `added` DESC")
if bans:
for ban in bans:
if ban['boards'] == '':
ban['boards'] = _('All boards')
else:
where = str2boards(ban['boards'])
if len(where) > 1:
ban['boards'] = '/' + \
'/, /'.join(where) + '/'
else:
ban['boards'] = '/' + where[0] + '/'
ban['added'] = formatTimestamp(ban['added'])
if ban['until'] == 0:
ban['until'] = _('Does not expire')
else:
ban['until'] = formatTimestamp(ban['until'])
if ban['blind']:
ban['blind'] = 'Sí'
else:
ban['blind'] = 'No'
template_filename = "bans.html"
template_values = {'mode': 0, 'bans': bans}
elif path_split[2] == 'changepassword':
form_submitted = False
try:
if self.formdata['oldpassword'] != '' and self.formdata['newpassword'] != '' and self.formdata['newpassword2'] != '':
form_submitted = True
except:
pass
if form_submitted:
if verifyPasswd(staff_account['username'], self.formdata['oldpassword']):
if self.formdata['newpassword'] == self.formdata['newpassword2']:
UpdateDb('UPDATE `staff` SET `password` = %s WHERE `id` = %s LIMIT 1',
(genPasswdHash(self.formdata['newpassword']), staff_account['id']))
message = _(
'Password successfully changed. Please log out and log back in.')
template_filename = "message.html"
else:
message = _('Passwords did not match.')
template_filename = "message.html"
else:
message = _('Current password incorrect.')
template_filename = "message.html"
else:
template_filename = "changepassword.html"
template_values = {}
elif path_split[2] == 'board':
if not administrator:
return
if len(path_split) > 3:
board = setBoard(path_split[3])
form_submitted = False
try:
if self.formdata['name'] != '':
form_submitted = True
except:
pass
if form_submitted:
rebuild = 0
# Update board settings
if self.formdata['longname'] != board['longname']:
board['longname'] = self.formdata['longname']
if board['board_type'] == 1:
rebuild |= RB_FRONT
else:
rebuild |= RB_ALL
if board['name'] != self.formdata['name']:
board['name'] = self.formdata['name']
rebuild |= RB_BOARD
if board['board_type'] == 1:
rebuild |= RB_KAKO
if board['subname'] != self.formdata['subname']:
board['subname'] = self.formdata['subname']
rebuild |= RB_HOME
board['anonymous'] = self.formdata['anonymous']
board['subject'] = self.formdata['subject']
board['message'] = self.formdata['message']
board['useid'] = self.formdata['useid']
board['slip'] = self.formdata['slip']
board['countrycode'] = self.formdata['countrycode']
if 'recyclebin' in self.formdata:
board['recyclebin'] = 1
else:
board['recyclebin'] = 0
if 'disable_name' in self.formdata and not board['disable_name']:
board['disable_name'] = 1
if board['board_type'] == 0:
rebuild |= RB_ALL
elif 'disable_name' not in self.formdata and board['disable_name']:
board['disable_name'] = 0
if board['board_type'] == 0:
rebuild |= RB_ALL
if 'disable_subject' in self.formdata and not board['disable_subject']:
board['disable_subject'] = 1
if board['board_type'] == 0:
rebuild |= RB_ALL
elif 'disable_subject' not in self.formdata and board['disable_subject']:
board['disable_subject'] = 0
if board['board_type'] == 0:
rebuild |= RB_ALL
if 'secret' in self.formdata:
board['secret'] = 1
else:
board['secret'] = 0
if 'locked' in self.formdata:
board['locked'] = 1
else:
board['locked'] = 0
if board['postarea_desc'] != self.formdata['postarea_desc']:
board['postarea_desc'] = self.formdata['postarea_desc']
if board['board_type'] == 1:
rebuild |= RB_FRONT
else:
rebuild |= RB_ALL
if board['postarea_extra'] != self.formdata['postarea_extra']:
board['postarea_extra'] = self.formdata['postarea_extra']
if board['board_type'] == 1:
rebuild |= RB_FRONT
else:
rebuild |= RB_ALL
if board['force_css'] != self.formdata['force_css']:
board['force_css'] = self.formdata['force_css']
rebuild |= RB_ALL
if 'allow_noimage' in self.formdata and not board['allow_noimage']:
board['allow_noimage'] = 1
if board['board_type'] == 0:
rebuild |= RB_ALL
elif 'allow_noimage' not in self.formdata and board['allow_noimage']:
board['allow_noimage'] = 0
if board['board_type'] == 0:
rebuild |= RB_ALL
if 'allow_images' in self.formdata and not board['allow_images']:
board['allow_images'] = 1
rebuild |= RB_FRONT
elif 'allow_images' not in self.formdata and board['allow_images']:
board['allow_images'] = 0
rebuild |= RB_FRONT
if 'allow_image_replies' in self.formdata and not board['allow_image_replies']:
board['allow_image_replies'] = 1
rebuild |= RB_ALL
elif 'allow_image_replies' not in self.formdata and board['allow_image_replies']:
board['allow_image_replies'] = 0
rebuild |= RB_ALL
if 'allow_spoilers' in self.formdata and not board['allow_spoilers']:
board['allow_spoilers'] = 1
if board['board_type'] == 0:
rebuild |= RB_ALL
elif 'allow_spoilers' not in self.formdata and board['allow_spoilers']:
board['allow_spoilers'] = 0
if board['board_type'] == 0:
rebuild |= RB_ALL
if 'allow_oekaki' in self.formdata and not board['allow_oekaki']:
board['allow_oekaki'] = 1
if board['board_type'] == 0:
rebuild |= RB_ALL
elif 'allow_oekaki' not in self.formdata and board['allow_oekaki']:
board['allow_oekaki'] = 0
if board['board_type'] == 0:
rebuild |= RB_ALL
if 'archive' in self.formdata:
board['archive'] = 1
else:
board['archive'] = 0
# update file types
new_filetypes = [(ft.replace('filetype', '')) for ft in self.formdata if ft.startswith('filetype')]
if sorted(board['filetypes_ext']) != sorted(new_filetypes):
UpdateDb("DELETE FROM boards_filetypes WHERE boardid = %s", (board['id'],) )
for filetype in filetypelist():
if 'filetype'+filetype['ext'] in self.formdata:
UpdateDb("INSERT INTO boards_filetypes VALUES (%s, %s)", (board['id'], filetype['id']) )
if board['board_type'] == 0:
rebuild |= RB_ALL
try:
if board['numthreads'] != int(self.formdata['numthreads']):
board['numthreads'] = int(self.formdata['numthreads'])
rebuild |= RB_FRONT
except:
raise UserError(_("Max threads shown must be numeric."))
try:
if board['numcont'] != int(self.formdata['numcont']):
board['numcont'] = int(self.formdata['numcont'])
rebuild |= RB_FRONT
except:
raise UserError(_("Max replies shown must be numeric."))
try:
if board['numline'] != int(self.formdata['numline']):
board['numline'] = int(self.formdata['numline'])
rebuild |= RB_FRONT
except:
raise UserError(_("Max lines shown must be numeric."))
try:
if board['thumb_px'] != int(self.formdata['thumb_px']):
board['thumb_px'] = int(self.formdata['thumb_px'])
if board['board_type'] == 0:
rebuild |= RB_ALL
except:
raise UserError(_("Max thumb dimensions must be numeric."))
try:
if board['maxsize'] != int(self.formdata['maxsize']):
board['maxsize'] = int(self.formdata['maxsize'])
if board['board_type'] == 0:
rebuild |= RB_ALL
except:
raise UserError(_("Max size must be numeric."))
try:
if board['maxage'] != int(self.formdata['maxage']):
board['maxage'] = int(self.formdata['maxage'])
if board['board_type'] == 0:
rebuild |= RB_ALL
except:
raise UserError(_("Max age must be numeric."))
try:
if board['maxinactive'] != int(self.formdata['maxinactive']):
board['maxinactive'] = int(self.formdata['maxinactive'])
if board['board_type'] == 0:
rebuild |= RB_ALL
except:
raise UserError(_("Max inactivity must be numeric."))
try:
board['threadsecs'] = int(self.formdata['threadsecs'])
except:
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."))
new_type = int(self.formdata['type'])
if new_type != board['board_type']:
switchBoard(new_type)
board['board_type'] = new_type
rebuild |= RB_ALL
updateBoardSettings()
board = setBoard(board['dir'])
if rebuild & RB_ALL:
regenerateBoard(everything=True)
elif rebuild & RB_FRONT:
regenerateBoard()
if rebuild & RB_KAKO:
regenerateKako()
if rebuild & RB_HOME:
regenerateHome()
message = _('Board options successfully updated.')
template_filename = "message.html"
logAction(staff_account['username'], _('Updated options for /%s/') % board['dir'])
else:
template_filename = "boardoptions.html"
template_values = { 'mode': 1,
'boardopts': board,
'filetypes': filetypelist(),
'supported_filetypes': board['filetypes_ext'] }
else:
# List all boards
template_filename = "boardoptions.html"
template_values = {'mode': 0, 'boards': boardlist()}
elif path_split[2] == 'recyclebin':
if not administrator:
return
message = None
if len(path_split) > 5:
if path_split[4] == 'restore':
board = setBoard(path_split[5])
post = FetchOne('SELECT `parentid`, `IS_DELETED` FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1', (board['id'], path_split[6]))
if not post:
message = _('Unable to locate a post with that ID.') + '
'
template_filename = "message.html"
else:
UpdateDb('UPDATE `posts` SET `IS_DELETED` = 0 WHERE `boardid` = %s AND `id` = %s LIMIT 1', (board['id'], path_split[6]))
if post['parentid'] != 0:
threadUpdated(post['parentid'])
else:
regenerateFrontPages()
if post['IS_DELETED'] == 2:
regenerateTrash()
message = _('Post successfully restored.')
logAction(staff_account['username'], _('Restored post %s') % ('/' + path_split[5] + '/' + path_split[6]) )
if path_split[4] == 'delete':
board = setBoard(path_split[5])
post = FetchOne('SELECT id, parentid, message, INET6_NTOA(ip) AS ip FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1',
(board['id'], path_split[6]))
if not post:
message = _('Unable to locate a post with that ID.')
else:
deletePost(post['id'], None)
if post['parentid']:
threadUpdated(post['parentid'])
else:
regenerateFrontPages()
message = "Post %s eliminado permanentemente" % ('/' + board['dir'] + '/' + str(post['id']))
logAction(staff_account['username'], message + ' desde papelera. Contenido: ' + post['message'] + ' IP: ' + post['ip'])
# Delete more than 1 post
if 'deleteall' in self.formdata:
return # TODO
deleted = 0
for key in self.formdata:
if key[:2] == '!i':
# Board where the post is
dir = key[2:].split('/')[0]
postid = key[2:].split('/')[1] # Post to delete
# Delete post start
post = FetchOne('SELECT `parentid`, `dir` FROM `posts` INNER JOIN `boards` ON posts.boardid = boards.id WHERE `dir` = \'' + _mysql.escape_string(dir) + '\' AND posts.id = \'' + _mysql.escape_string(postid) + '\' LIMIT 1')
if not post:
message = _('Unable to locate a post with that ID.')
else:
board = setBoard(dir)
deletePost(int(postid), None)
if post['parentid']:
threadUpdated(post['parentid'])
else:
regenerateFrontPages()
deleted += 1
# Delete post end
logAction(staff_account['username'], _('Permadeleted %s post(s).') % str(deleted))
message = _('Permadeleted %s post(s).') % str(deleted)
# Start
import math
pagesize = float(Settings.RECYCLEBIN_POSTS_PER_PAGE)
try:
currentpage = int(path_split[3])
except:
currentpage = 0
skip = False
if 'type' in self.formdata:
type = int(self.formdata["type"])
else:
type = 0
# Generate board list
boards = FetchAll('SELECT `name`, `dir` FROM `boards` ORDER BY `dir`')
for board in boards:
if 'board' in self.formdata and self.formdata['board'] == board['dir']:
board['checked'] = True
else:
board['checked'] = False
# Get type filter
if type != 0:
type_condition = "= " + str(type)
else:
type_condition = "!= 0"
# Table
if 'board' in self.formdata and self.formdata['board'] != 'all':
cboard = setBoard(self.formdata['board'])['dir']
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" % (cboard, type_condition, currentpage*pagesize, pagesize))
try:
totals = FetchOne("SELECT COUNT(id) AS count FROM `posts` WHERE IS_DELETED %s AND `boardid` = %s LIMIT 1" % (type_condition, posts[0]['boardid']) )
except:
skip = True
else:
cboard = 'all'
posts = FetchAll("SELECT posts.id, posts.timestamp, timestamp_formatted, IS_DELETED, INET6_NTOA(posts.ip) AS ip, posts.message, dir FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE IS_DELETED %s ORDER BY `timestamp` DESC LIMIT %d, %d" % (type_condition, currentpage*pagesize, pagesize))
totals = FetchOne("SELECT COUNT(id) AS count FROM `posts` WHERE IS_DELETED %s" % type_condition)
template_filename = "recyclebin.html"
template_values = {'message': message,
'type': type,
'boards': boards,
'skip': skip}
if not skip:
# Calculate number of pages
total = totals["count"]
pages = int(math.ceil(total / pagesize))
# Create delete form
if 'board' in self.formdata and self.formdata['board'] != 'all':
board = setBoard(self.formdata['board'])['dir']
else:
board = None
navigator = ''
if currentpage > 0:
navigator += '< '
else:
navigator += '< '
for i in range(pages):
if i != currentpage:
navigator += ''+str(i)+' '
else:
navigator += str(i)+' '
if currentpage < (pages-1):
navigator += '> '
else:
navigator += '> '
template_values.update({'currentpage': currentpage,
'curboard': board,
'posts': posts,
'navigator': navigator})
# End recyclebin
elif path_split[2] == 'lockboard':
if not administrator:
return
try:
board_dir = path_split[3]
except:
board_dir = ''
if board_dir == '':
template_filename = "lockboard.html"
template_values = {'boards': boardlist()}
elif path_split[2] == 'boardlock':
board = setBoard(path_split[3])
if int(board['locked']):
# Si esta cerrado... abrir
board['locked'] = 0
updateBoardSettings()
message = _('Board opened successfully.')
template_filename = "message.html"
else:
# Si esta abierta, cerrar
board['locked'] = 1
updateBoardSettings()
message = _('Board closed successfully.')
template_filename = "message.html"
elif path_split[2] == 'addboard':
if not administrator:
return
action_taken = False
board_dir = ''
try:
if self.formdata['name'] != '':
board_dir = self.formdata['dir']
except:
pass
if board_dir != '':
action_taken = True
board_exists = FetchOne("SELECT * FROM `boards` WHERE `dir` = %s LIMIT 1", (board_dir,))
if not board_exists:
os.mkdir(Settings.ROOT_DIR + board_dir)
os.mkdir(Settings.ROOT_DIR + board_dir + '/res')
if not os.path.exists(Settings.IMAGES_DIR + board_dir):
os.mkdir(Settings.IMAGES_DIR + board_dir)
os.mkdir(Settings.IMAGES_DIR + board_dir + '/src')
os.mkdir(Settings.IMAGES_DIR + board_dir + '/thumb')
os.mkdir(Settings.IMAGES_DIR + board_dir + '/mobile')
os.mkdir(Settings.IMAGES_DIR + board_dir + '/cat')
if os.path.exists(Settings.ROOT_DIR + board_dir) and os.path.isdir(Settings.ROOT_DIR + board_dir):
UpdateDb("INSERT INTO `boards` (`dir`, `name`) VALUES (%s, %s)",
(board_dir, self.formdata['name']))
board = setBoard(board_dir)
f = open(Settings.ROOT_DIR +
board['dir'] + '/.htaccess', 'w')
try:
f.write('DirectoryIndex index.html')
finally:
f.close()
regenerateFrontPages()
message = _('Board added')
template_filename = "message.html"
logAction(staff_account['username'], _(
'Added board %s') % ('/' + board['dir'] + '/'))
else:
message = _(
'There was a problem while making the directories.')
template_filename = "message.html"
else:
message = _(
'There is already a board with that directory.')
template_filename = "message.html"
if not action_taken:
template_filename = "addboard.html"
template_values = {}
elif path_split[2] == 'trim':
if not administrator:
return
board = setBoard(path_split[3])
trimThreads()
self.output = "done trimming"
return
elif path_split[2] == 'setexpires':
if not administrator:
return
board = setBoard(path_split[3])
parentid = int(path_split[4])
days = int(path_split[5])
t = time.time()
expires = int(t) + (days * 86400)
date_format = '%d/%m'
expires_formatted = datetime.datetime.fromtimestamp(
expires).strftime(date_format)
sql = "UPDATE posts SET expires = timestamp + (%s * 86400), expires_formatted = FROM_UNIXTIME((timestamp + (%s * 86400)), '%s') WHERE boardid = %s AND id = %s" % (
str(days), str(days), date_format, board["id"], str(parentid))
UpdateDb(sql)
self.output = "done " + sql
return
elif path_split[2] == 'fixflood':
if not administrator:
return
board = setBoard('zonavip')
threads = FetchAll(
"SELECT * FROM posts WHERE boardid = %s AND parentid = 0 AND subject LIKE 'querido mod%%'" % board['id'])
for thread in threads:
self.output += "%s
" % thread['id']
#deletePost(thread['id'], None)
return
elif path_split[2] == 'fixico':
if not administrator:
return
board = setBoard(path_split[3])
if board['dir'] != 'noticias':
return
threads = FetchAll("SELECT * FROM posts WHERE boardid = %s AND parentid = 0 AND message NOT LIKE ''
fname = Settings.ROOT_DIR + \
board["dir"] + "/kako/" + \
str(item["timestamp"]) + ".json"
if os.path.isfile(fname):
import json
with open(fname) as f:
thread = json.load(f)
thread['posts'] = [
dict(list(zip(thread['keys'], row))) for row in thread['posts']]
template_fname = "txt_archive.html"
post_preview = cut_home_msg(
thread['posts'][0]['message'], 0)
page = renderTemplate("txt_archive.html", {"threads": [
thread], "preview": post_preview}, False)
with open(Settings.ROOT_DIR + board["dir"] + "/kako/" + str(thread['timestamp']) + ".html", "w") as f:
f.write(page)
self.output += 'done' + str(time.time() - t) + '
'
else:
self.output += 'El hilo no existe.
'
elif path_split[2] == 'fixexpires':
if not administrator:
return
board = setBoard(path_split[3])
if int(board["maxage"]):
date_format = '%d/%m'
date_format_y = '%m/%Y'
if int(board["maxage"]) >= 365:
date_format = date_format_y
sql = "UPDATE posts SET expires = timestamp + (%s * 86400), expires_formatted = FROM_UNIXTIME((timestamp + (%s * 86400)), '%s') WHERE boardid = %s AND parentid = 0" % (
board["maxage"], board["maxage"], date_format, board["id"])
UpdateDb(sql)
alert_time = int(
round(int(board['maxage']) * Settings.MAX_AGE_ALERT))
sql = "UPDATE posts SET expires_alert = CASE WHEN UNIX_TIMESTAMP() > (expires - %d*86400) THEN 1 ELSE 0 END WHERE boardid = %s AND parentid = 0" % (alert_time,
board["id"])
UpdateDb(sql)
else:
sql = "UPDATE posts SET expires = 0, expires_formatted = '', expires_alert = 0 WHERE boardid = %s AND parentid = 0" % (
board["id"])
UpdateDb(sql)
self.output = "done"
return
elif path_split[2] == 'fixid':
if not administrator:
return
board = setBoard(path_split[3])
posts = FetchAll(
'SELECT * FROM `posts` WHERE `boardid` = %s' % board['id'])
self.output = "total: %d
" % len(posts)
for post in posts:
new_timestamp_formatted = formatTimestamp(
post['timestamp'])
tim = 0
if board["useid"] != 0:
new_timestamp_formatted += ' ID:' + \
iphash(post['ip'], '', tim, 1,
False, False, False, 0)
self.output += "%s - %s
" % (
post['id'], new_timestamp_formatted)
query = "UPDATE `posts` SET timestamp_formatted = '%s' WHERE boardid = '%s' AND id = '%s'" % (
new_timestamp_formatted, board['id'], post['id'])
UpdateDb(query)
return
elif path_split[2] == 'fixname':
return
board = setBoard(path_split[3])
#posts = FetchAll('SELECT * FROM `posts` WHERE `boardid` = %s' % board['id'])
#posts = FetchAll('SELECT * FROM `posts` WHERE `name` LIKE \'%s\'' % '%%')
#posts = FetchAll('SELECT * FROM `posts` WHERE `name` = \'%s\'' % path_split[4])
new_name = board['anonymous']
self.output = new_name + "
"
for post in posts:
self.output += "%s
" % (post['id'])
query = "UPDATE `posts` SET `name` = '%s' WHERE boardid = '%s' AND id = '%s'" % (
new_name, board['id'], post['id'])
UpdateDb(query)
return
elif path_split[2] == 'setsub':
if not administrator:
return
board = setBoard(path_split[3])
thread = FetchOne(
'SELECT * FROM `posts` WHERE `parentid` = 0 AND `boardid` = %s' % board['id'])
subject = str(path_split[4])
self.output = subject + "->" + thread['id'] + "
"
query = "UPDATE `posts` SET `subject` = '%s' WHERE boardid = '%s' AND id = '%s'" % (
subject, board['id'], thread['id'])
UpdateDb(query)
return
elif path_split[2] == 'fixlength':
if not administrator:
return
board = setBoard(path_split[3])
threads = FetchAll(
'SELECT * FROM `posts` WHERE parentid = 0 AND `boardid` = %s' % board['id'])
for t in threads:
length = threadNumReplies(t['id'])
UpdateDb('UPDATE posts SET length = %d WHERE boardid = %s AND id = %s' % (
length, board['id'], t['id']))
self.output = 'done'
return
elif path_split[2] == 'archive':
if not administrator:
return
t = time.time()
board = setBoard(path_split[3])
postid = int(path_split[4])
archiveThread(postid)
self.output = "todo ok %s" % str(time.time() - t)
elif path_split[2] == 'filters':
action_taken = False
if len(path_split) > 3 and path_split[3] == 'add':
if "add" in self.formdata:
edit_id = 0
if 'edit' in self.formdata:
edit_id = int(self.formdata['edit'])
# We decide what type of filter it is.
# 0: Word / 1: Name/Trip
filter_type = int(self.formdata["type"])
filter_action = int(self.formdata["action"])
filter_from = ''
filter_tripcode = ''
where = ''
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:
if self.formdata[keyname] == "1":
where.append(board['dir'])
if len(where) > 0:
where = boards2str(where)
else:
self.error(
_("You must select what board the filter will affect"))
return
if filter_type == 0:
# Word filter
if len(self.formdata["word"]) > 0:
filter_from = html.escape(self.formdata["word"])
else:
self.error(_("You must enter a word."))
return
elif filter_type == 1:
# Name/trip filter
can_add = False
if len(self.formdata["name"]) > 0:
filter_from = self.formdata["name"]
can_add = True
if len(self.formdata["trip"]) > 0:
filter_tripcode = self.formdata["trip"]
can_add = True
if not can_add:
self.error(
_("You must enter a name and/or a tripcode."))
return
# Action
sql_query = ''
filter_reason = ''
if len(self.formdata["reason"]) > 0:
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 (%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 = self.formdata["changeto"]
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."))
return
elif filter_action == 2:
# Ban
filter_seconds = 0
if len(self.formdata["seconds"]) > 0:
filter_seconds = int(self.formdata["seconds"])
if "blind" in self.formdata and self.formdata["blind"] == 1:
filter_blind = 1
else:
filter_blind = 2
sql_query = "INSERT INTO `filters` (`id`, `boards`, `type`, `action`, `from`, `from_trip`, `reason`, `seconds`, `blind`, `added`, `staff`) VALUES (%s, %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_seconds, filter_blind, timestamp(), staff_account['username'])
elif filter_action == 3:
# Redirect URL
if len(self.formdata['redirect_url']) > 0:
redirect_url = self.formdata['redirect_url']
redirect_time = 0
try:
redirect_time = int(self.formdata['redirect_time'])
except:
pass
else:
self.error(
_("You must enter a URL to redirect to."))
return
sql_query = "INSERT INTO `filters` (`id`, `boards`, `type`, `action`, `from`, `from_trip`, `reason`, `redirect_url`, `redirect_time`, `added`, `staff`) VALUES (%s, %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, redirect_url, redirect_time, timestamp(), staff_account['username'])
# DO QUERY!
if edit_id > 0:
UpdateDb("DELETE FROM `filters` WHERE `id` = %s", (edit_id,))
UpdateDb(sql_query, sql_params)
message = 'Filter edited.'
else:
filt = FetchOne("SELECT `id` FROM `filters` WHERE `boards` = %s AND `type` = %s AND `from` = %s", (where, filter_type, filter_from))
if not filt:
UpdateDb(sql_query, sql_params)
message = 'Filter added.'
else:
message = 'This filter already exists here:' + ' edit'
action_taken = True
template_filename = "message.html"
else:
# Create add form
edit_id = 0
if 'edit' in self.formdata and int(self.formdata['edit']) > 0:
# Load values
edit_id = int(self.formdata['edit'])
filt = FetchOne(
"SELECT * FROM `filters` WHERE `id` = %s LIMIT 1" % str(edit_id))
if not filt['boards']:
where = ''
else:
where = str2boards(filt['boards'])
startvalues = {'type': filt['type'],
'trip': filt['from_trip'],
'where': where,
'action': filt['action'],
'changeto': html.escape(filt['to'], True),
'reason': filt['reason'],
'seconds': filt['seconds'],
'blind': filt['blind'],
'redirect_url': filt['redirect_url'],
'redirect_time': filt['redirect_time'], }
if filt['type'] == 1:
startvalues['name'] = filt['from']
startvalues['word'] = ''
else:
startvalues['name'] = ''
startvalues['word'] = filt['from']
else:
startvalues = {'type': '0',
'word': '',
'name': '',
'trip': '',
'where': [],
'action': '0',
'changeto': '',
'reason': _('Forbidden word'),
'seconds': '0',
'blind': '1',
'redirect_url': 'http://',
'redirect_time': '5'}
if edit_id > 0:
submit = "Editar Filtro"
else:
submit = "Agregar filtro"
action_taken = True
template_filename = "filters.html"
template_values = {'mode': 1,
'edit_id': edit_id,
'boards': boardlist(),
'startvalues': startvalues,
'submit': submit}
elif len(path_split) > 4 and path_split[3] == 'delete':
delid = int(path_split[4])
UpdateDb("DELETE FROM `filters` WHERE id = '%s' LIMIT 1", str(delid))
message = _('Deleted filter %s.') % str(delid)
template_filename = "message.html"
action_taken = True
if not action_taken:
filters = FetchAll("SELECT * FROM `filters` ORDER BY `added` DESC")
for filter in filters:
if not filter['boards']:
filter['boards'] = _('All boards')
else:
where = str2boards(filter['boards'])
filter['boards'] = '/' + '/, /'.join(where) + '/'
if filter['type'] == 0:
filter['type_formatted'] = _('Word:') + ' ' + html.escape(filter['from']) + ''
elif filter['type'] == 1:
filter['type_formatted'] = _('Name/Tripcode:')+' '
if filter['from']:
filter['type_formatted'] += '' + \
filter['from'] + ''
if filter['from_trip'] != '':
filter['type_formatted'] += '' + \
filter['from_trip'] + ''
else:
filter['type_formatted'] = '?'
if filter['action'] == 0:
filter['action_formatted'] = _('Abort post')
elif filter['action'] == 1:
filter['action_formatted'] = _(
'Change to:') + ' ' + html.escape(filter['to']) + ''
elif filter['action'] == 2:
if filter['blind'] == 1:
blind = _('Yes')
else:
blind = _('No')
filter['action_formatted'] = _('Autoban:') + '
' + \
(_('Length:')+' %s
'+_('Blind:') +
' %s') % (filter['seconds'], blind)
elif filter['action'] == 3:
filter['action_formatted'] = (_('Redirect to:')+' %s ('+_('in %s secs')+')') % (
filter['redirect_url'], filter['redirect_time'])
else:
filter['action_formatted'] = '?'
filter['added'] = formatTimestamp(filter['added'])
template_filename = "filters.html"
template_values = {'mode': 0, 'filters': filters}
elif path_split[2] == 'logs':
if staff_account['rights'] not in [0, 2]:
return
logs = FetchAll(
'SELECT * FROM `logs` ORDER BY `timestamp` DESC')
for log in logs:
log['timestamp_formatted'] = formatTimestamp(
log['timestamp'])
template_filename = "logs.html"
template_values = {'logs': logs}
elif path_split[2] == 'logout':
message = _('Logging out...') + ''
deleteCookie(self, 'weabot_manage')
deleteSession(staff_account['session_id'])
template_filename = "message.html"
elif path_split[2] == 'quotes':
# Quotes for the post screen
if "save" in self.formdata:
try:
f = open('quotes.conf', 'w')
f.write(self.formdata["data"])
f.close()
message = 'Datos guardados.'
template_filename = "message.html"
except:
message = 'Error al guardar datos.'
template_filename = "message.html"
try:
f = open('quotes.conf', 'r')
data = html.escape(f.read(1048576), True)
f.close()
template_filename = "quotes.html"
template_values = {'data': data}
except:
message = 'Error al leer datos.'
template_filename = 'message.html'
elif path_split[2] == 'recent_images':
try:
if int(self.formdata['images']) > 256:
images = '256'
else:
images = self.formdata['images']
posts = FetchAll('SELECT * FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE CHAR_LENGTH(`thumb`) > 0 ORDER BY `timestamp` DESC LIMIT %s' % (images) )
except:
posts = FetchAll('SELECT * FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE CHAR_LENGTH(`thumb`) > 0 ORDER BY `timestamp` DESC LIMIT 32')
template_filename = "recent_images.html"
template_values = {'posts': posts}
elif path_split[2] == 'news':
if not administrator:
return
type = 1
if 'type' in self.formdata:
type = int(self.formdata['type'])
if type > 2:
raise UserError("Tipo no soportado")
# canal del home
if len(path_split) > 3:
if path_split[3] == 'add':
t = datetime.datetime.now()
# Insertar el nuevo post
title = ''
message = self.formdata["message"].replace(
"\n", "
")
# Titulo
if 'title' in self.formdata:
title = self.formdata["title"]
# Post anonimo
if 'anonymous' in self.formdata and self.formdata['anonymous'] == '1':
to_name = "Staff ★"
else:
to_name = "%s ★" % staff_account['username']
timestamp_formatted = formatDate(t)
if type > 0:
timestamp_formatted = re.sub(r"\(.+", "", timestamp_formatted)
else:
timestamp_formatted = re.sub(r"\(...\)", " ", 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()
message = _("Added successfully.")
template_filename = "message.html"
if path_split[3] == 'delete':
# Eliminar un post
id = int(path_split[4])
UpdateDb("DELETE FROM `news` WHERE id = %d AND type = %d" % (id, type))
regenerateNews()
regenerateHome()
message = _("Deleted successfully.")
template_filename = "message.html"
else:
posts = FetchAll(
"SELECT * FROM `news` WHERE type = %d ORDER BY `timestamp` DESC" % type)
template_filename = "news.html"
template_values = {'action': type, 'posts': posts}
elif path_split[2] == 'newschannel':
# if not administrator:
# return
if len(path_split) > 3:
if path_split[3] == 'add':
t = datetime.datetime.now()
# Delete old posts
#posts = FetchAll("SELECT `id` FROM `news` WHERE `type` = '1' ORDER BY `timestamp` DESC LIMIT "+str(Settings.MODNEWS_MAX_POSTS)+",30")
# for post in posts:
# UpdateDb("DELETE FROM `news` WHERE id = " + post['id'] + " AND `type` = '0'")
# Insert new post
message = ''
try:
# Cut long lines
message = self.formdata["message"]
message = clickableURLs(html.escape(message).rstrip()[0:8000])
message = onlyAllowedHTML(message)
if Settings.USE_MARKDOWN:
message = markdown(message)
if not Settings.USE_MARKDOWN:
message = message.replace("\n", "
")
except:
pass
# If it's preferred to remain anonymous...
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, %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"
if path_split[3] == 'delete':
if not administrator:
# We check that if he's not admin, he shouldn't be able to delete other people's posts
post = FetchOne("SELECT `staffid` FROM `news` WHERE id = %s AND type = '0'" % (int(path_split[4])) )
if post['staffid'] != staff_account['id']:
self.error(_('That post is not yours.'))
return
# Delete!
UpdateDb("DELETE FROM `news` WHERE id = %s AND type = '0'", (path_split[4],))
message = _("Deleted successfully.")
template_filename = "message.html"
else:
# If he's not admin, show only his own posts
if administrator:
posts = FetchAll("SELECT * FROM `news` WHERE type = '0' ORDER BY `timestamp` DESC")
else:
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}
elif path_split[2] == 'reports':
if not moderator:
return
message = None
import math
pagesize = Settings.REPORTS_PER_PAGE
totals = FetchOne("SELECT COUNT(id) FROM `reports`")
total = int(totals['COUNT(id)'])
pages = int(math.ceil(total // pagesize))
try:
currentpage = int(path_split[3])
except:
currentpage = 0
if len(path_split) > 4:
if path_split[4] == 'ignore':
# Delete report
UpdateDb("DELETE FROM `reports` WHERE `id` = %s", (path_split[5],))
message = _('Report %s ignored.') % path_split[5]
if 'ignore' in self.formdata:
ignored = 0
if 'board' in self.formdata and self.formdata['board'] != 'all':
reports = FetchAll("SELECT `id` FROM `reports` WHERE `board` = %s ORDER BY `timestamp` DESC LIMIT %s, %s",
(self.formdata['board'], currentpage*pagesize, pagesize))
else:
reports = FetchAll("SELECT `id` FROM `reports` ORDER BY `timestamp` DESC LIMIT %s, %s",
(currentpage*pagesize, pagesize))
for report in reports:
keyname = 'i' + str(report['id'])
if keyname in self.formdata:
# Ignore here
UpdateDb("DELETE FROM `reports` WHERE `id` = %s",
(report['id'],))
ignored += 1
message = _('Ignored %s report(s).') % str(ignored)
# Generate board list
boards = FetchAll(
'SELECT `name`, `dir` FROM `boards` ORDER BY `dir`')
for board in boards:
if 'board' in self.formdata and self.formdata['board'] == board['dir']:
board['checked'] = True
else:
board['checked'] = False
# Tabla
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 %s, %s",
(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 %s, %s",
(currentpage*pagesize, pagesize))
if 'board' in self.formdata:
curboard = self.formdata['board']
else:
curboard = None
# for report in reports:
# if report['parentid'] == '0':
# report['link'] = Settings.BOARDS_URL + report['board'] + '/res/' + report['postid'] + '.html#' + report['postid']
# else:
# report['link'] = Settings.BOARDS_URL + report['board'] + '/res/' + report['parentid'] + '.html#' + report['postid']
navigator = ''
if currentpage > 0:
navigator += '< '
else:
navigator += '< '
for i in range(pages):
if i != currentpage:
navigator += ''+str(i)+' '
else:
navigator += str(i)+' '
if currentpage < (pages-1):
navigator += '> '
else:
navigator += '> '
template_filename = "reports.html"
template_values = {'message': message,
'boards': boards,
'reports': reports,
'currentpage': currentpage,
'curboard': curboard,
'navigator': navigator}
# Show by IP
elif path_split[2] == 'ipshow':
if not moderator:
return
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", (ip,))
template_filename = "ipshow.html"
template_values = {"mode": 1, "ip": ip, "host": getHost(
ip), "country": getCountry(ip), "tor": addressIsTor(ip), "posts": posts}
logAction(staff_account['username'],
"ipshow on {}".format(ip))
else:
# Generate form
template_filename = "ipshow.html"
template_values = {"mode": 0}
elif path_split[2] == 'ipdelete':
if not moderator:
return
# Delete posts by IP
if 'ip' in self.formdata:
# If an IP was given...
if self.formdata['ip'] != '':
where = []
if 'board_all' not in self.formdata:
# If multiple boards, 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:
if self.formdata[keyname] == "1":
where.append(board)
else:
# If all boards were selected, add them all to the list
where = FetchAll('SELECT `id`, `dir` FROM `boards`')
# If no board was chosen
if len(where) <= 0:
self.error(_("Select a board first."))
return
try:
secs = int(self.formdata['seconds'])
except:
secs = 0
if secs > 0:
since = round(time.time() - secs)
deletedPostsTotal = 0
ip = self.formdata['ip']
deletedPosts = 0
for theboard in where:
board = setBoard(theboard['dir'])
isDeletedOP = False
# delete all starting posts first
if secs == 0:
op_posts = FetchAll("SELECT `id`, `message` FROM posts WHERE parentid = 0 AND boardid = %s AND ip = INET6_ATON(%s)", (board['id'], ip) )
else:
op_posts = FetchAll("SELECT `id`, `message` FROM posts WHERE parentid = 0 AND boardid = %s AND ip = INET6_ATON(%s) AND timestamp > %s", (board['id'], ip, since) )
for post in op_posts:
deletePost(post['id'], None)
deletedPosts += 1
deletedPostsTotal += 1
if secs == 0:
replies = FetchAll("SELECT `id`, `message`, `parentid` FROM posts WHERE parentid != 0 AND boardid = %s AND ip = INET6_ATON(%s)", (board['id'], ip) )
else:
replies = FetchAll("SELECT `id`, `message`, `parentid` FROM posts WHERE parentid != 0 AND boardid = %s AND ip = INET6_ATON(%s) AND timestamp > %s", (board['id'], ip, since) )
for post in replies:
deletePost(post['id'], None, 2)
deletedPosts += 1
deletedPostsTotal += 1
regenerateHome()
else:
self.error(_("Please enter an IP first."))
return
if deletedPosts > 0:
message = 'En total se eliminaron %(posts)s post(s) de %(ip)s.' % {'posts': str(deletedPosts), 'ip': self.formdata['ip']}
logAction(staff_account['username'], '%(posts)s post(s) eliminado(s) de IP: %(ip)s' % {'posts': str(deletedPosts), 'ip': self.formdata['ip']})
#logAction(staff_account['username'], '%(posts)s post(s) were deleted from %(board)s. IP: %(ip)s' % \
# {'posts': str(deletedPosts),
# 'board': '/' + board['dir'] + '/',
# 'ip': self.formdata['ip']})
else:
message = "No se encontraron posts"
template_filename = "message.html"
else:
# Show form
template_filename = "ipdelete.html"
template_values = {'boards': boardlist()}
elif path_split[2] == 'goto':
board = setBoard(path_split[3])
try:
pid = int(path_split[4])
except ValueError:
raise UserError("ID no válida.")
if board['board_type'] == 1:
first = get_parent_post(pid, board['id'])
url = "/%s/read/%s/" % (board['dir'], first['timestamp'])
else:
url = "/%s/res/%s.html" % (board['dir'], pid)
self.output = '' % url
elif path_split[2] == 'fixquote':
# when a mod deletes a post permanently messing with quote numbers
if not administrator:
return
board = setBoard(path_split[3])
if board['board_type'] == 1:
return
pid = int(path_split[4])
parent = FetchOne("SELECT `parentid` FROM `posts` \
WHERE `id` = %s AND `boardid` = %s LIMIT 1" % (pid, board['id']))
parent = parent['parentid']
ranges = '`id` >= ' + str(pid)
if len(path_split) > 5:
ranges += ' AND `id` <= ' + path_split[5]
posts = FetchAll('SELECT `id`, `message` FROM `posts` \
WHERE `boardid` = %s AND %s AND `parentid` = %s AND \
`message` LIKE \'%%">>>%%\'' % (board['id'], ranges, parent))
def fix_num(capture):
qnum = str(int(capture.group(2)) - 1)
return '/'+board['dir']+'/read/' + capture.group(1) + '/' + qnum + '">>>' + qnum
for post in posts:
rx = r'/'+board['dir']+'/read/(\d+)/(\d+)">>>(\d+)'
new_msg = re.sub(rx, fix_num, post['message'])
self.output += post['message'] + '