aboutsummaryrefslogblamecommitdiff
path: root/cgi/manage.py
blob: c4581be9103218ed6dbeb4424ddfc50c400ef142 (plain) (tree)





















                             
                      
  



                                                                                 
                                                                                                                                                                
 
                                                                                      








                                                                                                                           
                                                    




                                                                                        
  









                                                                                                                           






































































































                                                                                                                                                                                                                                                                                  
                                     
                                                                                                                                                                                                                                                                                                                                                                          

                                            



















                                                                                                                           
                                                 

                                                                         



                                                                                                                                                                                                               





                                                          

                                                                                                                                                       


                                                                       
                                                                      
 
                                                                                                                                                                                                                                                                      
                                                        
                                                                                                              
















































































































































































































                                                                                                                                                                                      
                                                                     






































                                                                                                                                                             
 


                                                                      













                                                                                                                                                                                   

                                                
 

                                                                        
 








                                                                                        
                                             










































                                                                                                                                                                                                                                                               
                           
















                                                                                                                                                                             

                                           
            

                                      

                                           

















































































































































































                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
                                                                                   
                                                                             
                                                                                                                                                                  






























                                                                                            

                                                       









































































































































                                                                                                                                                                      
                                                                                                                                                                                                      









                                                                  

                                                                                                                     














































































































































































































































































                                                                                                                                                                                                                                                                                                                                                                                                                  






                                                                                                                                                         































































































































































































































































                                                                                                                                                                                                                                                  

                                                  















































































































































































































































                                                                                                                                                                                                                                                                                                                                                               



                                                       




































































                                                                                                                                                             
                                                       


















































                                                                                                                                                             







                                                                  
                                                                


                             




                                                             


                                    

                 
                               
 













































                                                                                                        


                                                                                                                                                                                     























                                                                                        
                                                               


                                            







                                                                          
# 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, 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] == "recent":
          posts = FetchAll("SELECT posts.id, posts.subject, dir, boards.board_type, parentid, file, thumb, timestamp_formatted, timestamp, posts.message, INET_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 = '<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']
          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<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']

            # 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<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
              parent_time = post['timestamp']
            
            # 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}\">&gt;&gt;{oldpost}</a>".format(oldboard=oldboard, oldthread=oldthread, oldpost=old)
            
            if board['board_type'] == '1':
              new_url = "/{newboard}/read/{newthread}/{newpost}\">&gt;&gt;{newpost}</a>".format(newboard=newboard, newthread=newthread, newpost=new)
            else:
              new_url = "/{newboard}/res/{newthread}.html#{newpost}\">&gt;&gt;{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:
            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 <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()
            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 `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 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.') + ' <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 id, parentid, message, INET_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':
              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)+'&amp;board='+cboard+'">&lt;</a> '
          else:
            navigator += '&lt; '
          
          for i in range(pages):
            if i != currentpage:
              navigator += '<a href="'+Settings.CGI_URL+'manage/recyclebin/'+str(i)+'?type='+str(type)+'&amp;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)+'&amp;board='+cboard+'">&gt;</a> '
          else:
            navigator += '&gt; '
          
          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" />'
        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", "<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)+'">&lt;</a> '
        else:
          navigator += '&lt; '
        
        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)+'">&gt;</a> '
        else:
          navigator += '&gt; '
        
        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))
            if '.' in self.formdata['ip']:
              ip = self.formdata['ip']
            else:
              ip = inet_ntoa(long(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']}
          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