aboutsummaryrefslogblamecommitdiff
path: root/cgi/framework.py
blob: 898d6a16562a0ab5f001c97b12cfe4093f9f2fbf (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










               
              




                               
 
                  





                                                                               
                                             








                                                                                                                                                                                            




                            
 
                             







                                                                         
 




                                                                                                







                                                         

                     











                                                     
         

                                

                       












                                                   
                    
                                                               
                                                                                                

                               
 

                                                      
 
 
                   




                                

 
                









                                                         
                     

                      
                                                 

                                          

                

 
                          













                                                                                                                                        

                      







                                                                                 

                                   

































                                                                        

                                   




                                                                    

                                       

                                                  

                          










                                                                    
                      




































                                                                                
                             



                                                                     

                           

        

                          




                                                      
                 




                        


                                   
 
                                         






                                                           

                   


















                                                                 

                            






                                           
                   







                                         


                               
 
                              




                                                              

                                  


                               

                                                                                                   







                                                    
                           




























                                                                                                   

                            













































                                                                                               

                         



                                                              

                         



                                                         

                      
















                                                               

                               














                                                    
# coding=utf-8
import os
import cgi
import datetime
import time
import hashlib
import pickle
import socket
import _mysql
import urllib
import re
import logging
from Cookie import SimpleCookie

from settings import Settings
from database import *


def setBoard(dir):
    """
    Sets the board which the script is operating on by filling Settings._.BOARD
    with the data from the db.
    """
    if not dir:
        raise UserError, _("The specified board is invalid.")
    logging.debug("Seteando el board " + dir)
    board = FetchOne(
        "SELECT * FROM `boards` WHERE `dir` = '%s' LIMIT 1" % _mysql.escape_string(dir))
    if not board:
        raise UserError, _("The specified board is invalid.")

    board["filetypes"] = FetchAll(
        "SELECT * FROM `boards_filetypes` INNER JOIN `filetypes` ON filetypes.id = boards_filetypes.filetypeid WHERE `boardid` = %s ORDER BY `ext` ASC" % _mysql.escape_string(board['id']))
    board["filetypes_ext"] = [filetype['ext']
                              for filetype in board['filetypes']]

    Settings._.BOARD = board

    return board


def cleanDir(path, ext=None):
    if ext:
        filelist = [f for f in os.listdir(path) if f.endswith("." + ext)]
    else:
        filelist = os.listdir(path)

    for f in filelist:
        os.remove(os.path.join(path, f))


def addressIsBanned(ip, board, blind_only=False):
    query = "SELECT * FROM `bans` WHERE INET6_ATON('"+str(ip)+"') BETWEEN `ipstart` AND `ipend`"
    if blind_only:
        query += " AND `blind` = '1'"
    bans = FetchAll(query)
    for ban in bans:
        if ban["boards"] != "":
            boards = pickle.loads(ban["boards"])
        if ban["boards"] == "" or board in boards:
            if board not in Settings.EXCLUDE_GLOBAL_BANS:
                return True
    return False


def addressIsTor(ip):
    if Settings._.IS_TOR is None:
        res = False
        nodes = []
        if ip == '127.0.0.1':  # Tor proxy address
            res = True
        else:
            with open('tor.txt') as f:
                nodes = [line.rstrip() for line in f]
            if ip in nodes:
                res = True
        Settings._.IS_TOR = res
        return res
    else:
        return Settings._.IS_TOR


def addressIsProxy(ip):
    if Settings._.IS_PROXY is None:
        res = False
        proxies = []
        with open('proxy.txt') as f:
            proxies = [line.rstrip() for line in f]
        if ip in proxies:
            res = True
        Settings._.IS_PROXY = res
        return res
    else:
        return Settings._.IS_PROXY


def addressIsES(ip):
    ES = ['AR', 'BO', 'CL', 'CO', 'CR', 'CU', 'EC', 'ES', 'GF',
          'GY', 'GT', 'HN', 'MX', 'NI', 'PA', 'PE', 'PY', 'PR', 'SR', 'UY', 'VE', 'v6']  # 'BR',
    return getCountry(ip) in ES


def addressIsBannedCountry(ip):
    return getCountry(ip) in Settings.BANNED_COUNTRIES


def getCountry(ip):
    if ":" in ip:
        return "v6"
    else:
        import geoip
        return geoip.country(ip)


def getHost(ip):
    if Settings._.HOST is None:
        try:
            Settings._.HOST = socket.gethostbyaddr(ip)[0]
            return Settings._.HOST
        except socket.herror:
            return None
    else:
        return Settings._.HOST


def hostIsBanned(ip):
    host = getHost(ip)
    if host:
        for banned_host in Settings.BANNED_HOSTS:
            if host.endswith(banned_host):
                return True
    
    return False


def updateBoardSettings():
    """
    Pickle the board's settings and store it in the configuration field
    """
    board = Settings._.BOARD
    #UpdateDb("UPDATE `boards` SET `configuration` = '%s' WHERE `id` = %s LIMIT 1" % (_mysql.escape_string(configuration), board["id"]))

    del board["filetypes"]
    del board["filetypes_ext"]
    post_values = ["`" + _mysql.escape_string(str(key)) + "` = '" + _mysql.escape_string(
        str(value)) + "'" for key, value in board.iteritems()]

    UpdateDb("UPDATE `boards` SET %s WHERE `id` = '%s' LIMIT 1" %
             (", ".join(post_values), board["id"]))


def timestamp(t=None):
    """
    Create MySQL-safe timestamp from the datetime t if provided, otherwise create
    the timestamp from datetime.now()
    """
    if not t:
        t = datetime.datetime.now()
    return int(time.mktime(t.timetuple()))


def formatDate(t=None, home=False):
    """
    Format a datetime to a readable date
    """
    if not t:
        t = datetime.datetime.now()

    days = {'en': ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'],
            'es': ['lun', 'mar', 'mie', 'jue', 'vie', 'sab', 'dom'],
            'jp': ['月', '火', '水', '木', '金', '土', '日']}

    daylist = days[Settings.LANG]
    format = "%d/%m/%y(%a)%H:%M:%S"

    if not home:
        try:
            board = Settings._.BOARD
            if board["dir"] == 'world':
                daylist = days['en']
            elif board["dir"] == '2d':
                daylist = days['jp']
        except:
            pass

    t = t.strftime(format)

    t = re.compile(r"mon", re.DOTALL | re.IGNORECASE).sub(daylist[0], t)
    t = re.compile(r"tue", re.DOTALL | re.IGNORECASE).sub(daylist[1], t)
    t = re.compile(r"wed", re.DOTALL | re.IGNORECASE).sub(daylist[2], t)
    t = re.compile(r"thu", re.DOTALL | re.IGNORECASE).sub(daylist[3], t)
    t = re.compile(r"fri", re.DOTALL | re.IGNORECASE).sub(daylist[4], t)
    t = re.compile(r"sat", re.DOTALL | re.IGNORECASE).sub(daylist[5], t)
    t = re.compile(r"sun", re.DOTALL | re.IGNORECASE).sub(daylist[6], t)
    return t


def formatTimestamp(t, home=False):
    """
    Format a timestamp to a readable date
    """
    return formatDate(datetime.datetime.fromtimestamp(int(t)), home)


def timeTaken(time_start, time_finish):
    return str(round(time_finish - time_start, 3))


def parseIsoPeriod(t_str):
    m = re.match('P(?:(\d+)D)?T(?:(\d+)H)?(?:(\d+)M)?(\d+)S', t_str)
    if m:
        grps = [x for x in m.groups() if x]
        if len(grps) == 1:
            grps.insert(0, '0')
        grps[-1] = grps[-1].zfill(2)
        return ':'.join(grps)
    else:
        return '???'


def getFormData(self):
    """
    Process input sent to WSGI through a POST method and output it in an easy to
    retrieve format: dictionary of dictionaries in the format of {key: value}
    """
    # This must be done to avoid a bug in cgi.FieldStorage
    self.environ.setdefault("QUERY_STRING", "")
    if self.environ["QUERY_STRING"] == "rawpost":
        return None
    wsgi_input = self.environ["wsgi.input"]
    post_form = self.environ.get("wsgi.post_form")
    if (post_form is not None
            and post_form[0] is wsgi_input):
        return post_form[2]
    fs = cgi.FieldStorage(fp=wsgi_input,
                          environ=self.environ,
                          keep_blank_values=1)
    new_input = InputProcessed()
    post_form = (new_input, wsgi_input, fs)
    self.environ["wsgi.post_form"] = post_form
    self.environ["wsgi.input"] = new_input

    try:
        formdata = {}
        for key in dict(fs):
            try:
                formdata.update({key: fs[key].value})
                if key == "file":
                    formdata.update(
                        {"file_original": secure_filename(fs[key].filename)})
            except AttributeError:
                formdata.update({key: fs[key]})

        return formdata
    except TypeError:
        return fs


class InputProcessed(object):
    def read(self):
        raise EOFError("El stream de wsgi.input ya se ha consumido.")
    readline = readlines = __iter__ = read


class UserError(Exception):
    pass


def secure_filename(path):
    split = re.compile(r'[\0%s]' % re.escape(
        ''.join([os.path.sep, os.path.altsep or ''])))
    return cgi.escape(split.sub('', path))


def getMD5(data):
    m = hashlib.md5()
    m.update(data)

    return m.hexdigest()


def nullstr(len): return "\0" * len


def hide_data(data, length, key, secret):
    """
    Encrypts data, useful for tripcodes and IDs
    """
    crypt = rc4(nullstr(length), rc4(
        nullstr(32), key + secret) + data).encode('base64')
    return crypt.rstrip('\n')


def rc4(data, key):
    """
    rc4 implementation
    """
    x = 0
    box = range(256)
    for i in range(256):
        x = (x + box[i] + ord(key[i % len(key)])) % 256
        box[i], box[x] = box[x], box[i]
    x = 0
    y = 0
    out = []
    for char in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        out.append(chr(ord(char) ^ box[(box[x] + box[y]) % 256]))

    return ''.join(out)


def getRandomLine(filename):
    import random
    f = open(filename, 'r')
    lines = f.readlines()
    num = random.randint(0, len(lines) - 1)
    return lines[num]


def getRandomIco():
    from glob import glob
    from random import choice
    icons = glob("../static/ico/*")
    if icons:
        return choice(icons).lstrip('..')
    else:
        return ''


def N_(message): return message


def getCookie(self, value=""):
    try:
        return urllib.unquote_plus(self._cookies[value].value)
    except KeyError:
        return None


def reCookie(self, key, value=""):
    board = Settings._.BOARD
    setCookie(self, key, value)


def setCookie(self, key, value="", max_age=None, expires=None, path="/", domain=None, secure=None):
    """
    Copied from Colubrid
    """
    if self._newcookies is None:
        self._newcookies = SimpleCookie()
    self._newcookies[key] = urllib.quote_plus(value)
    if not max_age is None:
        self._newcookies[key]["max-age"] = max_age
    if not expires is None:
        if isinstance(expires, basestring):
            self._newcookies[key]["expires"] = expires
            expires = None
        elif isinstance(expires, datetime):
            expires = expires.utctimetuple()
        elif not isinstance(expires, (int, long)):
            expires = datetime.datetime.gmtime(expires)
        else:
            raise ValueError("Se requiere de un entero o un datetime")
        if not expires is None:
            now = datetime.datetime.gmtime()
            month = _([N_("Jan"), N_("Feb"), N_("Mar"), N_("Apr"), N_("May"), N_("Jun"), N_("Jul"),
                       N_("Aug"), N_("Sep"), N_("Oct"), N_("Nov"), N_("Dec")][now.tm_mon - 1])
            day = _([N_("Monday"), N_("Tuesday"), N_("Wednesday"), N_("Thursday"),
                     N_("Friday"), N_("Saturday"), N_("Sunday")][expires.tm_wday])
            date = "%02d-%s-%s" % (
                now.tm_mday, month, str(now.tm_year)[-2:]
            )
            d = "%s, %s %02d:%02d:%02d GMT" % (day, date, now.tm_hour,
                                               now.tm_min, now.tm_sec)
            self._newcookies[key]["expires"] = d
    if not path is None:
        self._newcookies[key]["path"] = path
    if not domain is None:
        if domain != "THIS":
            self._newcookies[key]["domain"] = domain
    if not secure is None:
        self._newcookies[key]["secure"] = secure


def deleteCookie(self, key):
    """
    Copied from Colubrid
    """
    if key not in self._cookies:
        return  # Cookie doesn't exist
    if self._newcookies is None:
        self._newcookies = SimpleCookie()
    self._newcookies[key] = ""
    if self._cookies[key]["path"]:
        self._newcookies[key]["path"] = self._cookies[key]["path"]
    else:
        self._newcookies[key]["path"] = "/"
    self._newcookies[key]["domain"] = self._cookies[key]["domain"]
    self._newcookies[key]["expires"] = "Thu, 01 Jan 1970 00:00:00 GMT"


def elapsed_time(seconds, suffixes=['y', 'w', 'd', 'h', 'm', 's'], add_s=False, separator=' '):
    """
    Takes an amount of seconds and turns it into a human-readable amount of time.
    """
    # the formatted time string to be returned
    time = []

    # the pieces of time to iterate over (days, hours, minutes, etc)
    # - the first piece in each tuple is the suffix (d, h, w)
    # - the second piece is the length in seconds (a day is 60s * 60m * 24h)
    parts = [(suffixes[0], 60 * 60 * 24 * 7 * 52),
             (suffixes[1], 60 * 60 * 24 * 7),
             (suffixes[2], 60 * 60 * 24),
             (suffixes[3], 60 * 60),
             (suffixes[4], 60),
             (suffixes[5], 1)]

    # for each time piece, grab the value and remaining seconds, and add it to
    # the time string
    for suffix, length in parts:
        value = seconds / length
        if value > 0:
            seconds = seconds % length
            time.append('%s%s' % (str(value),
                                  (suffix, (suffix, suffix + 's')[value > 1])[add_s]))
        if seconds < 1:
            break

    return separator.join(time)


def inet_aton(ip_string):
    import socket
    import struct
    return struct.unpack('!L', socket.inet_aton(ip_string))[0]


def inet_ntoa(packed_ip):
    import socket
    import struct
    return socket.inet_ntoa(struct.pack('!L', packed_ip))


def is_bad_proxy(pip):
    import urllib2
    import socket
    socket.setdefaulttimeout(3)

    try:
        proxy_handler = urllib2.ProxyHandler({'http': pip})
        opener = urllib2.build_opener(proxy_handler)
        opener.addheaders = [('User-agent', 'Mozilla/5.0')]
        urllib2.install_opener(opener)
        req = urllib2.Request('http://bienvenidoainternet.org')
        sock = urllib2.urlopen(req)
    except urllib2.HTTPError, e:
        return e.code
    except Exception, detail:
        return True
    return False


def send_mail(subject, srcmsg):
    import smtplib
    from email.mime.text import MIMEText

    msg = MIMEText(srcmsg)
    me = 'weabot@bienvenidoainternet.org'
    you = 'burocracia@bienvenidoainternet.org'

    msg['Subject'] = 'The contents of %s' % textfile
    msg['From'] = me
    msg['To'] = you

    s = smtplib.SMTP('localhost')
    s.sendmail(me, [you], msg.as_string())
    s.quit()