rendered paste body#!/usr/bin/env python# -*- coding: utf-8 -*-# nautilus-pastebin - Nautilus extension to paste a file to a pastebin service# Written by:# Alessio Treglia <quadrispro@ubuntu.com># Copyright (C) 2009, Alessio Treglia## Part of this code has been taken from pastebinit script# pastebinit was written by: # Stéphane Graber <stgraber@stgraber.org># Daniel Bartlett <dan@f-box.org># David Paleino <d.paleino@gmail.com># URL: http://www.stgraber.org/download/projects/pastebin/# Copyright (C) 2006-2009, Stéphane Graber, Daniel Bartlett, David Paleino# Distributed under the terms of the GNU GPL-2 license## This package is free software; you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation; either version 2 of the License, or# (at your option) any later version.## This package is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this package; if not, write to the Free Software# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA#import urllibimport gtkimport nautilusimport pynotifyimport os, sys, re, sys, mimetypesimport gettextfrom ConfigParser import ConfigParsergettext.install("nautilus-pastebin")# GlobalsNAME_APP = "nautilus-pastebin"CONFIG_FILENAME = 'nautilus-pastebin.conf'USERCONFIG_RELPATH = '.config/nautilus-pastebin'PATH_APP = '/usr/share/nautilus-pastebin'LOGGING = Trueclass ConfigFileException(Exception): passpynotify.init("Nautilus pastebin extension")def getConfigurationFilename(): homedir = os.environ.get('HOME') file_conf = '' try: file_conf = os.path.join(homedir, USERCONFIG_RELPATH, CONFIG_FILENAME) os.stat(file_conf) except: file_conf = os.path.join('/etc', CONFIG_FILENAME) return file_confdef getPluginsPath(): homedir = os.environ.get('HOME') plugins_dirs = [] tmp = os.path.join(homedir, USERCONFIG_RELPATH) if os.path.isdir(tmp): plugins_dirs.append(tmp) tmp = os.path.join(PATH_APP, 'plugins') if os.path.isdir(tmp): plugins_dirs.append(tmp) return plugins_dirsdef log(message): if LOGGING: if sys.version.split()[0] < '3': print message else: print (message)# Custom urlopener to handle 401'sclass pasteURLopener(urllib.FancyURLopener): def http_error_401(self, url, fp, errcode, errmsg, headers, data=None): return Noneclass ConfigurationValidator(object): """Configuration wrapper """ NEEDED_OPTS = ['pastebin'] def validate(self): log (_("Validating configuration file...")) conf = {} opts = self.__config.options('generals') # Each needed field must be fill'd. if not set(ConfigurationValidator.NEEDED_OPTS).issubset(set(opts)): raise ConfigFileException, _("Missing some required values in the configuration file.") for k in opts: conf[k] = self.__config.get('generals', k) if k in ConfigurationValidator.NEEDED_OPTS and conf[k] == '': raise ConfigFileException, _("Missing some required values in the configuration file.") # Everything seems well-configured for k,v in conf.items(): setattr(self,k,v) return True def getPastebinConfiguration(self): """Retrieve and returns the configuration related to the pastebin service.""" pastebinConfiguration = None if self.__config.has_section(self.pastebin): pastebinConfiguration = PastebinConfiguration( dict(self.__config.items(self.pastebin)) ) return pastebinConfiguration def reloadConfiguration(self): """Reload the configuration file""" log (_("Reloading configuration...")) self.__config.read(self.filename) def __init__(self, filename='nautilus-pastebin.conf'): log (_("Initializing global configuration validator...")) self.filename = filename self.__config = ConfigParser() self.__config.read(filename)class PastebinConfiguration(object): PARAMETERS = ["website", "content", "user", "jabberid", "version", "format", "parentpid", "permatag", "title", "username", "password"] def __init__(self, optionsDict): log (_("Initializing pastebin configuration...")) # Initialize all parameters for k in PastebinConfiguration.PARAMETERS: setattr(self,k,'') # Read the configuration and set the parameters for k,v in optionsDict.items(): setattr(self,k,v) # Initializing formatMap... formatMap = dict(map(lambda k: (k,''), filter(lambda x: x.startswith('text'), mimetypes.types_map.values()))) for i in formatMap.keys(): # Set 'text' as default format formatMap[i] = 'text' # Read the custom types list from the configuration exec(optionsDict['mimetypes']) self.mimetypes = formatMap def setFormatByMimeType(self, mimetype): try: self.format = self.mimetypes[mimetype] except KeyError: return False # unsupported mimetype return True def __str__(self): s = 'Configuration:\n' for k in PastebinConfiguration.PARAMETERS: s += "%(k)s = %(v)s\n" % {'k' : k, 'v': getattr(self, k)} s += "mimetypes = %s" % self.mimetypes.__str__() return sclass PastebinValidatorFactory(object): """Singleton factory which returns a validator object dinamically.""" __instance = None class __impl(object): """Implementation of the class' methods""" def getValidator(self, pastebinConfiguration): website = pastebinConfiguration.website log (_("Looking for a validator: %s") %website) if (re.search("http://((([a-zA-Z0-9\-_\.]*)(pastebin\.com)))", website) and not website == "http://www.pastebin.com") or website == "http://pastebin.mozilla.org": return PastebinComValidator(pastebinConfiguration) elif re.search("http://((([a-zA-Z0-9\-_\.]*)(pastebin\.ca)))", website): return PastebinCaValidator(pastebinConfiguration) elif website == "http://rafb.net": return RafbNetValidator(pastebinConfiguration) elif website == "http://stikked.com": return StikkedComValidator(pastebinConfiguration) elif website == "http://slexy.org": return SlexyOrgValidator(pastebinConfiguration) elif website == "http://fpaste.org": return FpasteValidator(pastebinConfiguration) elif website == "http://paste2.org": return Paste2OrgValidator(pastebinConfiguration) elif re.search("http://((([a-zA-Z0-9\-_\.]*)(pastey\.net)))", website): return PasteyNetValidator(pastebinConfiguration) elif website == "http://yourpaste.net": return YourpasteNetValidator(pastebinConfiguration) elif website == "http://gist.github.com": return GistGithubComValidator(pastebinConfiguration) elif website == "http://paste.ubuntu.com": return PasteUbuntuComValidator(pastebinConfiguration) elif website == "http://paste.debian.net": return PasteDebianNetValidator(pastebinConfiguration) else: return None # Service has not been recognized def __init__(self): if PastebinValidatorFactory.__instance is None: PastebinValidatorFactory.__instance = PastebinValidatorFactory.__impl() self.__dict__['_PastebinValidatorFactory__instance'] = PastebinValidatorFactory.__instance def __getattr__(self, attr): return getattr(self.__instance, attr) def __setattr__(self, attr, value): return setattr(self.__instance, attr, value)class GenericPastebinValidator(object): NOT_EMPTY_FIELDS = ['website', 'content'] def __init__(self, pastebinConfiguration): if not self.__class__.__name__ == 'GenericPastebinValidator': self.conf = pastebinConfiguration else: raise NotImplementedError def validate(self): if not self.__class__.__name__ == 'GenericPastebinValidator': if not self.checkNotEmpty(['content', 'website']): return False if self.conf.user == '': self.conf.user = os.environ.get('USER') return True else: raise NotImplementedError def checkNotEmpty(self, opts): if not self.__class__.__name__ == 'GenericPastebinValidator': try: for i in GenericPastebinValidator.NOT_EMPTY_FIELDS: if getattr(self.conf, i) == '': raise Exception except: return False return True else: raise NotImplementedError def getParameters(self): raise NotImplementedErrorclass PasteUbuntuComValidator(GenericPastebinValidator): def validate(self): PasteUbuntuComValidator.NOT_EMPTY_FIELDS.append('format') return GenericPastebinValidator.validate(self) def getParameters(self): params = {} params['poster'] = self.conf.user params['syntax'] = self.conf.format params['content'] = self.conf.content return paramsclass PasteDebianNetValidator(GenericPastebinValidator): def validate(self): PasteDebianNetValidator.NOT_EMPTY_FIELDS.append('format') return GenericPastebinValidator.validate(self) def getParameters(self): params = {} params['poster'] = self.conf.user params['lang'] = self.conf.format # "-1" The format, for syntax hilighting, default to plain text #params['syntax'] = self.conf.format #The format, for syntax hilighting params['code'] = self.conf.content params['remember'] = "0" #Do you want a cookie ? params['expire'] = "259200" # expire in 72h return paramsclass GistGithubComValidator(GenericPastebinValidator): def validate(self): GistGithubComValidator.NOT_EMPTY_FIELDS.extend(['format', 'title']) return GenericPastebinValidator.validate(self) def getParameters(self): params = {} params['page'] = "/gists" params['poster'] = self.conf.user params['file_ext[gistfile1]'] = self.conf.format #The format, for syntax hilighting params['file_contents[gistfile1]'] = self.conf.content params['file_name[gistfile1]'] = self.conf.title return paramsclass YourpasteNetValidator(GenericPastebinValidator): def validate(self): YourpasteNetValidator.NOT_EMPTY_FIELDS.extend(['format', 'title']) return GenericPastebinValidator.validate(self) def getParameters(self): params = {} params['syntax'] = self.conf.format params['name'] = self.conf.user params['desc'] = self.conf.title params['expire'] = "0" # Forever params['code'] = self.conf.content params['private'] = "0" # It's not a private post params['remember'] = "0" # Cookies? ;) params['page'] = "/paste" params['regexp'] = '">http://yourpaste.net(.*)/</a>' return paramsclass PasteyNetValidator(GenericPastebinValidator): def parentFixup(self, website, parentid): if parentid == "": return "" url_opener = pasteURLopener() page = url_opener.open(website + '/' + parentid, None) matches = re.split('<input.*?name="parent".*?value="(.*?)"', page.read()) if len(matches) <= 1 or re.match(parentid, matches[1]) == None: # The obfuscated version didn't begin with the partial version, # or unable to find the obfuscated version for some reason! # Create a paste with no parent (should we throw, instead?) return "" return matches[1] def validate(self): PasteyNetValidator.NOT_EMPTY_FIELDS.extend(['format','title']) return GenericPastebinValidator.validate(self) def getParameters(self): params = {} params['author'] = self.conf.user params['subject'] = self.conf.title params['parent'] = self.parentFixup(self.conf.website, self.conf.parentpid) params['text'] = self.conf.content params['language'] = self.conf.format params['paste'] = "Paste" params['page'] = '/submit.php' params['regexp'] = '">http://(?:(?:[a-zA-Z0-9\-_\.]*)(?:pastey\.net))(/.*)</a>' return paramsclass PastebinComValidator(GenericPastebinValidator): def validate(self): PastebinComValidator.NOT_EMPTY_FIELDS.extend(['format','version']) return GenericPastebinValidator.validate(self) def getParameters(self): params = {} params['poster'] = self.conf.user params['code2'] = self.conf.content params['version'] = self.conf.version params['parent_pid'] = self.conf.parentpid #For reply, "" means homepage (new thread) params['format'] = self.conf.format #The format, for syntax hilighting params['paste'] = "Send" params['remember'] = "0" #Do you want a cookie ? params['expiry'] = "f" #The expiration, f = forever return paramsclass PastebinCaValidator(GenericPastebinValidator): def getParameters(self): params = {} params['name'] = self.conf.user params['content'] = self.conf.content params['type'] = "1" #The expiration, 1 = raw params['save'] = "0" #Do you want a cookie ? params['s'] = "Submit Post" params['regexp'] = '">http://.*pastebin.ca/(.*)</a></p><p>' return paramsclass RafbNetValidator(GenericPastebinValidator): def validate(self): RafbNetValidator.NOT_EMPTY_FIELDS.append('format') return GenericPastebinValidator.validate(self) def getParameters(self): params = {} params['page'] = "/paste/paste.php" params['nick'] = self.conf.user params['text'] = self.conf.content params['lang'] = "Plain Text" #The format, for syntax hilighting params['cvt_tabs'] = "No" return paramsclass StikkedComValidator(GenericPastebinValidator): def validate(self): StikkedComValidator.NOT_EMPTY_FIELDS.extend(['title', 'format']) return GenericPastebinValidator.validate(self) def getParameters(self): params = {} params['name'] = self.conf.user params['title'] = self.conf.title params['email'] = "" params['lang'] = self.conf.format #"text" #The format, for syntax hilighting params['code'] = self.conf.content params['website'] = "" params['submit'] = "submit" return paramsclass SlexyOrgValidator(GenericPastebinValidator): def validate(self): SlexyOrgValidator.NOT_EMPTY_FIELDS.extend(['title', 'format']) return GenericPastebinValidator.validate(self) def getParameters(self): params = {} params['page'] = "/index.php/submit" params['raw_paste'] = self.conf.content params['comment'] = "" params['author'] = self.conf.user params['language'] = self.conf.format #"text" #The format, for syntax hilighting params['permissions'] = "0" params['desc'] = self.conf.title params['linenumbers'] = "0" params['expire'] = "0" params['submit'] = "Submit Paste" return paramsclass FpasteValidator(GenericPastebinValidator): def validate(self): FpasteValidator.NOT_EMPTY_FIELDS.append('format') return GenericPastebinValidator.validate(self) def getParameters(self): params = {} params['lang'] = self.conf.format #The format, for syntax hilighting params['page'] = "/paste/save" params['content'] = self.conf.content params['submit'] = "nautilus-pastebin" return paramsclass Paste2OrgValidator(GenericPastebinValidator): def validate(self): Paste2OrgValidator.NOT_EMPTY_FIELDS.extend(['format','title']) return GenericPastebinValidator.validate(self) def getParameters(self): params = {} params['page'] = "/new-paste" params['description'] = self.conf.title params['lang'] = self.conf.format params['code'] = self.conf.content params['parent'] = "0" return paramsclass ValidatorPluginLoader(object): """Experimental feature""" @staticmethod def getValidatorPlugin(name): sys.path.extend(getPluginsPath()) obj = None stmt = """ from %(name)s import %(name)sValidator obj = %(name)sValidator """ exec(stmt % {'name' : name}) return obj def __init__(self): raise NotImplementedErrorclass PastebinitExtension(nautilus.MenuProvider): def __init__(self): log ("Intializing Nautilus Pastebin extension...") def get_file_items(self, window, files): if len(files)!=1: return filename = files[0] validator = ConfigurationValidator(getConfigurationFilename()) validator.validate() pasteconf = validator.getPastebinConfiguration() if not pasteconf.setFormatByMimeType(filename.get_mime_type()): return self.pasteconf = pasteconf items = [] #Called when the user selects a file in Nautilus. item = nautilus.MenuItem("NautilusPython::pastebin_item", _("Pastebin"), _("Pastebin this file")) item.set_property('icon', "nautilus-pastebin") item.connect("activate", self.menu_activate_cb, files) items.append(item) return items def menu_activate_cb(self, menu, files): if len(files) != 1: return filename = files[0] if filename.get_uri_scheme() != 'file': return if filename.is_directory(): return pastefile = urllib.unquote(filename.get_uri()[7:]) log (_("Trying to paste %s") % pastefile) fp = open(pastefile) content = fp.read() fp.close() # OK, let's go... self.pasteconf.content = content validator = PastebinValidatorFactory().getValidator(self.pasteconf) if not validator.validate(): return website = self.pasteconf.website params = validator.getParameters() if not re.search(".*/", website): website += "/" reLink=None tmp_page="" if params.__contains__("page"): website+=params['page'] tmp_page=params['page'] params.__delitem__("page") if params.__contains__("regexp"): reLink=params['regexp'] params.__delitem__("regexp") params = urllib.urlencode(params) #Convert to a format usable with the HTML POST try: url_opener = pasteURLopener() page = url_opener.open(website, params) #Send the informations and be redirected to the final page summary = 'File pasted to: ' pasteurl = '<a href="%(pasteurl)s">%(pasteurl)s</a>' if reLink: #Check if we have to apply a regexp website = website.replace(tmp_page, "") message = pasteurl % { 'pasteurl' : website + "/" + re.split(reLink, page.read())[1] } else: message = pasteurl % { 'pasteurl': page.url } except: summary = _("Unable to read or parse the result page") message = _("It could be a server timeout or a change server side, try later.") finally: n = pynotify.Notification(summary, message) n.show()