aboutsummaryrefslogblamecommitdiff
path: root/cgi/manage.py
blob: ca1ace6aaf980fda26d0835ab34659fe23338303 (plain) (tree)
1
2
3
4
5
6
7
              

          
           
             
               
              







                             
 
                             








                                                                   
                                                                                  
                           
                                                                                                                                             
 
                                                                                          


                                                          
                                                                                                            

                                                     

                                                                                                                   








                                                                                                
                     



                                                     
                                                
                                
                                        
                             

                                                                                



                                        
         
































































                                                                                         






                                                                                    












































                                                                                                  

                                                 
                                                    

                                                                                                                                          

                                                       
                                                                                                                                                                                                                                                                                            
                                                  
                                                                                      
                     
                                                                                                                                        
                                                  
                                                                                          
                                           
                                                                                                                                                                                                                                                                                                                                                                                                                                           


                                                  
                                                






                                                                         
                                         

                                                   
                                                 
                                                                                                                


                                                                  
                                                                  


                                                             
                                                                             

                                                       

                                                                                                                      






                                                                                                

                                          




                                                                                                                                    
                                                           








                                                                                                                                           
                                                                          




                                                                           


















                                                                                  
 
                                           
                                                                                                       
                                                                                                                      
                                                          



                                                             











                                                                                                                     



                                                                               
                                                 
                                                                       
                                                   
                                                                 
                                                   
                                                             
                                                   
                                                             
                                                




                                                                            
                                                         










                                                                 
                        



                                                                                                                                                             

                                                 


                                                     









                                                          
                                                                                                                                                       

                                   
                                                                 
                                     
                     
                                                                 


                                  

                                                                     
                                                                                      



                                                                     
                                                          




                                                                          
                                                  

                                                                                  
 









                                                                                                                
                                           
                                                                                                                                           



                                                                        
                                            


                                                                   
                                               





                                                    
                                                                                                               
                                                                 
                                          

                                                                  
                                                                                                                       

                                                                  
                                                                                                                       





                                                       

                                                                                                                                           
                            
                                                                   



                                                                                   
                     
                                        
                                                                                

                                                          
                                               





                                               
                                                                                                                                                        
                                              
                                          


                                                                   
                                                                                                                           

                                                                      
                                                                                                                              

                                                      

                                         

                                     













































































                                                                                                                                                          
                                                                                        




                                                       
                                                                                                                                                                                
























                                                                                                   
                                                 




































                                                                                                                                                                                                 

                                                                                                                                                                                                                                                       







                                                      
                                                                                                                      
                             
                                                                                                                          





















                                                                                                                                  
                                                                                                                                   
                     








                                                                        
                                                                                                                                                                   
                    




                                                                                  
                                                                                              
                                                          
                     



                                                        
                                                
 

                                               
















                                                                                      
                                 

                                                                   
                                                                 

                                                                                            
 
                                                           
                                                                               
                             
                                     
                                  
                                                            



                                                                           
                                                            


                                                                     
                                                         
                                 



                                                                                       
                                                   
                                                                                                                                     








                                                                                                                                
                                                             



                                                                                  


                                                                                                                                                                      

                                          
                                                   

                                                                   
                             

                                                                   
                                          










                                                                    

                                                    
                                   
                                                   
                                                           
                                                                                                                                                                                                                                                                                                  


                                                       
                                     
                                                                     


















                                                                           






                                                 


                                                                                         
                                        

                                                                                  











                                                                                                                                       
                                                                                                                                           




                                                               
                                                                 





                                                                        
                                                 


                                                                            
                                            














                                                                                                                                         

                                                                                                   








                                                                                                  
                     


























                                                                           
                                                         
                                                   
                             
                                                   
                                                           
                                                     
                             
                                                     
                                                              
                                                        
                             
                                                        
                                                     
                                               
                             
                                               
                                                     
                                               
                             
                                               
                                                                               
                                                            
                                                      
                             
                                                      
                                                           
                                                     
                             
                                                     
                                                                  
                                                            
                             
                                                            
                                                             
                                                       
                             
                                                       
                                                           
                                                     
                             
                                                     
                                                      
                                                
                             
                                                






                                                                                                
                                                                           



                                                                                             
                                                                                  
                               
                                                                                    



                                                                            
                                                                                    



                                                                            
                                                                                  



                                                                              
                                                                                       



                                                                            
                                                                           



                                                                          
                                                                          

                            
                                                                                    
                               
                                                                                 

                            
                                                                                  
                               
                                                                                           



                                                                              
                                                                                       











                                                                                                    
                     











                                                                        
                                                                                                                                                                
                                    
                                                                                           

                                                              
                                                                                                                                                    
                                                     



                                                               


                                                       
                                                                      
                                                                                                                                      


                                                       

                                                                                                                                                     
                                    
                                                                                
                             
                                                        
 
                                                



                                                               
                                                                                                                        
                                                                                                                                                   

                                         
                                                

                                  
                                             





                                                                            
                                                                                                                                                                                                                                                         
                                        
                                                                                    



                                                             
                                                                 
                                     
                                                        


                                             
                                                                                                      











                                                                          
                                           




                                                     
                                                                                      
                                    
                                                                                           










                                                     
                                                                                

                                                                                                                                                                                                                                                                                                                                                                 
                        
                                                                                                                                                                           



                                   
                                                                                                                                                                                                                                                                                                                               
                                                                                                                    








                                                      
                                           


                                                            

                                                                                    







































































                                                                                              
                                                                                                            









                                                                                                                          

                                                                                            


























                                                                                    
                


                                               
                      
                                               


                                     














                                                                                                                                                                                   
                      


                                             
                


                                                                                                                             
                


                                                          
                      
                                           


                                     
                                               


                                              
 
                                                                                                                                         





                                                                                                                                                          
                      
                                            


                                     





                                                                                                     
                                                                    







                                                        
                                                                                            












                                                                                                                              


                                     





















                                                                                                                                                                                            
                      
                                          


                                     







                                                                               
                                           
                                                             

                                                          






                                                                                                                  
                      

                                                                                             

                                                                                                






                                                                                                     
                      
                                           


                                     









                                                                                                    


                                     










                                                                                                


                                     







                                                                  
                                              
                                   
                                                   








                                                                    
                                  
                                                            



                                                                           
                                                            


                                                                     
                                                         







                                                                                           
                                                                                






                                                                       
                                                                   

                                                              
                                                                       









                                                                                  
                                                                   

                                              

                                                                                                                                                                                               


                                                                  
                                                                     

                                                                                                                                                                                                             





                                                                             
                                              
                                                                 


                                                                                        
                                 
                                                
 


                                                                                                                                                                                                                           


                                                                      
                                                                            

                                                 
                                                                                       






                                                                              

                                                                                                                                                                                                                                        

                                       

                                                                                         

                                                      
                                                                                                                                                                
                                        
                                                               









                                                                                               
                                                                                      



                                                                                                 
                                                  

                                          
                                                                  



                                                                     
                                                                                     













































                                                                                     
                                                                                       
                                          
                                                

                                                              

                                                                             


                                                                                                                 
                                                                              
                                              






                                                                                     
                                                 
                                                                        
                                                   
                                                           


                                                                                           





                                                                                     
                                                   








                                                                                                            
                                                         
















                                                                                                
                                           










                                                           
                                                             







                                                      

                                                          

                                                        
                                                                                                                                                                                      
                       
                                                                                                                                                                          










                                                        
                                                        















                                                                   
                                                                                              




                                                                          
                                                                                          
                             
                                                                                              
 

                                                                                                                                                                                







                                                          
                                                                                               

























                                                                                                                                                              
                                                                                          








                                                                         
                                                                                              




                                                                          

                                                                                                                                                                               





                                                                                                                  
                                                                                                                                




                                                                        
                                                                                                     




                                                                
                                                                                                           
                         

                                                                                                                          








                                                                               
                                                    

                                                                    
                                                         








                                                    
                                                                                           
                                                                         
                                             
                               
                                                                                    

                                                                                                                                  
                         

                                                                                                               

                                          
                                                         
                                                    
                                         

                                                                             







                                                                        
                                                                                           




                                                
                                                                                

                                                                                                                                                                                                                                                     
                     

                                                                                                                                                                                                                                  
 
                                            









































                                                                                                                                        
                                         

                                                 
                                                
                                                                                                                                                                                                                                                   












                                                                                                    
                                    
                                         


                                                 
                                                            

                                                                                 

                                                                 
                                                            


                                                                     

                                                                                   





                                                                  






                                                                
                                             
                                                





                                                             




                                                                                                                                                                                                  

                                                            


                                                      




                                                                                                                                                                                                              

                                                                 



                                                      



                                                                  









                                                                                                                                                                      

                                                      
                               

                                                             






                                                
                                                   











































                                                                                                                                       



                                           
                                                                                                                                                  

                                                          
                                                                                  






                                                           
             
                        

                                                                                             



                                                                  
            

                                                      
               

















                                                                 
                                                                                     

 
                          







                                                                    
                     



                                                                 
                       







                                        
 
                         

                                   
                                                 
 

                                                                                                      


                       

                                

                   


                                                                                           

                                         





                      

                             
                                                 
 
                                                                                                     
 

                              
                                                                                       

 
                    
                                                                         
 
 
                             
                                                                                                                     
 
 
                          




                                

                                   


                                
                                                                                    







                                                    
                                                                                          







                                                                   

                



                                                                            
                   

                                                                        
# coding=utf-8
import os
import cgi
import html
import shutil
import datetime
import logging

