diff options
Diffstat (limited to 'cgi/framework.py')
-rw-r--r-- | cgi/framework.py | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/cgi/framework.py b/cgi/framework.py new file mode 100644 index 0000000..4c89bb7 --- /dev/null +++ b/cgi/framework.py @@ -0,0 +1,467 @@ +# coding=utf-8 +import os +import cgi +import datetime +import time +import hashlib +import pickle +import socket +import _mysql +import urllib +import re +from Cookie import SimpleCookie + +from settings import Settings +from database import * + +class CLT(datetime.tzinfo): + """ + Clase para zona horaria chilena. + Como el gobierno nos tiene los horarios de verano para la pura cagá, + por mientras dejo el DST como un boolean. Cuando lo fijen, dejarlo automático. + """ + def __init__(self): + self.isdst = False + + def utcoffset(self, dt): + #return datetime.timedelta(hours=-3) + self.dst(dt) + return datetime.timedelta(hours=Settings.TIME_ZONE) + + def dst(self, dt): + if self.isdst: + return datetime.timedelta(hours=1) + else: + return datetime.timedelta(0) + + def tzname(self,dt): + return "GMT -3" + +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.") + logTime("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']] + logTime("Board seteado.") + + Settings._.BOARD = board + + return board + +def addressIsBanned(ip, board): + packed_ip = inet_aton(ip) + bans = FetchAll("SELECT * FROM `bans` WHERE (`netmask` IS NULL AND `ip` = '"+str(packed_ip)+"') OR (`netmask` IS NOT NULL AND '"+str(packed_ip)+"' & `netmask` = `ip`)") + logTime("SELECT * FROM `bans` WHERE (`netmask` IS NULL AND `ip` = '"+str(packed_ip)+"') OR (`netmask` IS NOT NULL AND '"+str(packed_ip)+"' & `netmask` = `ip`)") + 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'] # 'BR', + return getCountry(ip) in ES + +def getCountry(ip): + 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: + banned_hosts = [] + for banned_host in banned_hosts: + if host.endswith(banned_host): + return True + return False + else: + 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(CLT()) + # Timezone fix + #t += datetime.timedelta(hours=1) + + 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), CLT()), 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} + """ + 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] + # This must be done to avoid a bug in cgi.FieldStorage + self.environ.setdefault("QUERY_STRING", "") + 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 + + 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 + +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=""): + return urllib.unquote_plus(self._cookies[value].value) + +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._cookies is None: + self._cookies = SimpleCookie() + self._cookies[key] = urllib.quote_plus(value) + if not max_age is None: + self._cookies[key]["max-age"] = max_age + if not expires is None: + if isinstance(expires, basestring): + self._cookies[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._cookies[key]["expires"] = d + if not path is None: + self._cookies[key]["path"] = path + if not domain is None: + if domain != "THIS": + self._cookies[key]["domain"] = domain + else: + self._cookies[key]["domain"] = Settings.DOMAIN + if not secure is None: + self._cookies[key]["secure"] = secure + +def deleteCookie(self, key): + """ + Copied from Colubrid + """ + if self._cookies is None: + self._cookies = SimpleCookie() + if not key in self._cookies: + self._cookies[key] = "" + self._cookies[key]["max-age"] = 0 + +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, struct + return struct.unpack('!L',socket.inet_aton(ip_string))[0] + +def inet_ntoa(packed_ip): + import socket, 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() + +class weabotLogger: + def __init__(self): + self.times = [] + + def log(self, message): + self.times.append([time.time(), message]) + + def allTimes(self): + output = "Time Logged action\n--------------------------\n" + start = self.times[0][0] + for time in self.times: + difference = str(time[0] - start) + difference_split = difference.split(".") + if len(difference_split[0]) < 2: + difference_split[0] = "0" + difference_split[0] + + if len(difference_split[1]) < 7: + difference_split[1] = ("0" * (7 - len(difference_split[1]))) + difference_split[1] + elif len(difference_split[1]) > 7: + difference_split[1] = difference_split[1][:7] + + output += ".".join(difference_split) + " " + time[1] + "\n" + + return output + +logger = weabotLogger() +def logTime(message): + global logger + logger.log(message) + +def logTimes(): + global logger + return logger.allTimes() |