#!/usr/bin/python import sys,os,glob,thread,time,StringIO from optparse import OptionParser #get the path where the script resides (could be different from "current working directory") #this is always the first element in sys.path script_location = sys.path[0] #adding the 'include' directory relative to the location of the script sys.path.append(script_location+os.sep+'include') class hoster_plugin: def __init__(self): self.pluginlist = {} self.instancelist = {} #setting the absolute path to the plugins directory as attribute of the instance self.plugin_path = script_location+os.sep+'plugins' #appending the plugins directory to the search path sys.path.append(self.plugin_path) self._importPlugins() def _importPlugins(self): #fetch a list of all files (and directorys) in the plugin directory matching '*.py' and iterate over it for f in glob.glob(self.plugin_path+os.sep+'*.py'): #check if fetched filename is really a file if os.path.isfile(f): #split directory and filename, strip the last three chars ('.py') filename = os.path.basename(f)[:-3] try: #import all plugin modules and write them in the attribute 'pluginlist' self.pluginlist[filename] = __import__(filename,globals(),locals(),[]) except: continue def createInstances(self): for modul in self.pluginlist.keys(): try: self.instancelist[modul] = self.pluginlist[modul].hoster_class() except: del self.pluginlist[modul] continue def plugin_info(plugin_instance): for name in plugin_instance.instancelist.keys(): plugin_instance.instancelist[name].identify() print '\n' def trigger_error(message,fatal=False): #if verbose mode is not activated we call the write method of the temp stdout directly to print the message if not options.verbose: tmp_stdout.write('Error: %s\n' % message) else: print 'Error: %s' % message if fatal: sys.exit() def read_file(filename): try: #use an temporary file object which gets garbage collected immediatly #no .close() call is necessary link_list = [x.rstrip() for x in open(filename,'r')] except: trigger_error('could not open file',True) return link_list def decrypt_main(link_list,plugin_instance): decrypted_list = [] failure_list = [] temp_list = [] #main decryption loop while link_list != []: hoster_list = [] #try to distribute all given links to the available plugins for name in plugin_instance.instancelist.keys(): for link in link_list: #if the .probe() function returns true we asume that the link can be decrypted with this plugin if plugin_instance.instancelist[name].probe(link): #the master plugin tells us if the link is finally decrypted. in this case append it to the decryptet_list if plugin_instance.instancelist[name].info['master']: decrypted_list.append(link) #is the the plugin not the master then add the link to the url_list attribute from which the decrypt() function will read it later else: plugin_instance.instancelist[name].url_list.append(link) if not name in hoster_list: hoster_list.append(name) #remember all links which could be assigned to a plugin temp_list.append(link) #delete all links from the list which could not be assigned to a plugin link_list = filter(lambda x: x not in temp_list,link_list) temp_list = [] #we asume that every link which cannot be assigned to a plugin cannot be decrypted failure_list.extend(link_list) link_list = [] #start a new thread for every decryption process #TODO: Limit the number of parallel processes exitmutexes = {} for hoster in hoster_list: exitmutexes[hoster] = False thread.start_new(plugin_instance.instancelist[hoster].thread_decrypt, (hoster,exitmutexes)) #Wait until every process has exited while False in exitmutexes.values(): time.sleep(1) pass #get the decrypted data and delete it from the instances for hoster in hoster_list: failure_list.extend(plugin_instance.instancelist[hoster].failure_list) #to support also cascade decryption (different encryption service in a row) we asume that the decrypted links can again be decrypted by another plugin #this circle breaks if the link ist recognized by the master plugin or if it cannot be assigned to another plugin link_list.extend(plugin_instance.instancelist[hoster].decrypted_list) plugin_instance.instancelist[hoster].clear_data() return (decrypted_list,failure_list) def print_result(decrypted_list,failure_list): print '\nsuccessful:' for x in decrypted_list: print x print '\nfailure:' for x in failure_list: print x if __name__ == '__main__': parser = OptionParser(usage='%prog [options] file') parser.add_option('-p', '--plugin-info',action='store_true',dest='plugin_info',default=False, help='display a list of available plugins and exit') parser.add_option('-v', '--verbose', action='store_true',dest='verbose',default=False, help='show more detailed status messages during decryption') options,args=parser.parse_args() #Create list of available filehoster plugins plugins = hoster_plugin() #Create necessary instances plugins.createInstances() #interpreting some given commandline options if options.plugin_info: plugin_info(plugins) sys.exit() #if args is empty we got no file to read from. so display the help message and exit if args == []: parser.print_help() sys.exit() # i'm not sure wether it is necessary to call .destroy explicitly # parser.destroy() #if the verbose mode is not activated we will reroute the standard output stream if not options.verbose: tmp_stdout = sys.stdout sys.stdout = StringIO.StringIO() decrypted_list,failure_list = decrypt_main(read_file(args[0]),plugins) #if we changed the standard output we will now change it back to allow the result being printed out if not options.verbose: sys.stdout = tmp_stdout print_result(decrypted_list,failure_list)