from database import *
from settings import Settings
from framework import *
from formatting import *
from template import *
from post import *


def manage(self, path_split):
    page = ''
    validated = False
    administrator = False
    moderator = True
    skiptemplate = False
    staff_account = None

    if 'username' in self.formdata and 'password' in self.formdata:
        # If no admin accounts available, create admin:admin
        first_admin = FetchOne("SELECT 1 FROM `staff` WHERE `rights` = 0 LIMIT 1")
        if not first_admin:
            InsertDb("INSERT INTO `staff` (`username`, `password`, `added`, `rights`) VALUES ('admin', %s, 0, 0)", (genPasswdHash("admin"),))

        staff_account = verifyPasswd(self.formdata['username'], self.formdata['password'])
        if staff_account:
            session_uuid = newSession(staff_account['id'])
            setCookie(self, 'weabot_manage', session_uuid)
            UpdateDb("DELETE FROM `logs` WHERE `timestamp` < %s", (timestamp() - Settings.MANAGE_LOG_TIME,))
        else:
            page += _('Incorrect username/password.')
            logAction('', 'Failed login. U:'+self.formdata['username']+' IP logged.')
            logging.warn("Failed login. U:{} IP:{}".format(self.formdata['username'], self.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` = %s WHERE `id` = %s LIMIT 1',
                (timestamp(), staff_account['id']))

    if not validated:
        template_filename = "login.html"
        template_values = {}
    else:
        if len(path_split) > 2:
            if path_split[2] == 'rebuild':
                if not administrator:
                    return

                try:
                    board_dir = path_split[3]
                except:
                    board_dir = ''

                if board_dir == '':
                    template_filename = "rebuild.html"
                    template_values = {'boards': boardlist()}
                else:
                    everything = ("everything" in self.formdata)
                    if board_dir == '!ALL':
                        t1 = time.time()
                        boards = FetchAll(
                            'SELECT `dir` FROM `boards` WHERE secret = 0')
                        for board in boards:
                            board = setBoard(board['dir'])
                            regenerateBoard(everything)

                        message = _('Rebuilt %(board)s in %(time)s seconds.') % {
                            'board': _('all boards'), 'time': timeTaken(t1, time.time())}
                        logAction(staff_account['username'], _(
                            'Rebuilt %s') % _('all boards'))
                    elif board_dir == '!BBS':
                        t1 = time.time()
                        boards = FetchAll(
                            'SELECT `dir` FROM `boards` WHERE `board_type` = 1')
                        for board in boards:
                            board = setBoard(board['dir'])
                            regenerateBoard(everything)

                        message = _('Rebuilt %(board)s in %(time)s seconds.') % {
                            'board': _('all boards'), 'time': timeTaken(t1, time.time())}
                        logAction(staff_account['username'], _(
                            'Rebuilt %s') % _('all boards'))
                    elif board_dir == '!IB':
                        t1 = time.time()
                        boards = FetchAll(
                            'SELECT `dir` FROM `boards` WHERE `board_type` = 1')
                        for board in boards:
                            board = setBoard(board['dir'])
                            regenerateBoard(everything)

                        message = _('Rebuilt %(board)s in %(time)s seconds.') % {
                            'board': _('all boards'), 'time': timeTaken(t1, time.time())}
                        logAction(staff_account['username'], _(
                            'Rebuilt %s') % _('all boards'))
                    elif board_dir == '!HOME':
                        t1 = time.time()
                        regenerateHome()
                        message = _('Rebuilt %(board)s in %(time)s seconds.') % {
                            'board': _('home'), 'time': timeTaken(t1, time.time())}
                        logAction(staff_account['username'], _(
                            'Rebuilt %s') % _('home'))
                    elif board_dir == '!NEWS':
                        t1 = time.time()
                        regenerateNews()
                        message = _('Rebuilt %(board)s in %(time)s seconds.') % {
                            'board': _('news'), 'time': timeTaken(t1, time.time())}
                        logAction(staff_account['username'], _(
                            'Rebuilt %s') % _('news'))
                    elif board_dir == '!TRASH':
                        t1 = time.time()
                        regenerateTrash()
                        message = _('Rebuilt %(board)s in %(time)s seconds.') % {
                            'board': _('trash'), 'time': timeTaken(t1, time.time())}
                        logAction(staff_account['username'], _(
                            'Rebuilt %s') % _('trash'))
                    elif board_dir == '!KAKO':
                        t1 = time.time()
                        boards = FetchAll(
                            'SELECT `dir` FROM `boards` WHERE archive = 1')
                        for board in boards:
                            board = setBoard(board['dir'])
                            regenerateKako()

                        message = _('Rebuilt %(board)s in %(time)s seconds.') % {
                            'board': 'kako', 'time': timeTaken(t1, time.time())}
                        logAction(staff_account['username'], _(
                            'Rebuilt %s') % 'kako')
                    elif board_dir == '!HTACCESS':
                        t1 = time.time()
                        if regenerateAccess():
                            message = _('Rebuilt %(board)s in %(time)s seconds.') % {
                                'board': _('htaccess'), 'time': timeTaken(t1, time.time())}
                            logAction(staff_account['username'], _(
                                'Rebuilt %s') % _('htaccess'))
                        else:
                            message = _(
                                'htaccess regeneration deactivated by sysop.')
                    else:
                        t1 = time.time()
                        board = setBoard(board_dir)
                        regenerateBoard(everything)

                        message = _('Rebuilt %(board)s in %(time)s seconds.') % {
                            'board': '/' + board['dir'] + '/', 'time': timeTaken(t1, time.time())}
                        logAction(staff_account['username'],
                                  'Rebuilt /' + board['dir'] + '/')

                    template_filename = "message.html"
            elif path_split[2] == 'mod':
                if not moderator:
                    return

                try:
                    board = setBoard(path_split[3])
                except:
                    board = ""

                if not board:
                    template_filename = "mod.html"
                    template_values = {"mode": 1, 'boards': boardlist()}
                elif len(path_split) > 4:
                    parentid = int(path_split[4])
                    # make sure it's the full thread
                    check = FetchOne("SELECT `parentid` FROM `posts` WHERE `id` = %s AND `boardid` = %s LIMIT 1", (parentid, board['id']))
                    if check['parentid']:
                      parentid = int(check['parentid'])
                    
                    posts = FetchAll('SELECT id, timestamp, timestamp_formatted, name, message, file, thumb, IS_DELETED, locked, subject, length, INET6_NTOA(ip) AS ip FROM `posts` WHERE (parentid = %s OR id = %s) AND boardid = %s ORDER BY `id` ASC', (parentid, parentid, board['id']))
                    template_filename = "mod.html"
                    template_values = {"mode": 3, "dir": board["dir"], "posts": posts}
                else:
                    threads = FetchAll("SELECT * FROM `posts` WHERE boardid = %s AND parentid = 0 ORDER BY `bumped` DESC" % board["id"])
                    template_filename = "mod.html"
                    template_values = {"mode": 2, "dir": board["dir"], "threads": threads}
            elif path_split[2] == "recent":
                posts = FetchAll("SELECT posts.id, posts.subject, dir, boards.board_type, CASE parentid WHEN '0' THEN posts.id ELSE parentid END AS parentid, file, thumb, timestamp_formatted, timestamp, posts.message, INET6_NTOA(ip) AS ip, posts.name, email, tripcode, boards.name AS board_name FROM posts INNER JOIN boards ON posts.boardid = boards.id WHERE posts.timestamp > UNIX_TIMESTAMP() - 86400 ORDER BY timestamp DESC")
                template_filename = "recent.html"
                template_values = {"posts": posts}
            elif path_split[2] == 'staff':
                if staff_account['rights'] != 0:
                    return
                action_taken = False

                if len(path_split) > 3:
                    if path_split[3] == 'add' or path_split[3] == 'edit':
                        member = None
                        member_username = ''
                        member_rights = 3

                        if path_split[3] == 'edit':
                          if len(path_split) > 4:
                            member = FetchOne('SELECT * FROM `staff` WHERE `id` = %s LIMIT 1', (path_split[4],))
                            if member:
                              member_username = member['username']
                              member_rights = member['rights']
                              action = 'edit/' + str(member['id'])

                              try:
                                if self.formdata.get('user'):
                                  if self.formdata['rights'] in [0, 1, 2, 3]:
                                    action_taken = True

                                    UpdateDb("UPDATE `staff` SET `username` = %s, `rights` = %s WHERE `id` = LIMIT 1",
                                            (self.formdata['user'], self.formdata['rights'], member['id']))
                                    message = _(
                                        'Staff member updated.')
                                    logAction(staff_account['username'], _(
                                        'Updated staff account for %s') % self.formdata['user'])
                                    template_filename = "message.html"
                              except:
                                pass
                        else:
                            action = 'add'
                            if self.formdata.get('user') and self.formdata.get('pass'):
                                username_taken = FetchOne(
                                    'SELECT COUNT(1) as count FROM `staff` WHERE `username` = %s LIMIT 1', (self.formdata['user'],))
                                if not username_taken['count']:
                                    if self.formdata['rights'] in ['0', '1', '2', '3']:
                                        action_taken = True
                                        pass_hash = genPasswdHash(
                                            self.formdata['pass'])

                                        InsertDb("INSERT INTO `staff` (`username`, `password`, `added`, `rights`) VALUES (%s, %s, %s, %s)",
                                                (self.formdata['user'], pass_hash, timestamp(), self.formdata['rights']))
                                        message = _('Staff member added.')
                                        logAction(
                                            staff_account['username'], 'Added staff account for ' + self.formdata['user'])

                                        template_filename = "message.html"
                                else:
                                    action_taken = True
                                    message = _(
                                        'That username is already in use.')
                                    template_filename = "message.html"

                        if not action_taken:
                            action_taken = True

                            if action == 'add':
                                submit = 'Agregar'
                            else:
                                submit = 'Editar'

                            template_filename = "staff.html"
                            template_values = {'mode': 1,
                                               'action': action,
                                               'member': member,
                                               'member_username': member_username,
                                               'member_rights': member_rights,
                                               'submit': submit}
                    elif path_split[3] == 'delete':
                        if not moderator:
                            return

                        action_taken = True
                        message = '<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

                        action_taken = True
                        member = FetchOne('SELECT `username` FROM `staff` WHERE `id` = %s LIMIT 1', (path_split[4],))
                        if member:
                            UpdateDb('DELETE FROM `staff` WHERE `id` = %s LIMIT 1', (path_split[4],))
                            message = 'Staff member deleted.'
                            template_filename = "message.html"
                            logAction(staff_account['username'], _(
                                'Deleted staff account for %s') % member['username'])
                        else:
                            message = _(
                                'Unable to locate a staff account with that ID.')
                            template_filename = "message.html"

                if not action_taken:
                    staff = FetchAll('SELECT * FROM `staff` ORDER BY `rights`')
                    for member in staff:
                        if member['rights'] == 0:
                            member['rights'] = _('Super-administrator')
                        elif member['rights'] == 1:
                            member['rights'] = _('Administrator')
                        elif member['rights'] == 2:
                            member['rights'] = _('Developer')
                        elif member['rights'] == 3:
                            member['rights'] = _('Moderator')
                        if member['lastactive']:
                            member['lastactivestamp'] = member['lastactive']
                            member['lastactive'] = formatTimestamp(
                                member['lastactive'])
                        else:
                            member['lastactive'] = _('Never')
                            member['lastactivestamp'] = 0
                    template_filename = "staff.html"
                    template_values = {'mode': 0, 'staff': staff}
            elif path_split[2] == 'delete':
                if not moderator:
                    return

                do_ban = False
                try:
                    if self.formdata['ban'] == 'true':
                        do_ban = True
                except:
                    pass
                

                board = setBoard(path_split[3])
                post = FetchOne('SELECT id, message, thumb, name, tripcode FROM posts WHERE boardid = %s AND id = %s LIMIT 1' % (board['id'], path_split[4]))

                template_filename = "delete.html"
                template_values = {'do_ban': do_ban,
                                   'curboard': board,
                                   'post': post }
            elif path_split[2] == 'delete_confirmed':
                if not moderator:
                    return

                do_ban = self.formdata.get('ban')
                permanently = self.formdata.get('perma')
                imageonly = self.formdata.get('imageonly')

                board = setBoard(path_split[3])
                postid = int(path_split[4])
                post = FetchOne('SELECT id, message, parentid, INET6_NTOA(ip) AS ip FROM posts WHERE boardid = %s AND id = %s' % (board['id'], postid))

                if not permanently:
                    deletePost(path_split[4], None, 2, imageonly)
                    regenerateTrash()
                else:
                    deletePost(path_split[4], None, 0, imageonly)
                regenerateHome()

                # Borrar denuncias
                UpdateDb("DELETE FROM `reports` WHERE `postid` = %s",
                         (int(path_split[4]),))
                boards = FetchAll('SELECT `name`, `dir` FROM `boards` ORDER BY `dir`')

                if imageonly:
                    message = 'Archivo de post /%s/%s eliminado.' % (
                        board['dir'], post['id'])
                elif permanently or post["parentid"] == 0:
                    message = 'Post /%s/%s eliminado permanentemente.' % (
                        board['dir'], post['id'])
                else:
                    message = 'Post /%s/%s enviado a la papelera.' % (
                        board['dir'], post['id'])
                template_filename = "message.html"
                logAction(staff_account['username'], message +
                          ' Contenido: ' + post['message'] + ' IP: ' + post['ip'])

                if do_ban:
                    message = _('Redirecting to ban page...') + '<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])
                postid = int(path_split[4])
                post = FetchOne('SELECT `parentid`, `locked` FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1', (board['id'], pid) )
                if not post:
                    message = _('Unable to locate a post with that ID.')
                    template_filename = "message.html"
                else:
                    if not post['parentid']:
                        message = _('Post is not a thread opener.')
                        template_filename = "message.html"
                    else:
                        if post['locked'] == 0:
                            # Cerrar si esta abierto
                            setLocked = 1
                        else:
                            # Abrir si esta cerrado
                            setLocked = 0

                        UpdateDb("UPDATE `posts` SET `locked` = %s WHERE `boardid` = %s AND `id` = %s LIMIT 1",
                            (setLocked, board["id"], post["id"]))
                        threadUpdated(pid)
                    if setLocked == 1:
                        message = _('Thread successfully closed.')
                        logAction(staff_account['username'], _('Closed thread %s') % ('/' + board['dir'] + '/' + pid) )
                    else:
                        message = _('Thread successfully opened.')
                        logAction(staff_account['username'], _('Opened thread %s') % ('/' + board['dir'] + '/' + pid) )
                    template_filename = "message.html"
            elif path_split[2] == 'permasage':
                setPermasaged = 0

                # Nos vamos al board y ubicamos el post
                board = setBoard(path_split[3])
                pid = int(path_split[4])
                post = FetchOne('SELECT `parentid`, `locked` FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1', (board['id'], pid) )
                if not post:
                    message = 'No se encuentra un hilo con ese ID.'
                    template_filename = "message.html"
                elif post['locked'] == '1':
                    message = 'Solo se puede aplicar permasage en un hilo abierto.'
                    template_filename = "message.html"
                else:
                    if post['parentid']:
                        message = 'El post indicado es una respuesta a un hilo.'
                        template_filename = "message.html"
                    else:
                        if post['locked'] == 2:
                            # Sacar permasage
                            setPermasaged = 0
                        else:
                            # Colocar permasage
                            setPermasaged = 2

                        UpdateDb("UPDATE `posts` SET `locked` = %s WHERE `boardid` = '%s' AND `id` = '%s' LIMIT 1" % (setPermasaged, board["id"], pid) )
                        regenerateFrontPages()
                        threadUpdated(pid)

                    if setPermasaged == 2:
                        message = 'Thread successfully permasaged.'
                        logAction(staff_account['username'], 'Activado permasage en el hilo /%s/%s' % (board['dir'], pid) )
                    else:
                        message = 'Thread successfully un-permasaged.'
                        logAction(staff_account['username'], 'Desactivado permasage en el hilo /%s/%s' % (board['dir'], pid) )
                    template_filename = "message.html"
            elif path_split[2] == 'move':
                raise NotImplementedError

                #if not moderator:
                if not administrator:
                    return

                oldboardid = ""
                oldthread = ""
                newboardid = ""
                try:
                    oldboardid = path_split[3]
                    oldthread = path_split[4]
                    newboardid = path_split[5]
                except:
                    pass

                try:
                    oldboardid = self.formdata['oldboardid']
                    oldthread = self.formdata['oldthread']
                    newboardid = self.formdata['newboardid']
                except:
                    pass

                if oldboardid and oldthread and newboardid:
                    message = "import"
                    import shutil
                    message += "ok"

                    board = setBoard(oldboardid)
                    oldboard = board['dir']
                    oldboardsubject = board['subject']
                    oldboardname = random.choice(board["anonymous"].split('|'))

                    # get old posts
                    posts = FetchAll(
                        "SELECT * FROM `posts` WHERE (`id` = {0} OR `parentid` = {0}) AND `boardid` = {1} ORDER BY id ASC".format(oldthread, board['id']))

                    # switch to new board
                    board = setBoard(newboardid)
                    newboard = board['dir']

                    refs = {}
                    moved_files = []
                    moved_thumbs = []
                    moved_cats = []
                    newthreadid = 0
                    newthread = 0
                    num = 1

                    message = "from total: %s<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(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.items():
                        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 INET6_NTOA(`ip`) AS `ip` FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1' % (board['id'], int(path_split[4])) )
                    
                    if not post:
                        message = _('Unable to locate a post with that ID.')
                        template_filename = "message.html"
                    else:
                        message = '<meta http-equiv="refresh" content="0;url=' + \
                            Settings.CGI_URL + 'manage/ban?ip=' + post['ip'] + '" />Espere...'
                        template_filename = "message.html"
                else:
                    reason = self.formdata.get('reason')
                    if reason is not None:
                        # Start ban process
                        import netaddr
                        ip = self.formdata['ip']

                        # Parse CIDR or IP glob
                        try:
                            if netaddr.valid_ipv4(ip) or netaddr.valid_ipv6(ip):
                                ipaddress = netaddr.IPAddress(ip)
                                ipstart = ipend = ipstr = str(ipaddress)
                            elif netaddr.valid_glob(ip):
                                iprange = netaddr.glob_to_iprange(ip)
                                ipstart, ipend = str(iprange[0]), str(iprange[-1])

                                cidrs = iprange.cidrs()
                                if len(cidrs) == 1:
                                    ipstr = str(cidrs[0])
                                else:
                                    ipstr = str(iprange)
                            elif '/' in ip:
                                # Try with CIDR
                                ipnetwork = netaddr.IPNetwork(ip)
                                ipstart, ipend = str(ipnetwork[0]), str(ipnetwork[-1])
                                ipstr = str(ipnetwork)
                            else:
                                self.error("IP o rango inválido.")
                                return
                        except netaddr.core.AddrFormatError as e:
                            self.error("Problema con el IP o rango ingresado: {}".format(e))
                            return

                        if self.formdata['seconds'] != '0':
                            until = timestamp() + int(self.formdata['seconds'])
                        else:
                            until = 0
                        where = ''
                        if 'board_all' not in self.formdata:
                            where = []
                            boards = FetchAll('SELECT `dir` FROM `boards`')
                            for board in boards:
                                keyname = 'board_' + board['dir']
                                if keyname in self.formdata:
                                    if self.formdata[keyname] == "1":
                                        where.append(board['dir'])
                            if len(where) > 0:
                                where = boards2str(where)
                            else:
                                self.error(
                                    _("You must select where the ban shall be placed"))
                                return

                        if 'edit' in self.formdata:
                            UpdateDb("DELETE FROM `bans` WHERE `id` = '" + _mysql.escape_string(self.formdata['edit']) + "' LIMIT 1")
                        """else: # TODO : Duplicate check
                            ban = FetchOne("SELECT `id` FROM `bans` WHERE `ip` = '" + _mysql.escape_string(
                                ip) + "' AND `boards` = '" + _mysql.escape_string(where) + "' LIMIT 1")
                            if ban:
                                self.error(_('There is already an identical ban for this IP.') + '<a href="' +
                                           Settings.CGI_URL+'manage/ban/' + ip + '?edit=' + ban['id']+'">' + _('Edit') + '</a>')
                                return"""

                        # Blind mode
                        blind = self.formdata.get('blind', 0)

                        #raise UserError, "{} {} {}".format(ipstart, ipend, ipstr)

                        # Banear sin mensaje
                        InsertDb("INSERT INTO `bans` (`ipstart`, `ipend`, `ipstr`, `boards`, `added`, `until`, `staff`, `reason`, `note`, `blind`) VALUES "
                                 "(INET6_ATON(%s), INET6_ATON(%s), %s, %s, %s, %s, %s, %s, %s, %s)",
                                 (ipstart, ipend, ipstr, where, timestamp(), until, staff_account['username'], self.formdata['reason'], self.formdata['note'], blind))

                        regenerateAccess()
                        if 'edit' in self.formdata:
                            message = _('Ban successfully edited.')
                            action = 'Edited ban for ' + ip
                        else:
                            message = _('Ban successfully placed.')
                            action = 'Banned ' + ip
                            if until != 0:
                                action += ' until ' + \
                                    formatTimestamp(until)
                            else:
                                action += ' permanently'
                        logAction(staff_account['username'], action)
                        template_filename = 'message.html'
                    else:
                        startvalues = {'where': [],
                                       'reason': '',
                                       'note': '',
                                       'message': '(GET OUT)',
                                       'seconds': 0,
                                       'blind': 1}
                        edit_id = 0
                        if 'edit' in self.formdata:
                            edit_id = self.formdata['edit']
                            ban = FetchOne("SELECT `id`, INET6_NTOA(`ip`) AS 'ip', CASE WHEN `netmask` IS NULL THEN '255.255.255.255' ELSE INET_NTOA(`netmask`) END AS 'netmask', boards, added, until, staff, reason, note, blind FROM `bans` WHERE `id` = %s ORDER BY `added` DESC", (edit_id) )
                            if ban:
                                if ban['boards'] == '':
                                    where = ''
                                else:
                                    where = boards2str(ban['boards'])
                                if ban['until'] == '0':
                                    until = 0
                                else:
                                    until = int(ban['until']) - timestamp()
                                startvalues = {'where': where,
                                               'reason': ban['reason'],
                                               'note': ban['note'],
                                               'seconds': str(until),
                                               'blind': ban['blind']
                                               }
                            else:
                                edit_id = 0

                        template_filename = "bans.html"
                        template_values = {'mode': 1,
                                           'boards': boardlist(),
                                           'ip': self.formdata.get('ip'),
                                           'startvalues': startvalues,
                                           'edit_id': edit_id}
            elif path_split[2] == 'bans':
                if not moderator:
                    return

                action_taken = False
                if len(path_split) > 4:
                    if path_split[3] == 'delete':
                        ip = FetchOne("SELECT ipstr FROM `bans` WHERE `id` = %s LIMIT 1",
                                      (path_split[4],))
                        if ip:
                            # Delete ban
                            UpdateDb('DELETE FROM `bans` WHERE `id` = %s LIMIT 1',
                                    (path_split[4],))
                            regenerateAccess()
                            message = _('Ban successfully deleted.')
                            template_filename = "message.html"
                            logAction(staff_account['username'], _(
                                'Deleted ban for %s') % ip)
                        else:
                            message = _(
                                'There was a problem while deleting that ban.  It may have already been removed, or recently expired.')
                            template_filename = "message.html"

                if not action_taken:
                    bans = FetchAll(
                        "SELECT `id`, `ipstr` AS 'ip', boards, added, until, staff, reason, note, blind FROM `bans` ORDER BY `added` DESC")
                    if bans:
                        for ban in bans:
                            if ban['boards'] == '':
                                ban['boards'] = _('All boards')
                            else:
                                where = str2boards(ban['boards'])
                                if len(where) > 1:
                                    ban['boards'] = '/' + \
                                        '/, /'.join(where) + '/'
                                else:
                                    ban['boards'] = '/' + where[0] + '/'
                            ban['added'] = formatTimestamp(ban['added'])
                            if ban['until'] == 0:
                                ban['until'] = _('Does not expire')
                            else:
                                ban['until'] = formatTimestamp(ban['until'])
                            if ban['blind']:
                                ban['blind'] = 'Sí'
                            else:
                                ban['blind'] = 'No'
                    template_filename = "bans.html"
                    template_values = {'mode': 0, 'bans': bans}
            elif path_split[2] == 'changepassword':
                form_submitted = False
                try:
                    if self.formdata['oldpassword'] != '' and self.formdata['newpassword'] != '' and self.formdata['newpassword2'] != '':
                        form_submitted = True
                except:
                    pass
                if form_submitted:
                    if verifyPasswd(staff_account['username'], self.formdata['oldpassword']):
                        if self.formdata['newpassword'] == self.formdata['newpassword2']:
                            UpdateDb('UPDATE `staff` SET `password` = %s WHERE `id` = %s LIMIT 1',
                                (genPasswdHash(self.formdata['newpassword']), staff_account['id']))
                            message = _(
                                'Password successfully changed.  Please log out and log back in.')
                            template_filename = "message.html"
                        else:
                            message = _('Passwords did not match.')
                            template_filename = "message.html"
                    else:
                        message = _('Current password incorrect.')
                        template_filename = "message.html"
                else:
                    template_filename = "changepassword.html"
                    template_values = {}
            elif path_split[2] == 'board':
                if not administrator:
                    return

                if len(path_split) > 3:
                    board = setBoard(path_split[3])
                    form_submitted = False
                    try:
                        if self.formdata['name'] != '':
                            form_submitted = True
                    except:
                        pass
                    if form_submitted:
                        # 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:
                            board['recyclebin'] = 1
                        else:
                            board['recyclebin'] = 0
                        if 'disable_name' in self.formdata:
                            board['disable_name'] = 1
                        else:
                            board['disable_name'] = 0
                        if 'disable_subject' in self.formdata:
                            board['disable_subject'] = 1
                        else:
                            board['disable_subject'] = 0
                        if 'secret' in self.formdata:
                            board['secret'] = 1
                        else:
                            board['secret'] = 0
                        if 'locked' in self.formdata:
                            board['locked'] = 1
                        else:
                            board['locked'] = 0
                        board['postarea_desc'] = self.formdata['postarea_desc']
                        if 'allow_noimage' in self.formdata:
                            board['allow_noimage'] = 1
                        else:
                            board['allow_noimage'] = 0
                        if 'allow_images' in self.formdata:
                            board['allow_images'] = 1
                        else:
                            board['allow_images'] = 0
                        if 'allow_image_replies' in self.formdata:
                            board['allow_image_replies'] = 1
                        else:
                            board['allow_image_replies'] = 0
                        if 'allow_spoilers' in self.formdata:
                            board['allow_spoilers'] = 1
                        else:
                            board['allow_spoilers'] = 0
                        if 'allow_oekaki' in self.formdata:
                            board['allow_oekaki'] = 1
                        else:
                            board['allow_oekaki'] = 0
                        if 'archive' in self.formdata:
                            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:
                                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`, `IS_DELETED` FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1', (board['id'], path_split[6]))
                        if not post:
                            message = _('Unable to locate a post with that ID.') + '<br />'
                            template_filename = "message.html"
                        else:
                            UpdateDb('UPDATE `posts` SET `IS_DELETED` = 0 WHERE `boardid` = %s AND `id` = %s LIMIT 1', (board['id'], path_split[6]))
                            if post['parentid'] != 0:
                                threadUpdated(post['parentid'])
                            else:
                                regenerateFrontPages()

                            if post['IS_DELETED'] == 2:
                              regenerateTrash()

                            message = _('Post successfully restored.')
                            logAction(staff_account['username'], _('Restored post %s') % ('/' + path_split[5] + '/' + path_split[6]) )

                    if path_split[4] == 'delete':
                        board = setBoard(path_split[5])
                        post = FetchOne('SELECT id, parentid, message, INET6_NTOA(ip) AS ip FROM `posts` WHERE `boardid` = %s AND `id` = %s LIMIT 1',
                                        (board['id'], path_split[6]))
                        if not post:
                            message = _('Unable to locate a post with that ID.')
                        else:
                            deletePost(post['id'], None)

                            if post['parentid']:
                                threadUpdated(post['parentid'])
                            else:
                                regenerateFrontPages()

                            message = "Post %s eliminado permanentemente" % ('/' + board['dir'] + '/' + str(post['id']))
                            logAction(staff_account['username'], message + ' desde papelera. Contenido: ' + post['message'] + ' IP: ' + post['ip'])

                # Delete more than 1 post
                if 'deleteall' in self.formdata:
                    return  # TODO
                    deleted = 0
                    for key in self.formdata:
                        if key[:2] == '!i':
                            # Board where the post is
                            dir = key[2:].split('/')[0]
                            postid = key[2:].split('/')[1]  # Post to delete

                            # Delete post start
                            post = FetchOne('SELECT `parentid`, `dir` FROM `posts` INNER JOIN `boards` ON posts.boardid = boards.id WHERE `dir` = \'' + _mysql.escape_string(dir) + '\' AND posts.id = \'' + _mysql.escape_string(postid) + '\' LIMIT 1')
                            if not post:
                                message = _('Unable to locate a post with that ID.')
                            else:
                                board = setBoard(dir)
                                deletePost(int(postid), None)
                                if post['parentid'] != '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:
                    type = int(self.formdata["type"])
                else:
                    type = 0

                # Generate board list
                boards = FetchAll('SELECT `name`, `dir` FROM `boards` ORDER BY `dir`')
                for board in boards:
                    if 'board' in self.formdata and self.formdata['board'] == board['dir']:
                        board['checked'] = True
                    else:
                        board['checked'] = False

                # Get type filter
                if type != 0:
                    type_condition = "= " + str(type)
                else:
                    type_condition = "!= 0"

                # Table
                if 'board' in self.formdata and self.formdata['board'] != 'all':
                    cboard = setBoard(self.formdata['board'])['dir']
                    posts = FetchAll("SELECT posts.id, posts.timestamp, timestamp_formatted, IS_DELETED, INET6_NTOA(posts.ip) AS ip, posts.message, dir, boardid FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE `dir` = '%s' AND IS_DELETED %s ORDER BY `timestamp` DESC LIMIT %d, %d" % (cboard, type_condition, currentpage*pagesize, pagesize))
                    try:
                        totals = FetchOne("SELECT COUNT(id) AS count FROM `posts` WHERE IS_DELETED %s AND `boardid` = %s LIMIT 1" % (type_condition, posts[0]['boardid']) )
                    except:
                        skip = True
                else:
                    cboard = 'all'
                    posts = FetchAll("SELECT posts.id, posts.timestamp, timestamp_formatted, IS_DELETED, INET6_NTOA(posts.ip) AS ip, posts.message, dir FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE IS_DELETED %s ORDER BY `timestamp` DESC LIMIT %d, %d" % (type_condition, currentpage*pagesize, pagesize))
                    totals = FetchOne("SELECT COUNT(id) AS count FROM `posts` WHERE IS_DELETED %s" % type_condition)

                template_filename = "recyclebin.html"
                template_values = {'message': message,
                                   'type': type,
                                   'boards': boards,
                                   'skip': skip}

                if not skip:
                    # Calculate number of pages
                    total = totals["count"]
                    pages = int(math.ceil(total / pagesize))

                    # Create delete form
                    if 'board' in self.formdata and self.formdata['board'] != 'all':
                        board = setBoard(self.formdata['board'])['dir']
                    else:
                        board = None

                    navigator = ''
                    if currentpage > 0:
                        navigator += '<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` = %s LIMIT 1", (board_dir,))
                    if not board_exists:
                        os.mkdir(Settings.ROOT_DIR + board_dir)
                        os.mkdir(Settings.ROOT_DIR + board_dir + '/res')
                        if not os.path.exists(Settings.IMAGES_DIR + board_dir):
                            os.mkdir(Settings.IMAGES_DIR + board_dir)
                        os.mkdir(Settings.IMAGES_DIR + board_dir + '/src')
                        os.mkdir(Settings.IMAGES_DIR + board_dir + '/thumb')
                        os.mkdir(Settings.IMAGES_DIR + board_dir + '/mobile')
                        os.mkdir(Settings.IMAGES_DIR + board_dir + '/cat')
                        if os.path.exists(Settings.ROOT_DIR + board_dir) and os.path.isdir(Settings.ROOT_DIR + board_dir):
                            UpdateDb("INSERT INTO `boards` (`dir`, `name`) VALUES (%s, %s)",
                                (board_dir, self.formdata['name']))
                            board = setBoard(board_dir)
                            f = open(Settings.ROOT_DIR +
                                     board['dir'] + '/.htaccess', 'w')
                            try:
                                f.write('DirectoryIndex index.html')
                            finally:
                                f.close()
                            regenerateFrontPages()
                            message = _('Board added')
                            template_filename = "message.html"
                            logAction(staff_account['username'], _(
                                'Added board %s') % ('/' + board['dir'] + '/'))
                        else:
                            message = _(
                                'There was a problem while making the directories.')
                            template_filename = "message.html"
                    else:
                        message = _(
                            'There is already a board with that directory.')
                        template_filename = "message.html"

                if not action_taken:
                    template_filename = "addboard.html"
                    template_values = {}
            elif path_split[2] == 'trim':
                if not administrator:
                    return
                
                board = setBoard(path_split[3])
                trimThreads()
                self.output = "done trimming"
                return
            elif path_split[2] == 'setexpires':
                if not administrator:
                    return
                
                board = setBoard(path_split[3])
                parentid = int(path_split[4])
                days = int(path_split[5])
                t = time.time()

                expires = int(t) + (days * 86400)
                date_format = '%d/%m'
                expires_formatted = datetime.datetime.fromtimestamp(
                    expires).strftime(date_format)

                sql = "UPDATE posts SET expires = timestamp + (%s * 86400), expires_formatted = FROM_UNIXTIME((timestamp + (%s * 86400)), '%s') WHERE boardid = %s AND id = %s" % (
                    str(days), str(days), date_format, board["id"], str(parentid))
                UpdateDb(sql)

                self.output = "done " + sql
                return
            elif path_split[2] == 'fixflood':
                if not administrator:
                    return
                
                board = setBoard('zonavip')
                threads = FetchAll(
                    "SELECT * FROM posts WHERE boardid = %s AND parentid = 0 AND subject LIKE 'querido mod%%'" % board['id'])
                
                for thread in threads:
                    self.output += "%s<br>" % thread['id']
                    #deletePost(thread['id'], None)
                return
            elif path_split[2] == 'fixico':
                if not administrator:
                    return
                
                board = setBoard(path_split[3])
                
                if board['dir'] != 'noticias':
                    return

                threads = FetchAll("SELECT * FROM posts WHERE boardid = %s AND parentid = 0 AND message NOT LIKE '<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':
                if not administrator:
                    return
                
                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 += str(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(list(zip(thread['keys'], row))) for row in thread['posts']]
                        template_fname = "txt_archive.html"

                        post_preview = cut_home_msg(
                            thread['posts'][0]['message'], 0)
                        page = renderTemplate("txt_archive.html", {"threads": [
                                              thread], "preview": post_preview}, False)
                        with open(Settings.ROOT_DIR + board["dir"] + "/kako/" + str(thread['timestamp']) + ".html", "w") as f:
                            f.write(page)

                        self.output += 'done' + str(time.time() - t) + '<br />'
                    else:
                        self.output += 'El hilo no existe.<br />'
            elif path_split[2] == 'fixexpires':
                if not administrator:
                    return
                
                board = setBoard(path_split[3])

                if int(board["maxage"]):
                    date_format = '%d/%m'
                    date_format_y = '%m/%Y'
                    if int(board["maxage"]) >= 365:
                        date_format = date_format_y
                    sql = "UPDATE posts SET expires = timestamp + (%s * 86400), expires_formatted = FROM_UNIXTIME((timestamp + (%s * 86400)), '%s') WHERE boardid = %s AND parentid = 0" % (
                        board["maxage"], board["maxage"], date_format, board["id"])
                    UpdateDb(sql)

                    alert_time = int(
                        round(int(board['maxage']) * Settings.MAX_AGE_ALERT))
                    sql = "UPDATE posts SET expires_alert = CASE WHEN UNIX_TIMESTAMP() > (expires - %d*86400) THEN 1 ELSE 0 END WHERE boardid = %s AND parentid = 0" % (alert_time,
                                                                                                                                                                        board["id"])
                    UpdateDb(sql)
                else:
                    sql = "UPDATE posts SET expires = 0, expires_formatted = '', expires_alert = 0 WHERE boardid = %s AND parentid = 0" % (
                        board["id"])
                    UpdateDb(sql)

                self.output = "done"
                return
            elif path_split[2] == 'fixid':
                if not administrator:
                    return
                
                board = setBoard(path_split[3])
                posts = FetchAll(
                    'SELECT * FROM `posts` WHERE `boardid` = %s' % board['id'])
                self.output = "total: %d<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':
                return
                board = setBoard(path_split[3])
                #posts = FetchAll('SELECT * FROM `posts` WHERE `boardid` = %s' % board['id'])
                #posts = FetchAll('SELECT * FROM `posts` WHERE `name` LIKE \'%s\'' % '%%')
                #posts = FetchAll('SELECT * FROM `posts` WHERE `name` = \'%s\'' % path_split[4])
                new_name = board['anonymous']
                self.output = new_name + "<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':
                if not administrator:
                    return
                
                board = setBoard(path_split[3])
                thread = FetchOne(
                    'SELECT * FROM `posts` WHERE `parentid` = 0 AND `boardid` = %s' % board['id'])
                subject = str(path_split[4])
                self.output = subject + "->" + thread['id'] + "<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':
                if not administrator:
                    return
                
                board = setBoard(path_split[3])
                threads = FetchAll(
                    'SELECT * FROM `posts` WHERE parentid = 0 AND `boardid` = %s' % board['id'])
                for t in threads:
                    length = threadNumReplies(t['id'])
                    UpdateDb('UPDATE posts SET length = %d WHERE boardid = %s AND id = %s' % (
                        length, board['id'], t['id']))

                self.output = 'done'
                return
            elif path_split[2] == 'archive':
                if not administrator:
                    return
                
                t = time.time()
                board = setBoard(path_split[3])
                postid = int(path_split[4])
                archiveThread(postid)
                self.output = "todo ok %s" % str(time.time() - t)
            elif path_split[2] == 'filters':
                action_taken = False
                if len(path_split) > 3 and path_split[3] == 'add':
                    if "add" in self.formdata:
                        edit_id = 0
                        if 'edit' in self.formdata:
                            edit_id = int(self.formdata['edit'])

                        # We decide what type of filter it is.
                        # 0: Word / 1: Name/Trip
                        filter_type = int(self.formdata["type"])
                        filter_action = int(self.formdata["action"])
                        filter_from = ''
                        filter_tripcode = ''

                        where = ''
                        if 'board_all' not in self.formdata:
                            where = []
                            boards = FetchAll('SELECT `dir` FROM `boards`')
                            for board in boards:
                                keyname = 'board_' + board['dir']
                                if keyname in self.formdata:
                                    if self.formdata[keyname] == "1":
                                        where.append(board['dir'])
                            if len(where) > 0:
                                where = boards2str(where)
                            else:
                                self.error(
                                    _("You must select what board the filter will affect"))
                                return

                        if filter_type == 0:
                            # Word filter
                            if len(self.formdata["word"]) > 0:
                                filter_from = html.escape(self.formdata["word"])
                            else:
                                self.error(_("You must enter a word."))
                                return
                        elif filter_type == 1:
                            # Name/trip filter
                            can_add = False
                            if len(self.formdata["name"]) > 0:
                                filter_from = self.formdata["name"]
                                can_add = True
                            if len(self.formdata["trip"]) > 0:
                                filter_tripcode = self.formdata["trip"]
                                can_add = True
                            if not can_add:
                                self.error(
                                    _("You must enter a name and/or a tripcode."))
                                return

                        # Action
                        sql_query = ''
                        filter_reason = ''
                        if len(self.formdata["reason"]) > 0:
                            filter_reason = self.formdata["reason"]
                        if filter_action == 0:
                            # Cancel post
                            sql_query = "INSERT INTO `filters` (`id`, `boards`, `type`, `action`, `from`, `from_trip`, `reason`, `added`, `staff`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)"
                            sql_params = (edit_id, where, filter_type, filter_action, filter_from, filter_tripcode, filter_reason, timestamp(), staff_account['username'])
                        elif filter_action == 1:
                            # Change to
                            if len(self.formdata["changeto"]) > 0:
                                filter_to = self.formdata["changeto"]
                                sql_query = "INSERT INTO `filters` (`id`, `boards`, `type`, `action`, `from`, `from_trip`, `reason`, `to`, `added`, `staff`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
                                sql_params = (edit_id, where, filter_type, filter_action, filter_from, filter_tripcode, filter_reason, filter_to, timestamp(), staff_account['username'])
                            else:
                                self.error(
                                    _("You must enter a word to change to."))
                                return
                        elif filter_action == 2:
                            # Ban
                            filter_seconds = 0
                            if len(self.formdata["seconds"]) > 0:
                                filter_seconds = int(self.formdata["seconds"])
                            if "blind" in self.formdata and self.formdata["blind"] == 1:
                                filter_blind = 1
                            else:
                                filter_blind = 2

                            sql_query = "INSERT INTO `filters` (`id`, `boards`, `type`, `action`, `from`, `from_trip`, `reason`, `seconds`, `blind`, `added`, `staff`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
                            sql_params = (edit_id, where, filter_type, filter_action, filter_from, filter_tripcode, filter_reason,
                                         filter_seconds, filter_blind, timestamp(), staff_account['username'])
                        elif filter_action == 3:
                            # Redirect URL
                            if len(self.formdata['redirect_url']) > 0:
                                redirect_url = self.formdata['redirect_url']
                                redirect_time = 0
                                try:
                                    redirect_time = int(self.formdata['redirect_time'])
                                except:
                                    pass
                            else:
                                self.error(
                                    _("You must enter a URL to redirect to."))
                                return

                            sql_query = "INSERT INTO `filters` (`id`, `boards`, `type`, `action`, `from`, `from_trip`, `reason`, `redirect_url`, `redirect_time`, `added`, `staff`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
                            sql_params = (edit_id, where, filter_type, filter_action, filter_from, filter_tripcode, filter_reason, redirect_url, redirect_time, timestamp(), staff_account['username'])
                        # DO QUERY!
                        if edit_id > 0:
                            UpdateDb("DELETE FROM `filters` WHERE `id` = %s", (edit_id,))
                            UpdateDb(sql_query, sql_params)
                            message = 'Filter edited.'
                        else:
                            filt = FetchOne("SELECT `id` FROM `filters` WHERE `boards` = %s AND `type` = %s AND `from` = %s", (where, filter_type, filter_from))
                            if not filt:
                                UpdateDb(sql_query, sql_params)
                                message = 'Filter added.'
                            else:
                                message = 'This filter already exists here:' + ' <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 and int(self.formdata['edit']) > 0:
                            # Load values
                            edit_id = int(self.formdata['edit'])
                            filt = FetchOne(
                                "SELECT * FROM `filters` WHERE `id` = %s LIMIT 1" % str(edit_id))
                            if not filt['boards']:
                                where = ''
                            else:
                                where = str2boards(filt['boards'])
                            startvalues = {'type': filt['type'],
                                           'trip': filt['from_trip'],
                                           'where': where,
                                           'action': filt['action'],
                                           'changeto': html.escape(filt['to'], True),
                                           'reason': filt['reason'],
                                           'seconds': filt['seconds'],
                                           'blind': filt['blind'],
                                           'redirect_url': filt['redirect_url'],
                                           'redirect_time': filt['redirect_time'], }
                            if filt['type'] == '1':
                                startvalues['name'] = filt['from']
                                startvalues['word'] = ''
                            else:
                                startvalues['name'] = ''
                                startvalues['word'] = filt['from']
                        else:
                            startvalues = {'type': '0',
                                           'word': '',
                                           'name': '',
                                           'trip': '',
                                           'where': [],
                                           'action': '0',
                                           'changeto': '',
                                           'reason': _('Forbidden word'),
                                           'seconds': '0',
                                           'blind': '1',
                                           'redirect_url': 'http://',
                                           'redirect_time': '5'}

                        if edit_id > 0:
                            submit = "Editar Filtro"
                        else:
                            submit = "Agregar filtro"

                        action_taken = True
                        template_filename = "filters.html"
                        template_values = {'mode': 1,
                                           'edit_id': edit_id,
                                           'boards': boardlist(),
                                           'startvalues': startvalues,
                                           'submit': submit}
                elif len(path_split) > 4 and path_split[3] == 'delete':
                    delid = int(path_split[4])
                    UpdateDb(
                        "DELETE FROM `filters` WHERE id = '%s' LIMIT 1" % str(delid))
                    message = _('Deleted filter %s.') % str(delid)
                    template_filename = "message.html"
                    action_taken = True

                if not action_taken:
                    filters = FetchAll("SELECT * FROM `filters` ORDER BY `added` DESC")
                    for filter in filters:
                        if not filter['boards']:
                            filter['boards'] = _('All boards')
                        else:
                            where = str2boards(filter['boards'])
                            filter['boards'] = '/' + '/, /'.join(where) + '/'
                        if filter['type'] == 0:
                            filter['type_formatted'] = _('Word:') + ' <b>' + html.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>' + html.escape(filter['to']) + '</b>'
                        elif filter['action'] == 2:
                            if filter['blind'] == 1:
                                blind = _('Yes')
                            else:
                                blind = _('No')
                            filter['action_formatted'] = _('Autoban:') + '<br />' + \
                                (_('Length:')+' <i>%s</i><br />'+_('Blind:') +
                                 ' <i>%s</i>') % (filter['seconds'], blind)
                        elif filter['action'] == 3:
                            filter['action_formatted'] = (_('Redirect to:')+' %s ('+_('in %s secs')+')') % (
                                filter['redirect_url'], filter['redirect_time'])
                        else:
                            filter['action_formatted'] = '?'
                        filter['added'] = formatTimestamp(filter['added'])

                    template_filename = "filters.html"
                    template_values = {'mode': 0, 'filters': filters}
            elif path_split[2] == 'logs':
                if staff_account['rights'] not in [0, 2]:
                    return

                logs = FetchAll(
                    'SELECT * FROM `logs` ORDER BY `timestamp` DESC')
                for log in logs:
                    log['timestamp_formatted'] = formatTimestamp(
                        log['timestamp'])
                template_filename = "logs.html"
                template_values = {'logs': logs}
            elif path_split[2] == 'logout':
                message = _('Logging out...') + '<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:
                    try:
                        f = open('quotes.conf', 'w')
                        f.write(self.formdata["data"])
                        f.close()
                        message = 'Datos guardados.'
                        template_filename = "message.html"
                    except:
                        message = 'Error al guardar datos.'
                        template_filename = "message.html"
                try:
                    f = open('quotes.conf', 'r')
                    data = html.escape(f.read(1048576), True)
                    f.close()
                    template_filename = "quotes.html"
                    template_values = {'data': data}
                except:
                    message = 'Error al leer datos.'
                    template_filename = 'message.html'
            elif path_split[2] == 'recent_images':
                try:
                    if int(self.formdata['images']) > 256:
                        images = '256'
                    else:
                        images = self.formdata['images']
                    posts = FetchAll('SELECT * FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE CHAR_LENGTH(`thumb`) > 0 ORDER BY `timestamp` DESC LIMIT %s' % (images) )
                except:
                    posts = FetchAll('SELECT * FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE CHAR_LENGTH(`thumb`) > 0 ORDER BY `timestamp` DESC LIMIT 32')
                template_filename = "recent_images.html"
                template_values = {'posts': posts}
            elif path_split[2] == 'news':
                if not administrator:
                    return

                type = 1
                if 'type' in self.formdata:
                    type = int(self.formdata['type'])

                if type > 2:
                    raise UserError("Tipo no soportado")

                # canal del home
                if len(path_split) > 3:
                    if path_split[3] == 'add':
                        t = datetime.datetime.now()

                        # Insertar el nuevo post
                        title = ''
                        message = self.formdata["message"].replace(
                            "\n", "<br />")

                        # Titulo
                        if 'title' in self.formdata:
                            title = self.formdata["title"]

                        # Post anonimo
                        if 'anonymous' in self.formdata and self.formdata['anonymous'] == '1':
                            to_name = "Staff ★"
                        else:
                            to_name = "%s ★" % staff_account['username']
                        timestamp_formatted = formatDate(t)
                        if type > 0:
                            timestamp_formatted = re.sub(r"\(.+", "", timestamp_formatted)
                        else:
                            timestamp_formatted = re.sub(r"\(...\)", " ", timestamp_formatted)

                        UpdateDb("INSERT INTO `news` (type, staffid, staff_name, title, message, name, timestamp, timestamp_formatted) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
                                (type, staff_account['id'], staff_account['username'], title, message, to_name, timestamp(t), timestamp_formatted))

                        regenerateNews()
                        regenerateHome()
                        message = _("Added successfully.")
                        template_filename = "message.html"
                    if path_split[3] == 'delete':
                        # Eliminar un post
                        id = int(path_split[4])
                        UpdateDb("DELETE FROM `news` WHERE id = %d AND type = %d" % (id, type))
                        regenerateNews()
                        regenerateHome()
                        message = _("Deleted successfully.")
                        template_filename = "message.html"
                else:
                    posts = FetchAll(
                        "SELECT * FROM `news` WHERE type = %d ORDER BY `timestamp` DESC" % type)
                    template_filename = "news.html"
                    template_values = {'action': type, 'posts': posts}
            elif path_split[2] == 'newschannel':
                # if not administrator:
                #  return

                if len(path_split) > 3:
                    if path_split[3] == 'add':
                        t = datetime.datetime.now()
                        # Delete old posts
                        #posts = FetchAll("SELECT `id` FROM `news` WHERE `type` = '1' ORDER BY `timestamp` DESC LIMIT "+str(Settings.MODNEWS_MAX_POSTS)+",30")
                        # for post in posts:
                        #  UpdateDb("DELETE FROM `news` WHERE id = " + post['id'] + " AND `type` = '0'")

                        # Insert new post
                        message = ''
                        try:
                            # Cut long lines
                            message = self.formdata["message"]
                            message = clickableURLs(html.escape(message).rstrip()[0:8000])
                            message = onlyAllowedHTML(message)
                            if Settings.USE_MARKDOWN:
                                message = markdown(message)
                            if not Settings.USE_MARKDOWN:
                                message = message.replace("\n", "<br />")
                        except:
                            pass

                        # If it's preferred to remain anonymous...
                        if 'anonymous' in self.formdata and self.formdata['anonymous'] == '1':
                            to_name = "Staff ★"
                        else:
                            to_name = "%s ★" % staff_account['username']
                        timestamp_formatted = formatDate(t)

                        UpdateDb("INSERT INTO `news` (type, staffid, staff_name, title, message, name, timestamp, timestamp_formatted) VALUES (0, %s, %s, %s, %s, %s, %s, %s)",
                            (staff_account['id'], staff_account['username'], self.formdata['title'], message, to_name, timestamp(t), timestamp_formatted))

                        message = _("Added successfully.")
                        template_filename = "message.html"
                    if path_split[3] == 'delete':
                        if not administrator:
                            # We check that if he's not admin, he shouldn't be able to delete other people's posts
                            post = FetchOne("SELECT `staffid` FROM `news` WHERE id = %s AND type = '0'" % (int(path_split[4])) )
                            if post['staffid'] != staff_account['id']:
                                self.error(_('That post is not yours.'))
                                return

                        # Delete!
                        UpdateDb("DELETE FROM `news` WHERE id = %s AND type = '0'", (path_split[4],))
                        message = _("Deleted successfully.")
                        template_filename = "message.html"
                else:
                    # If he's not admin, show only his own posts
                    if administrator:
                        posts = FetchAll("SELECT * FROM `news` WHERE type = '0' ORDER BY `timestamp` DESC")
                    else:
                        posts = FetchAll("SELECT * FROM `news` WHERE staffid = %s AND type = 0 ORDER BY `timestamp` DESC",
                                        (staff_account['id'],))

                    template_filename = "news.html"
                    template_values = {'action': 'newschannel', 'posts': posts}
            elif path_split[2] == 'reports':
                if not moderator:
                    return

                message = None
                import math
                pagesize = Settings.REPORTS_PER_PAGE
                totals = FetchOne("SELECT COUNT(id) FROM `reports`")
                total = int(totals['COUNT(id)'])
                pages = int(math.ceil(total // pagesize))

                try:
                    currentpage = int(path_split[3])
                except:
                    currentpage = 0

                if len(path_split) > 4:
                    if path_split[4] == 'ignore':
                        # Delete report
                        UpdateDb("DELETE FROM `reports` WHERE `id` = %s", (path_split[5],))
                        message = _('Report %s ignored.') % path_split[5]
                if 'ignore' in self.formdata:
                    ignored = 0
                    if 'board' in self.formdata and self.formdata['board'] != 'all':
                        reports = FetchAll("SELECT `id` FROM `reports` WHERE `board` = %s ORDER BY `timestamp` DESC LIMIT %s, %s",
                                          (self.formdata['board'], currentpage*pagesize, pagesize))
                    else:
                        reports = FetchAll("SELECT `id` FROM `reports` ORDER BY `timestamp` DESC LIMIT %s, %s",
                                          (currentpage*pagesize, pagesize))

                    for report in reports:
                        keyname = 'i' + str(report['id'])
                        if keyname in self.formdata:
                            # Ignore here
                            UpdateDb("DELETE FROM `reports` WHERE `id` = %s",
                                    (report['id'],))
                            ignored += 1

                    message = _('Ignored %s report(s).') % str(ignored)

                # Generate board list
                boards = FetchAll(
                    'SELECT `name`, `dir` FROM `boards` ORDER BY `dir`')
                for board in boards:
                    if 'board' in self.formdata and self.formdata['board'] == board['dir']:
                        board['checked'] = True
                    else:
                        board['checked'] = False

                # Tabla
                if 'board' in self.formdata and self.formdata['board'] != 'all':
                    reports = FetchAll("SELECT id, timestamp, timestamp_formatted, postid, parentid, link, board, INET6_NTOA(ip) AS ip, reason, INET6_NTOA(repip) AS repip FROM `reports` WHERE `board` = %s ORDER BY `timestamp` DESC LIMIT %s, %s",
                                      (self.formdata['board'], currentpage*pagesize, pagesize))
                else:
                    reports = FetchAll("SELECT id, timestamp, timestamp_formatted, postid, parentid, link, board, INET6_NTOA(ip) AS ip, reason, INET6_NTOA(repip) AS repip FROM `reports` ORDER BY `timestamp` DESC LIMIT %s, %s",
                                      (currentpage*pagesize, pagesize))

                if 'board' in self.formdata:
                    curboard = self.formdata['board']
                else:
                    curboard = None

                # for report in reports:
                #  if report['parentid'] == '0':
                #    report['link'] = Settings.BOARDS_URL + report['board'] + '/res/' + report['postid'] + '.html#' + report['postid']
                #  else:
                #    report['link'] = Settings.BOARDS_URL + report['board'] + '/res/' + report['parentid'] + '.html#' + report['postid']

                navigator = ''
                if currentpage > 0:
                    navigator += '<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:
                    # If an IP was given...
                    if self.formdata['ip'] != '':
                        ip = self.formdata['ip']
                        posts = FetchAll("SELECT posts.*, boards.dir, boards.board_type, boards.subject AS default_subject FROM `posts` JOIN `boards` ON boards.id = posts.boardid WHERE ip = INET6_ATON(%s) ORDER BY posts.timestamp DESC", (ip,))
                        template_filename = "ipshow.html"
                        template_values = {"mode": 1, "ip": ip, "host": getHost(
                            ip), "country": getCountry(ip), "tor": addressIsTor(ip), "posts": posts}
                        logAction(staff_account['username'],
                                  "ipshow on {}".format(ip))
                else:
                    # Generate form
                    template_filename = "ipshow.html"
                    template_values = {"mode": 0}
            elif path_split[2] == 'ipdelete':
                if not moderator:
                    return

                # Delete posts by IP
                if 'ip' in self.formdata:
                    # If an IP was given...
                    if self.formdata['ip'] != '':
                        where = []
                        if 'board_all' not in self.formdata:
                            # If multiple boards, add them to a list
                            boards = FetchAll('SELECT `id`, `dir` FROM `boards`')
                            for board in boards:
                                keyname = 'board_' + board['dir']
                                if keyname in self.formdata:
                                    if self.formdata[keyname] == "1":
                                        where.append(board)
                        else:
                            # If all boards were selected, add them all to the list
                            where = FetchAll('SELECT `id`, `dir` FROM `boards`')

                        # If no board was chosen
                        if len(where) <= 0:
                            self.error(_("Select a board first."))
                            return

                        try:
                            secs = int(self.formdata['seconds'])
                        except:
                            secs = 0
                        if secs > 0:
                            since = round(time.time() - secs)

                        deletedPostsTotal = 0
                        ip = self.formdata['ip']
                        deletedPosts = 0
                        for theboard in where:
                            board = setBoard(theboard['dir'])
                            isDeletedOP = False

                            # delete all starting posts first
                            if secs == 0:
                                op_posts = FetchAll("SELECT `id`, `message` FROM posts WHERE parentid = 0 AND boardid = %s AND ip = INET6_ATON(%s)", (board['id'], ip) )
                            else:
                                op_posts = FetchAll("SELECT `id`, `message` FROM posts WHERE parentid = 0 AND boardid = %s AND ip = INET6_ATON(%s) AND timestamp > %s", (board['id'], ip, since) )

                            for post in op_posts:
                                deletePost(post['id'], None)
                                deletedPosts += 1
                                deletedPostsTotal += 1

                            if secs == 0:
                                replies = FetchAll("SELECT `id`, `message`, `parentid` FROM posts WHERE parentid != 0 AND boardid = %s AND ip = INET6_ATON(%s)", (board['id'], ip) )
                            else:
                                replies = FetchAll("SELECT `id`, `message`, `parentid` FROM posts WHERE parentid != 0 AND boardid = %s AND ip = INET6_ATON(%s) AND timestamp > %s", (board['id'], ip, since) )

                            for post in replies:
                                deletePost(post['id'], None, '2')
                                deletedPosts += 1
                                deletedPostsTotal += 1

                            regenerateHome()
                    else:
                        self.error(_("Please enter an IP first."))
                        return

                    if deletedPosts > 0:
                        message = 'En total se eliminaron %(posts)s post(s) de %(ip)s.' % {'posts': str(deletedPosts), 'ip': self.formdata['ip']}
                        logAction(staff_account['username'], '%(posts)s post(s) eliminado(s) de IP: %(ip)s' % {'posts': str(deletedPosts), 'ip': self.formdata['ip']})
                        #logAction(staff_account['username'], '%(posts)s post(s) were deleted from %(board)s. IP: %(ip)s' % \
                        # {'posts': str(deletedPosts),
                        #  'board': '/' + board['dir'] + '/',
                        #  'ip': self.formdata['ip']})
                    else:
                      message = "No se encontraron posts"

                    template_filename = "message.html"
                else:
                    # Show form
                    template_filename = "ipdelete.html"
                    template_values = {'boards': boardlist()}
            elif path_split[2] == 'goto':
                board = setBoard(path_split[3])
                board_type = board['board_type']
                
                try:
                  pid = int(path_split[4])
                except ValueError:
                  raise UserError("ID no válida.")
                
                if board_type == '1':
                  first = get_parent_post(pid, board['id'])
                  url = "/" + board['dir'] + "/read/" + first['timestamp'] + "/"
                else:
                  url = "/" + board['dir'] + "/res/" + str(pid) + ".html"
                    
                self.output = '<meta http-equiv="refresh" content="0;url=%s" />' % url
            elif path_split[2] == 'fixquote':
                # when a mod deletes a post permanently messing with quote numbers
                if not administrator:
                  return

                board = setBoard(path_split[3])
                if board['board_type'] == 1:
                  return
                
                pid = int(path_split[4])
                parent = FetchOne("SELECT `parentid` FROM `posts` \
                  WHERE `id` = %s AND `boardid` = %s LIMIT 1" % (pid, board['id']))
                parent = parent['parentid']
                
                ranges = '`id` >= ' + str(pid)
                if len(path_split) > 5:
                  ranges += ' AND `id` <= ' + path_split[5]
                
                posts = FetchAll('SELECT `id`, `message` FROM `posts` \
                  WHERE `boardid` = %s AND %s AND `parentid` = %s AND \
                  `message` LIKE \'%%">&gt;&gt;%%\'' % (board['id'], ranges, parent))
                
                def fix_num(capture):
                  qnum = str(int(capture.group(2)) - 1)
                  return '/'+board['dir']+'/read/' + capture.group(1) + '/' + qnum + '">&gt;&gt;' + qnum
                
                for post in posts:
                  rx = r'/'+board['dir']+'/read/(\d+)/(\d+)">&gt;&gt;(\d+)'
                  new_msg = re.sub(rx, fix_num, post['message'])
                  self.output += post['message'] + '<hr>' + new_msg + '<hr><hr>'
                  
                  query = "UPDATE `posts` SET `message` = '%s' WHERE `boardid` = %s AND `id` = %s" % (new_msg, board['id'], post['id'])
                  UpdateDb(query)
                
                # reflect changes
                threadUpdated(parent)
            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 256')
                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) AS 'count' FROM `reports`")["count"]
            posts = FetchAll("SELECT * FROM `news` WHERE type = 0 ORDER BY `timestamp` DESC")
            template_filename = "manage.html"
            template_values = {'reports': reports, 'posts': posts}

    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
    expires = timestamp() + Settings.SESSION_TIME

    InsertDb("INSERT INTO `session` (`session_id`, `expires`, `staff_id`) VALUES (UNHEX(%s), %s, %s)",
             (session_uuid, expires, staff_id))

    return session_uuid


def validateSession(session_id):
    cleanSessions()

    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)",
        (session_id,))

    if session:
        return session

    return None


def renewSession(session_id):
    expires = timestamp() + Settings.SESSION_TIME

    UpdateDb("UPDATE `session` SET expires = %s WHERE session_id = UNHEX(%s)", (expires, session_id))


def deleteSession(session_id):
    UpdateDb("DELETE FROM `session` WHERE session_id = UNHEX(%s)", (param_session_id,))


def cleanSessions():
    UpdateDb("DELETE FROM `session` WHERE expires <= %s", (timestamp(),))


def logAction(staff, action):
    InsertDb("INSERT INTO `logs` (`timestamp`, `staff`, `action`) VALUES (%s, %s, %s)", (timestamp(), staff, action))


def genPasswdHash(string):
    import argon2
    ph = argon2.PasswordHasher()

    return ph.hash(string)


def verifyPasswd(username, passwd):
    import argon2
    ph = argon2.PasswordHasher()

    staff_account = FetchOne("SELECT * FROM staff WHERE username = %s", (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