diff options
Diffstat (limited to 'cgi/manage.py')
-rw-r--r-- | cgi/manage.py | 1823 |
1 files changed, 1823 insertions, 0 deletions
diff --git a/cgi/manage.py b/cgi/manage.py new file mode 100644 index 0000000..44731ba --- /dev/null +++ b/cgi/manage.py @@ -0,0 +1,1823 @@ +# 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 + + try: + if self.formdata['username'] and self.formdata['password']: + # 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(genPasswd("admin")) + "', 0, 0)") + + password = genPasswd(self.formdata['password']) + + valid_account = FetchOne("SELECT * FROM `staff` WHERE `username` = '" + _mysql.escape_string(self.formdata['username']) + "' AND `password` = '" + _mysql.escape_string(password) + "' LIMIT 1") + if valid_account: + setCookie(self, 'weabot_manage', self.formdata['username'] + ':' + valid_account['password'], domain='THIS') + 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"]) + except: + pass + + try: + manage_cookie = getCookie(self, 'weabot_manage') + if manage_cookie != '': + username, password = manage_cookie.split(':') + staff_account = FetchOne("SELECT * FROM `staff` WHERE `username` = '" + _mysql.escape_string(username) + "' AND `password` = '" + _mysql.escape_string(password) + "' LIMIT 1") + if staff_account: + validated = True + if staff_account['rights'] == '0' or staff_account['rights'] == '1' or staff_account['rights'] == '2': + administrator = True + if staff_account['rights'] == '2': + moderator = False + UpdateDb('UPDATE `staff` SET `lastactive` = ' + str(timestamp()) + ' WHERE `id` = ' + staff_account['id'] + ' LIMIT 1') + except: + pass + + #validated = True + #moderator = True + #staff_account = {} + #staff_account['username'] = '' + #staff_account['rights'] = '0' + #staff_account['added'] = '0' + + 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, INET_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] == '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['username'] != '': + if self.formdata['rights'] in ['0', '1', '2', '3']: + action_taken = True + if not ':' in self.formdata['username']: + UpdateDb("UPDATE `staff` SET `username` = '" + _mysql.escape_string(self.formdata['username']) + "', `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['username']) + else: + message = _('The character : can not be used in usernames.') + template_filename = "message.html" + except: + pass + else: + action = 'add' + try: + if self.formdata['username'] != '' and self.formdata['password'] != '': + username_taken = FetchOne('SELECT * FROM `staff` WHERE `username` = \'' + _mysql.escape_string(self.formdata['username']) + '\' LIMIT 1') + if not username_taken: + if self.formdata['rights'] in ['0', '1', '2', '3']: + action_taken = True + if not ':' in self.formdata['username']: + password = genPasswd(self.formdata['password']) + + InsertDb("INSERT INTO `staff` (`username`, `password`, `added`, `rights`) VALUES ('" + _mysql.escape_string(self.formdata['username']) + "', '" + _mysql.escape_string(password) + "', " + str(timestamp()) + ", " + self.formdata['rights'] + ")") + message = _('Staff member added.') + logAction(staff_account['username'], 'Added staff account for ' + self.formdata['username']) + else: + message = _('The character : can not be used in usernames.') + + 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 = '<a href="' + Settings.CGI_URL + 'manage/staff/delete_confirmed/' + path_split[4] + '">' + _('Click here to confirm the deletion of that staff member') + '</a>' + 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, INET_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...') + '<meta http-equiv="refresh" content="0;url=' + Settings.CGI_URL + 'manage/ban?ip=' + post['ip'] + '" />' + 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': + 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'] + + # 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<br>" % 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'] + + # 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<br>" % (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 + + # log it + message += "%s -> %s<br>" % (old_id, new_id) + + num += 1 + + # fix anchors + for old, new in refs.iteritems(): + old_url = "/{oldboard}/res/{oldthread}.html#{oldpost}\">>>{oldpost}</a>".format(oldboard=oldboard, oldthread=oldthread, oldpost=old) + + if board['board_type'] == '1': + new_url = "/{newboard}/read/{newthread}/{newpost}\">>>{newpost}</a>".format(newboard=newboard, newthread=newthread, newpost=new) + else: + new_url = "/{newboard}/res/{newthread}.html#{newpost}\">>>{newpost}</a>".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 + "<br>" + 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: + 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 <a href=\"{url}\">/{newboard}/{newthread}</a>.".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() + + # regenerate + regenerateFrontPages() + regenerateThreadPage(newthreadid) + regenerateThreadPage(oldthread) + + 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 `ip` FROM `posts` WHERE `boardid` = ' + board['id'] + ' AND `id` = \'' + _mysql.escape_string(path_split[4]) + '\' LIMIT 1') + formatted_ip = inet_ntoa(long(post['ip'])) + #Creo que esto no deberia ir aqui... -> UpdateDb('UPDATE `posts` SET `banned` = 1 WHERE `boardid` = ' + board['id'] + ' AND `id` = \'' + _mysql.escape_string(path_split[4]) + '\'') + if not post: + message = _('Unable to locate a post with that ID.') + template_filename = "message.html" + else: + message = '<meta http-equiv="refresh" content="0;url=' + Settings.CGI_URL + 'manage/ban?ip=' + formatted_ip + '" />Espere...' + template_filename = "message.html" + else: + #if path_split[3] == '': + try: + ip = self.formdata['ip'] + except: + ip = '' + try: + netmask = insnetmask = self.formdata['netmask'] + if netmask == '255.255.255.255': + insnetmask = '' + except: + netmask = instnetmask = '' + #else: + # ip = path_split[3] + if ip != '': + try: + reason = self.formdata['reason'] + except: + reason = None + if reason is not None: + 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: + 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.') + '<a href="'+Settings.CGI_URL+'manage/ban/' + ip + '?edit=' + ban['id']+'">' + _('Edit') + '</a>') + return + + # Blind mode + if 'blind' in self.formdata.keys() and self.formdata['blind'] == '1': + blind = '1' + else: + blind = '0' + + # Banear sin mensaje + InsertDb("INSERT INTO `bans` (`ip`, `netmask`, `boards`, `added`, `until`, `staff`, `reason`, `note`, `blind`) VALUES (INET_ATON('" + _mysql.escape_string(ip) + "') & INET_ATON('"+_mysql.escape_string(netmask)+"'), INET_ATON('"+_mysql.escape_string(insnetmask)+"'), '" + _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': [], + 'netmask': '255.255.255.255', + '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`, INET_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, + 'netmask': ban['netmask'], + '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': 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 INET_NTOA(`ip`) AS 'ip' 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`, INET_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` 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 genPasswd(self.formdata['oldpassword']) == staff_account['password']: + if self.formdata['newpassword'] == self.formdata['newpassword2']: + UpdateDb('UPDATE `staff` SET `password` = \'' + genPasswd(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'] + if board['dir'] != 'anarkia': + 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.') + ' <a href="'+Settings.CGI_URL+'manage/rebuild/'+board['dir']+'">'+_('Rebuild')+'</a>' + 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.') + '<br />' + 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 `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.') + else: + deletePost(path_split[6], None) + + if post['parentid'] != '0': + threadUpdated(post['parentid']) + else: + regenerateFrontPages() + + message = _('Post successfully permadeleted.') + logAction(staff_account['username'], _('Permadeleted post %s') % ('/' + path_split[5] + '/' + path_split[6])) + + # 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': + dir = key[2:].split('/')[0] # Board where the post is + 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, INET_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 += '<a href="'+Settings.CGI_URL+'manage/recyclebin/'+str(currentpage-1)+'?type='+str(type)+'&board='+cboard+'"><</a> ' + else: + navigator += '< ' + + for i in range(pages): + if i != currentpage: + navigator += '<a href="'+Settings.CGI_URL+'manage/recyclebin/'+str(i)+'?type='+str(type)+'&board='+cboard+'">'+str(i)+'</a> ' + else: + navigator += str(i)+' ' + + if currentpage < (pages-1): + navigator += '<a href="'+Settings.CGI_URL+'manage/recyclebin/'+str(currentpage+1)+'?type='+str(type)+'&board='+cboard+'">></a> ' + 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<br>" % 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 '<img%%'" % board['id']) + for t in threads: + img_src = '<img src="%s" alt="ico" /><br />' % getRandomIco() + newmessage = img_src + t["message"] + #UpdateDb("UPDATE posts SET message = '%s' WHERE boardid = %s AND id = %s" % (_mysql.escape_string(newmessage), board['id'], t['id'])) + + self.output = repr(threads) + return + elif path_split[2] == 'fixkako': + board = setBoard(path_split[3]) + + threads = FetchAll('SELECT * FROM archive WHERE boardid = %s ORDER BY timestamp DESC' % board['id']) + for item in threads: + t = time.time() + self.output += item['timestamp'] + '<br />' + 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) + '<br />' + else: + self.output += 'El hilo no existe.<br />' + 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<br />" % 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 <br />" % (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 + "<br />" + for post in posts: + self.output += "%s<br />" % (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'] + "<br />" + 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:' + ' <a href="'+Settings.CGI_URL+'manage/filters/add?edit='+filt['id']+'">edit</a>' + 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:') + ' <b>' + cgi.escape(filter['from']) + '</b>' + elif filter['type'] == '1': + filter['type_formatted'] = _('Name/Tripcode:')+' ' + if filter['from'] != '': + filter['type_formatted'] += '<b class="name">' + filter['from'] + '</b>' + if filter['from_trip'] != '': + filter['type_formatted'] += '<span class="trip">' + filter['from_trip'] + '</span>' + else: + filter['type_formatted'] = '?' + if filter['action'] == '0': + filter ['action_formatted'] = _('Abort post') + elif filter['action'] == '1': + filter ['action_formatted'] = _('Change to:') + ' <b>' + cgi.escape(filter['to']) + '</b>' + elif filter['action'] == '2': + if filter['blind'] == '1': + blind = _('Yes') + else: + blind = _('No') + filter ['action_formatted'] = _('Autoban:') + '<br />' + \ + (_('Length:')+' <i>%s</i><br />'+_('Blind:')+' <i>%s</i>') % (filter['seconds'], blind) + elif filter['action'] == '3': + 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...') + '<meta http-equiv="refresh" content="0;url=' + Settings.CGI_URL + 'manage" />' + setCookie(self, 'weabot_manage', '', domain='THIS') + setCookie(self, 'weabot_staff', '') + 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", "<br />") + + # 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", "<br />") + 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, INET_NTOA(ip) AS ip, reason, reporterip 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, INET_NTOA(ip) AS ip, reason, reporterip 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 += '<a href="'+Settings.CGI_URL+'manage/reports/'+str(currentpage-1)+'"><</a> ' + else: + navigator += '< ' + + for i in range(pages): + if i != currentpage: + navigator += '<a href="'+Settings.CGI_URL+'manage/reports/'+str(i)+'">'+str(i)+'</a> ' + else: + navigator += str(i)+' ' + + if currentpage < (pages-1): + navigator += '<a href="'+Settings.CGI_URL+'manage/reports/'+str(currentpage+1)+'">></a> ' + 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'] != '': + formatted_ip = str(inet_aton(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 = '%s' ORDER BY posts.timestamp DESC" % _mysql.escape_string(formatted_ip)) + ip = self.formdata['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 = inet_aton(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 = " + 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 = " + 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']} + 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 logAction(staff, action): + InsertDb("INSERT INTO `logs` (`timestamp`, `staff`, `action`) VALUES (" + str(timestamp()) + ", '" + _mysql.escape_string(staff) + "\', \'" + _mysql.escape_string(action) + "\')") + +def genPasswd(string): + return getMD5(string + Settings.SECRET) + +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 |