# coding=utf-8
import _mysql
import os
import cgi
import shutil
import imaplib
import poplib
import datetime
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", 0)
if not first_admin:
InsertDb("INSERT INTO `staff` (`username`, `password`, `added`, `rights`) VALUES ('admin', '" +
_mysql.escape_string(genPasswdHash("admin")) + "', 0, 0)")
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() - 604800)) # one week
else:
page += _('Incorrect username/password.')
logAction('', 'Failed log-in. U:'+_mysql.escape_string(
self.formdata['username'])+' IP:'+self.environ["REMOTE_ADDR"])
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` = ' + str(timestamp()
) + ' WHERE `id` = ' + staff_account['id'] + ' LIMIT 1')
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 == '!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 self.formdata.get("thread"):
parentid = int(self.formdata["thread"])
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']))
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, 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':
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` = ' + _mysql.escape_string(path_split[4]) + ' LIMIT 1')
if member:
member_username = member['username']
member_rights = member['rights']
action = 'edit/' + 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` = '" + _mysql.escape_string(
self.formdata['user']) + "', `rights` = " + self.formdata['rights'] + " WHERE `id` = " + member['id'] + " LIMIT 1")
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'
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')
if not username_taken:
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'] + ")")
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"
except:
pass
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
try:
action_taken = True
member = FetchOne(
'SELECT `username` FROM `staff` WHERE `id` = ' + _mysql.escape_string(path_split[4]) + ' LIMIT 1')
if member:
UpdateDb('DELETE FROM `staff` WHERE `id` = ' +
_mysql.escape_string(path_split[4]) + ' LIMIT 1')
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"
except:
pass
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'] != '0':
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
template_filename = "delete.html"
template_values = {
'do_ban': do_ban, 'curboard': path_split[3], 'postid': path_split[4]}
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)
else:
deletePost(path_split[4], None, '0', imageonly)
regenerateHome()
# Borrar denuncias
UpdateDb("DELETE FROM `reports` WHERE `postid` = '" +
_mysql.escape_string(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])
post = FetchOne('SELECT `parentid`, `locked` FROM `posts` WHERE `boardid` = ' +
board['id'] + ' AND `id` = \'' + _mysql.escape_string(path_split[4]) + '\' LIMIT 1')
if not post:
message = _('Unable to locate a post with that ID.')
template_filename = "message.html"
else:
if post['parentid'] != '0':
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` = %d WHERE `boardid` = '%s' AND `id` = '%s' LIMIT 1" % (
setLocked, board["id"], _mysql.escape_string(path_split[4])))
threadUpdated(path_split[4])
if setLocked == 1:
message = _('Thread successfully closed.')
logAction(staff_account['username'], _('Closed thread %s') % (
'/' + path_split[3] + '/' + path_split[4]))
else:
message = _('Thread successfully opened.')
logAction(staff_account['username'], _('Opened thread %s') % (
'/' + path_split[3] + '/' + path_split[4]))
template_filename = "message.html"
elif path_split[2] == 'permasage':
setPermasaged = 0
# Nos vamos al board y ubicamos el post
board = setBoard(path_split[3])
post = FetchOne('SELECT `parentid`, `locked` FROM `posts` WHERE `boardid` = ' +
board['id'] + ' AND `id` = \'' + _mysql.escape_string(path_split[4]) + '\' LIMIT 1')
if not post:
message = 'Unable to locate a post with that 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'] != '0':
message = 'Post is not a thread opener.'
template_filename = "message.html"
else:
if post['locked'] == '2':
# Sacar permasage
setPermasaged = 0
else:
# Colocar permasage
setPermasaged = 2
UpdateDb("UPDATE `posts` SET `locked` = %d WHERE `boardid` = '%s' AND `id` = '%s' LIMIT 1" % (
setPermasaged, board["id"], _mysql.escape_string(path_split[4])))
regenerateFrontPages()
threadUpdated(path_split[4])
if setPermasaged == 2:
message = 'Thread successfully permasaged.'
logAction(
staff_account['username'], 'Enabled permasage in thread /' + path_split[3] + '/' + path_split[4])
else:
message = 'Thread successfully un-permasaged.'
logAction(
staff_account['username'], 'Disabled permasage in thread /' + path_split[3] + '/' + path_split[4])
template_filename = "message.html"
elif path_split[2] == 'move':
raise NotImplementedError
if not moderator:
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(inet_ntoa(long(
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.iteritems():
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` = ' +
board['id'] + ' AND `id` = \'' + _mysql.escape_string(path_split[4]) + '\' LIMIT 1')
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:
ipnetwork = netaddr.IPNetwork(ip)
ipstart, ipend = str(ipnetwork[0]), str(ipnetwork[-1])
ipstr = str(ipnetwork)
except netaddr.core.AddrFormatError:
# Invalid format so try with globs
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)
if self.formdata['seconds'] != '0':
until = str(
timestamp() + int(self.formdata['seconds']))
else:
until = '0'
where = ''
if 'board_all' not in self.formdata.keys():
where = []
boards = FetchAll('SELECT `dir` FROM `boards`')
for board in boards:
keyname = 'board_' + board['dir']
if keyname in self.formdata.keys():
if self.formdata[keyname] == "1":
where.append(board['dir'])
if len(where) > 0:
where = pickle.dumps(where)
else:
self.error(
_("You must select where the ban shall be placed"))
return
if 'edit' in self.formdata.keys():
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('" +
ipstart + "'), INET6_ATON('" + ipend + "'), '" + ipstr + "', '" +
_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():
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.keys():
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")
if ban:
if ban['boards'] == '':
where = ''
else:
where = pickle.loads(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` = '" +
_mysql.escape_string(path_split[4]) + "' LIMIT 1", 0)[0]
if ip != '':
# Delete ban
UpdateDb('DELETE FROM `bans` WHERE `id` = ' +
_mysql.escape_string(path_split[4]) + ' LIMIT 1')
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 = pickle.loads(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'] == '1':
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` = \'' + genPasswdHash(
self.formdata['newpassword']) + '\' WHERE `id` = ' + staff_account['id'] + ' LIMIT 1')
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:
# Update board settings
board['name'] = self.formdata['name']
board['longname'] = self.formdata['longname']
board['subname'] = self.formdata['subname']
board['anonymous'] = self.formdata['anonymous']
board['subject'] = self.formdata['subject']
board['message'] = self.formdata['message']
switchBoard(self.formdata['type'])
board['board_type'] = self.formdata['type']
board['useid'] = self.formdata['useid']
board['slip'] = self.formdata['slip']
board['countrycode'] = self.formdata['countrycode']
if 'recyclebin' in self.formdata.keys():
board['recyclebin'] = '1'
else:
board['recyclebin'] = '0'
if 'disable_name' in self.formdata.keys():
board['disable_name'] = '1'
else:
board['disable_name'] = '0'
if 'disable_subject' in self.formdata.keys():
board['disable_subject'] = '1'
else:
board['disable_subject'] = '0'
if 'secret' in self.formdata.keys():
board['secret'] = '1'
else:
board['secret'] = '0'
if 'locked' in self.formdata.keys():
board['locked'] = '1'
else:
board['locked'] = '0'
board['postarea_desc'] = self.formdata['postarea_desc']
if 'allow_noimage' in self.formdata.keys():
board['allow_noimage'] = '1'
else:
board['allow_noimage'] = '0'
if 'allow_images' in self.formdata.keys():
board['allow_images'] = '1'
else:
board['allow_images'] = '0'
if 'allow_image_replies' in self.formdata.keys():
board['allow_image_replies'] = '1'
else:
board['allow_image_replies'] = '0'
if 'allow_spoilers' in self.formdata.keys():
board['allow_spoilers'] = '1'
else:
board['allow_spoilers'] = '0'
if 'allow_oekaki' in self.formdata.keys():
board['allow_oekaki'] = '1'
else:
board['allow_oekaki'] = '0'
if 'archive' in self.formdata.keys():
board['archive'] = '1'
else:
board['archive'] = '0'
board['postarea_extra'] = self.formdata['postarea_extra']
board['force_css'] = self.formdata['force_css']
# Update file types
UpdateDb(
"DELETE FROM `boards_filetypes` WHERE `boardid` = %s" % board['id'])
for filetype in filetypelist():
if 'filetype'+filetype['ext'] in self.formdata.keys():
UpdateDb("INSERT INTO `boards_filetypes` VALUES (%s, %s)" % (
board['id'], filetype['id']))
try:
board['numthreads'] = int(
self.formdata['numthreads'])
except:
raise UserError, _("Max threads shown must be numeric.")
try:
board['numcont'] = int(self.formdata['numcont'])
except:
raise UserError, _("Max replies shown must be numeric.")
try:
board['numline'] = int(self.formdata['numline'])
except:
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.")
try:
board['maxsize'] = int(self.formdata['maxsize'])
except:
raise UserError, _("Max size must be numeric.")
try:
board['maxage'] = int(self.formdata['maxage'])
except:
raise UserError, _("Max age must be numeric.")
try:
board['maxinactive'] = int(
self.formdata['maxinactive'])
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.")
updateBoardSettings()
message = _('Board options successfully updated.') + ' '+_('Rebuild')+''
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` FROM `posts` WHERE `boardid` = ' +
board['id'] + ' AND `id` = \'' + _mysql.escape_string(path_split[6]) + '\' LIMIT 1')
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` = ' +
board['id'] + ' AND `id` = \'' + _mysql.escape_string(path_split[6]) + '\' LIMIT 1')
if post['parentid'] != '0':
threadUpdated(post['parentid'])
else:
regenerateFrontPages()
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` = ' +
board['id'] + ' AND `id` = \'' + _mysql.escape_string(path_split[6]) + '\' LIMIT 1')
if not post:
message = _(
'Unable to locate a post with that ID.')
else:
deletePost(path_split[6], None)
if post['parentid'] != '0':
threadUpdated(post['parentid'])
else:
regenerateFrontPages()
message = "Eliminado post %s permanentemente." % (
'/' + board['dir'] + '/' + post['id'])
logAction(
staff_account['username'], message + ' Contenido: ' + post['message'] + ' IP: ' + post['ip'])
# Delete more than 1 post
if 'deleteall' in self.formdata.keys():
return # TODO
deleted = 0
for key in self.formdata.keys():
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'] != '0':
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.keys():
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.keys() 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.keys() 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))
try:
totals = FetchOne("SELECT COUNT(id) FROM `posts` WHERE IS_DELETED %s AND `boardid` = %s" % (
_mysql.escape_string(type_condition), _mysql.escape_string(posts[0]['boardid'])), 0)
except:
skip = True
else:
cboard = 'all'
posts = FetchAll("SELECT posts.id, posts.timestamp, timestamp_formatted, IS_DELETED, posts.ip, posts.message, dir FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE IS_DELETED %s ORDER BY `timestamp` DESC LIMIT %d, %d" % (
_mysql.escape_string(type_condition), currentpage*pagesize, pagesize))
totals = FetchOne("SELECT COUNT(id) FROM `posts` WHERE IS_DELETED %s" %
_mysql.escape_string(type_condition), 0)
template_filename = "recyclebin.html"
template_values = {'message': message,
'type': type,
'boards': boards,
'skip': skip}
if not skip:
# Calculate number of pages
total = int(totals[0])
pages = int(math.ceil(total / pagesize))
# Create delete form
if 'board' in self.formdata.keys():
board = self.formdata['board']
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` = \'' + _mysql.escape_string(board_dir) + '\' LIMIT 1')
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 (\'' + _mysql.escape_string(
board_dir) + '\', \'' + _mysql.escape_string(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':
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':
board = setBoard(path_split[3])
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(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':
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':
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':
board = setBoard(path_split[3])
#posts = FetchAll('SELECT * FROM `posts` WHERE `boardid` = %s' % board['id'])
posts = FetchAll(
'SELECT * FROM `posts` WHERE `name` LIKE \'%s\'' % '%%')
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':
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':
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':
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.keys():
edit_id = 0
if 'edit' in self.formdata.keys():
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 = ''
# I don't like pickles... oh well.
where = ''
if 'board_all' not in self.formdata.keys():
where = []
boards = FetchAll('SELECT `dir` FROM `boards`')
for board in boards:
keyname = 'board_' + board['dir']
if keyname in self.formdata.keys():
if self.formdata[keyname] == "1":
where.append(board['dir'])
if len(where) > 0:
where = _mysql.escape_string(
pickle.dumps(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 = _mysql.escape_string(
cgi.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 = _mysql.escape_string(
self.formdata["name"])
can_add = True
if len(self.formdata["trip"]) > 0:
filter_tripcode = _mysql.escape_string(
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 = _mysql.escape_string(
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']))
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']))
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 = _mysql.escape_string(
self.formdata["seconds"])
if "blind" in self.formdata.keys() 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 (%d, '%s', '%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_seconds, filter_blind, str(timestamp()), _mysql.escape_string(staff_account['username']))
elif filter_action == 3:
# Redirect URL
if len(self.formdata['redirect_url']) > 0:
redirect_url = _mysql.escape_string(
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 (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')" % \
(edit_id, where, str(filter_type), str(filter_action), filter_from, filter_tripcode, filter_reason, redirect_url, str(
redirect_time), str(timestamp()), _mysql.escape_string(staff_account['username']))
# DO QUERY!
if edit_id > 0:
UpdateDb(
"DELETE FROM `filters` WHERE `id` = %s" % str(edit_id))
UpdateDb(sql_query)
message = 'Filter edited.'
else:
filt = FetchOne("SELECT `id` FROM `filters` WHERE `boards` = '%s' AND `type` = '%s' AND `from` = '%s'" % (
where, str(filter_type), filter_from))
if not filt:
UpdateDb(sql_query)
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.keys() 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 filt['boards'] == '':
where = ''
else:
where = pickle.loads(filt['boards'])
startvalues = {'type': filt['type'],
'trip': filt['from_trip'],
'where': where,
'action': filt['action'],
'changeto': cgi.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 filter['boards'] == '':
filter['boards'] = _('All boards')
else:
where = pickle.loads(filter['boards'])
if len(where) > 1:
filter['boards'] = '/' + \
'/, /'.join(where) + '/'
else:
filter['boards'] = '/' + where[0] + '/'
if filter['type'] == '0':
filter['type_formatted'] = _(
'Word:') + ' ' + cgi.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:') + ' ' + cgi.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'] != '0' and staff_account['rights'] != '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.keys():
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 = cgi.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']) > 100:
images = '100'
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 ' + _mysql.escape_string(images))
except:
posts = FetchAll(
'SELECT * FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE CHAR_LENGTH(`thumb`) > 0 ORDER BY `timestamp` DESC LIMIT 10')
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.keys() 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 (%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))
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(
cgi.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.keys() 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))
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 = '" +
_mysql.escape_string(path_split[4])+"' AND type = '0'")
if post['staffid'] != staff_account['id']:
self.error(_('That post is not yours.'))
return
# Delete!
UpdateDb("DELETE FROM `news` WHERE id = '" +
_mysql.escape_string(path_split[4]) + "' AND type = '0'")
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 = '" +
staff_account['id']+"' AND type = '0' ORDER BY `timestamp` DESC")
template_filename = "news.html"
template_values = {'action': 'newschannel', 'posts': posts}
elif path_split[2] == 'reports':
if not moderator:
return
message = None
import math
pagesize = float(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` = '" +
_mysql.escape_string(path_split[5])+"'")
message = _('Report %s ignored.') % path_split[5]
if 'ignore' in self.formdata.keys():
ignored = 0
if 'board' in self.formdata.keys() 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:
reports = FetchAll("SELECT `id` FROM `reports` ORDER BY `timestamp` DESC LIMIT %d, %d" % (
currentpage*pagesize, pagesize))
for report in reports:
keyname = 'i' + report['id']
if keyname in self.formdata.keys():
# Ignore here
UpdateDb("DELETE FROM `reports` WHERE `id` = '" +
_mysql.escape_string(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.keys() 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':
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():
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.keys():
# 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))
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 by IP
if 'ip' in self.formdata.keys():
# If an IP was given...
if self.formdata['ip'] != '':
where = []
if 'board_all' not in self.formdata.keys():
# 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 self.formdata[keyname] == "1":
where.append(board)
else:
# If all boards were selected="selected", all 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
deletedPostsTotal = 0
ip = self.formdata['ip']
deletedPosts = 0
for theboard in where:
board = setBoard(theboard['dir'])
isDeletedOP = False
# delete all starting posts first
op_posts = FetchAll(
"SELECT `id`, `message` FROM posts WHERE parentid = 0 AND boardid = '" + board['id'] + "' AND ip = INET6_ATON('" + str(ip) + "')")
for post in op_posts:
deletePost(post['id'], None)
deletedPosts += 1
deletedPostsTotal += 1
replies = FetchAll(
"SELECT `id`, `message`, `parentid` FROM posts WHERE parentid != 0 AND boardid = '" + board['id'] + "' AND ip = INET6_ATON('" + str(ip) + "')")
for post in replies:
deletePost(post['id'], None, '2')
deletedPosts += 1
deletedPostsTotal += 1
regenerateHome()
if deletedPosts > 0:
message = '%(posts)s post(s) were deleted from %(board)s.' % {
'posts': str(deletedPosts), 'board': '/' + board['dir'] + '/'}
template_filename = "message.html"
# 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:
self.error(_("Please enter an IP first."))
return
message = 'In total %(posts)s from IP %(ip)s were deleted.' % {
'posts': str(deletedPosts), 'ip': self.formdata['ip']}
logAction(staff_account['username'], message)
template_filename = "message.html"
else:
# Generate form...
template_filename = "ipdelete.html"
template_values = {'boards': boardlist()}
elif path_split[2] == 'search':
if not administrator:
return
search_logs = FetchAll(
'SELECT `id`,`timestamp`,`keyword`,`ita`,INET_NTOA(`ip`) AS `ip`,`res` FROM `search_log` ORDER BY `timestamp` DESC LIMIT 250')
for log in search_logs:
#log['ip'] = str(inet_ntoa(log['ip']))
log['timestamp_formatted'] = formatTimestamp(
log['timestamp'])
if log['keyword'].startswith('k '):
log['keyword'] = log['keyword'][2:]
log['archive'] = True
else:
log['archive'] = False
template_filename = "search.html"
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")
template_filename = "manage.html"
template_values = {'reports': reports, 'posts': posts}
if not skiptemplate:
try:
if template_filename == 'message.html':
template_values = {'message': message}
except:
template_filename = 'message.html'
template_values = {'message': '???'}
template_values.update({
'title': 'Manage',
'validated': validated,
'page': page,
})
if validated:
template_values.update({
'username': staff_account['username'],
'site_title': Settings.SITE_TITLE,
'rights': staff_account['rights'],
'administrator': administrator,
'added': formatTimestamp(staff_account['added']),
})
self.output += renderTemplate("manage/" +
template_filename, template_values)
def switchBoard(new_type):
board = Settings._.BOARD
if new_type == board['board_type']:
return
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':
# 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':
# Switching to Textboard
# Make kako dir
if not os.path.exists(kako_dir):
os.mkdir(kako_dir)
# Clean res dir
cleanDir(res_dir, ext="html")
def newSession(staff_id):
import uuid
session_uuid = uuid.uuid4().hex
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))
return session_uuid
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))
if session:
return session
return None
def renewSession(session_id):
param_session_id = _mysql.escape_string(session_id)
param_expires = timestamp() + Settings.SESSION_TIME
UpdateDb("UPDATE `session` SET expires = %d WHERE session_id = UNHEX('%s')" %
(param_expires, param_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)
def cleanSessions():
param_now = timestamp()
UpdateDb("DELETE FROM `session` WHERE expires <= %d" % param_now)
def logAction(staff, action):
InsertDb("INSERT INTO `logs` (`timestamp`, `staff`, `action`) VALUES (" + str(timestamp()) +
", '" + _mysql.escape_string(staff) + "\', \'" + _mysql.escape_string(action) + "\')")
def genPasswdHash(string):
import argon2
ph = argon2.PasswordHasher()
return ph.hash(string)
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)
if not staff_account:
return None
try:
ph.verify(staff_account['password'], passwd)
except argon2.exceptions.VerifyMismatchError:
return None
except argon2.exceptions.InvalidHash:
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'])
UpdateDb("UPDATE staff SET password = '%s' WHERE id = %s" %
(param_new_hash, staff_account['id']))
return staff_account
def boardlist():
boards = FetchAll('SELECT * FROM `boards` ORDER BY `board_type`, `dir`')
return boards
def filetypelist():
filetypes = FetchAll('SELECT * FROM `filetypes` ORDER BY `ext` ASC')
return filetypes