#!/usr/bin/python2.7 from subprocess import call, Popen, PIPE import urllib import urllib2 import os import sys import random import re import time urlencode = urllib.urlencode urlopen = urllib2.urlopen Request = urllib2.Request WORKING_DIR = "/tmp" BIN_CONVERT = "/usr/bin/convert" BIN_IDENTIFY = "/usr/bin/identify" DEFAULT_FINALFORMAT = "png"; SUBTLE_BREAK_MARK = 'pron' EXTREME_BREAK_MARK = 'sugar' HEADER_OFFSET = 5000 MAX_SIZE = 1024 * 1024 * 1.2 * 1.5 # 'CLASSIC':'jpg', # 'REDUX':'pcds', # 'BLURRY_BREAK':'viff', # 'BLURRY_BREAK_2':'mat', # 'SWIPE':'miff', # 'RGB_WASH':'psd', # 'RGB_WASH_2':'psb', # 'NOISY_BREAK':'palm', # 'NOISY_BREAK_2':'fig', # 'BROKEN_VIGNETTE':'pbm', # 'FAX_MACHINE':'cals', # 'STRIPES':'exr', # 'PHOTOCOPY':'art', TEST_PARAMS = { "url" : "http://i.asdf.us/im/27/1424816234661dumpfmpfifferkinggr_1424816412_pfifferking.gif" , "breaktype" : "RGB_WASH", "finalformat" : "png", "breakmode" : "extreme", "breakangle" : "10", "username" : "donkey", "expanded" : "false" } def bool_correct(s): if re.match(r'^false$', s, re.IGNORECASE): return False elif re.match(r'^true$', s, re.IGNORECASE): return True else: return s #{{{Utility functions class dotdict(dict): """dot.notation access to dictionary attributes""" def __getattr__(self, attr): return self.get(attr) __setattr__= dict.__setitem__ __delattr__= dict.__delitem__ def sanitize (str): return re.sub(r'\W+', '', str) def now(): return int(time.time()) def browser_request (url, data=None): headers = { 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)', 'Accept': '*/*', } try: req = Request(url, data, headers) response = urlopen(req) except IOError, e: if hasattr(e, 'code'): sys.stderr.write( '%s - ERROR %s' % (url, e.code) ) raise; return None else: return response def download(url, destination, max_size=MAX_SIZE): response = browser_request(url, None) rawimg = response.read() if len(rawimg) == 0: sys.stderr.write("got zero-length file") raise; if len(rawimg) > max_size: sys.stderr.write("file too big: max size {} KB / {} is {} KB".format( str(MAX_SIZE/1024), destination, str(len(rawimg)/1024) )) raise; f = open(destination, "w") f.write(rawimg) f.close() def dimensions (filepath): #works in lieu of a mimetype check (it reads the header as well) ident = (Popen([BIN_IDENTIFY, filepath], stdout=PIPE).communicate()[0]).split(" ") return ident[2].split("x") def file_size (filepath): try: return os.stat(file)[6] except Exception as e: sys.stderr.write(str(e)) raise; def gif_frames(filepath): try: info = Popen([BIN_IDENTIFY,filepath], stdout=PIPE).communicate()[0] frames = filter((lambda x: x), map( (lambda x: x.split(" ")[0]), (info).split('\n') )) return frames except Exception as e: sys.stderr.write(str(e)) raise; #}}} class Breaker(): def __init__(self, params): self.params = {} self.now = now() self.tag = "imBreak" self.commands = []; self._required_keys = [ "url", "breaktype", "finalformat", "breakmode", "breakangle", "username", "expanded" ] self.files_created = [] for k in self._required_keys: if k in params: if k == 'breaktype': self.params['breaktype'] = self._get_breaktype(params[k]) elif k == 'url': self.params[k] = params[k] else: self.params[k] = bool_correct(sanitize(params[k])) else: self.params[k] = False; self.params = dotdict(self.params) self.basename, self._first_format = self._get_filename(); self._downloaded_file = os.path.join(WORKING_DIR, "IMBREAKTMP{}.{}".format(self.basename, self._first_format)) # same here try: download(self.params.url, self._downloaded_file) self.files_created.append(self._downloaded_file) except Exception as e: sys.stderr.write(str(e)) raise; self._gif_frames = gif_frames(self._downloaded_file) self._gif_frames = self._gif_frames if len(self._gif_frames) > 1 else False self.width, self.height = dimensions(self._downloaded_file) # same here if not self.params.finalformat: self.params.finalformat = DEFAULT_FINALFORMAT if self._gif_frames: self.params.finalformat = 'gif' if self.params.breaktype == 'miff': self.params.finalformat = 'jpg' self.params.breakmode = 'subtle' #final filepath is stored in self.filepath self.filename = "{}.{}".format(self.basename, self.params.finalformat) self.filepath = os.path.join(WORKING_DIR, self.filename) #lets go back to this in a second self._conversion_file = os.path.join(WORKING_DIR, "IMBREAKTMP{}.{}".format(self.basename, self.params.breaktype)) # this def _call_cmd(self, cmd, error=""): try: call(cmd) self.commands.append(" ".join(cmd)); except Exception as e: if error: sys.stderr.write(error) else: sys.stderr.write(str(e)) def _get_breaktype(self, key): #{{{ conversion table breaktypeTranslate = { 'CLASSIC':'jpg', 'REDUX':'pcds', 'BLURRY_BREAK':'viff', 'BLURRY_BREAK_2':'mat', 'SWIPE':'miff', 'RGB_WASH':'psd', 'RGB_WASH_2':'psb', 'NOISY_BREAK':'palm', 'NOISY_BREAK_2':'fig', 'BROKEN_VIGNETTE':'pbm', 'FAX_MACHINE':'cals', 'STRIPES':'exr', 'PHOTOCOPY':'art', } #}}} return breaktypeTranslate[key] def _get_filename (self): url = self.params.url name_part = ""; file_format = ""; if "?" in url: url = url.split("?")[0] if "/" in url: url = urllib.unquote(url).replace(" ","") name_part = url.split("/")[-1] try: parts = name_part.split(".") name_part = sanitize(parts[-2]) file_format = sanitize(parts[-1]) if not name_part or not file_format: sys.stderr.write( "Incompatible input file type") raise; except Exception as e: sys.stderr.write( "Incompatible input file type") raise; else: sys.stderr.write( "Incompatible url") raise; if (len(name_part) > 20): name_part = name_part[:-20] return "{}{}_{}_{}".format(self.tag, name_part, self.now, self.params.username or ""), file_format #{{{#########rotatefunctions####################################### def _rotate(self): cmd = [BIN_CONVERT,self._downloaded_file,"-rotate",self.params.breakangle,"+repage",self._downloaded_file] self._call_cmd(cmd) def _rotate_back(self): angle = str(360-int(self.params.breakangle)) cmd = [BIN_CONVERT,self.filepath,"-rotate",angle,"+repage",self.filepath] self._call_cmd(cmd) if not self.params.expanded: cmd = [BIN_CONVERT,self.filepath,"-gravity","Center","-crop","{}x{}+0+0".format( self.width, self.height),"+repage",self.filepath] self._call_cmd(cmd) #}}} def _subtle_break(self): #assume the header is no longer than HEADER_OFFSET bytes breakpoint = random.randint(HEADER_OFFSET, len(self.file_data)) newfile = "" newfile = self.file_data[0:breakpoint]; newfile += SUBTLE_BREAK_MARK; newfile += self.file_data[breakpoint:] self.file_data = newfile[0:len(self.file_data)] def _extreme_break(self): increment = len(self.file_data)/10; i = 0 newfile = ""; for b in self.file_data: if i > HEADER_OFFSET and not (i % increment): b += EXTREME_BREAK_MARK newfile += b i += 1 self.file_data = newfile[0:len(self.file_data)] def _choose_frame(self): frame = random.choice(self._gif_frames) cmd = [BIN_CONVERT, frame, self._downloaded_file] self._call_cmd(cmd) def _enforce_jpg(self): if self.params.breaktype in [ "exr", "bmp", "miff" ] and not re.match(r'jpe?g', self._first_format, re.IGNORECASE): jpg_file = os.path.join(WORKING_DIR, "{}.{}".format(self.basename, "jpg")) cmd = [BIN_CONVERT,self._downloaded_file,jpg_file] self._call_cmd(cmd) cmd = ["rm",self._downloaded_file] self._call_cmd(cmd) def _first_conversion(self): if self._first_format == self.params.breaktype: self._downloaded_file = self._conversion_file return cmd = [BIN_CONVERT, self._downloaded_file, self._conversion_file] self._call_cmd(cmd) self.files_created.append(self._conversion_file) def _read_data(self, filepath): f = open(filepath, 'r'); data = f.read() f.close() return data def _prepare_filedata(self): if self._gif_frames: self._choose_frame() if self.params.breakangle: self._rotate() self._enforce_jpg(); self._first_conversion(); self.file_data = self._read_data(self._conversion_file) if not self.file_data: sys.stderr.write("Unable to get file_data") raise; def _add_false_data(self, breakmode): if breakmode == "subtle": self._subtle_break() elif breakmode == "extreme": self._extreme_break() f = open(self._conversion_file, 'w') f.write(self.file_data) f.close(); #{{{ SHRINK (UNUSED) def _shrink(self): cmd = [ BIN_CONVERT, "-resize", "500x500", self._downloaded_file, self._downloaded_file ]; self._call_cmd(cmd) #}}} def _final_conversion(self): cmd = [BIN_CONVERT, self._conversion_file, self.filepath] self._call_cmd(cmd) def psd_psbfilepath(num): return os.path.join(WORKING_DIR, "{}-{}.{}".format(self.basename, num, self.params.finalformat)) if self.params.breaktype == 'psd': cmd = ['mv', psd_psbfilepath(1), self.filepath] self._call_cmd(cmd) self.files_created.append(psd_psbfilepath(0)) if self.params.breaktype == 'psb': cmd = ['mv', psd_psbfilepath(0), self.filepath] self._call_cmd(cmd) self.files_created.append(psd_psbfilepath(1)) if self.params.breakangle: self._rotate_back() def _cleanup(self): cmd = ["rm"]+self.files_created self._call_cmd(cmd) def create(self, breakmode=""): if not breakmode: breakmode = self.params.breakmode self._prepare_filedata(); self._add_false_data(breakmode); self._final_conversion() self._cleanup() if __name__ == "__main__": b = Breaker(TEST_PARAMS) b.create(); print b.filepath