# Pingback plugin for vellum # Implements the Pingback spec: http://www.hixie.ch/specs/pingback/pingback # Requires xmlrpclib: # Included with Python 2.2+ # Otherwise get from http://www.pythonware.com/products/xmlrpc/ # and place in your plugins directory import vellum.hooks,vellum.Entry,vellum.functions,vellum.config import re,sys,os,time,bsddb,pickle import xmlrpclib,urllib2,urlparse from vellum.vellumObject import vellumObject __pluginname__ = "Pingback" __description__ = """Implements the Pingback spec, available at http://www.hixie.ch/specs/pingback/pingback.""" # -------------------------------------------------------------- # Pingback server # -------------------------------------------------------------- class Pingback(vellumObject): id = None url = '' title = '' excerpt = '' entryid = None created = None def save(self): self.setMyID() if not self.created: self.created = time.time() vellumObject.save(self) dbdir = vellum.config.get("DatabaseDir") if not dbdir: raise "There is no DatabaseDir listed in the config file" Pingback.db = bsddb.hashopen(os.path.join(dbdir,"pingbacks"),"c") def all_pingbacks(): return map(get_pingback,Pingback.db.keys()) def get_pingback(id): "Gets one individual pingback by ID" sid = str(id) if Pingback.db.has_key(sid): k,v = Pingback.db.set_location(sid) return pickle.loads(v) else: return None def get_pingbacks(entry): "Gets all pingbacks for the passed entry" ep = filter(lambda x,i=entry.id:str(x.entryid)==str(i),all_pingbacks()) ep.sort(lambda x,y:cmp(y.created,x.created)) return ep # Add the pingbacks() method to Entry vellum.Entry.Entry.pingbacks = get_pingbacks # Add an xmlrpc server to functions def pingbackXMLRPCserver(form): # We should have been called without a cgi object being invoked, # which means that sys.stdin has not been consumed rpc = sys.stdin.read() # Get the passed data # Give it to xmlrpclib data,method = xmlrpclib.loads(rpc) # dispatch appropriately if method == "pingback.ping": receive_ping(data) elif method == "system.getCapabilities": get_capabilities() elif method == "system.listMethods": list_methods() else: f = xmlrpclib.Fault(-32601,"Method '%s' not found" % method) print xmlrpclib.dumps(f) # And terminate so we don't print the vellum headers etc sys.exit(0) vellum.functions.pingback = pingbackXMLRPCserver # Don't need to be authenticated to call the pingback function vellum.functions.pingback.noAuth = 1 # and it wants to parse stdin itself vellum.functions.pingback.noStdinParsing = 1 def receive_ping(data): # Actually receive a ping if type(data) type(()): f = xmlrpclib.Fault(-32602,"You must pass two parameters, the linking URI and the linked-to URI") print xmlrpclib.dumps(f) return if len(data) 2: f = xmlrpclib.Fault(-32602,"You must pass two parameters, the linking URI and the linked-to URI") print xmlrpclib.dumps(f) return linkFrom,linkTo = data # Confirm that linkFrom contains a link to linkTo req = urllib2.Request(linkFrom) try: ufp = urllib2.urlopen(req) except: f = xmlrpclib.Fault(0,"I could not fetch %s for checking" % linkFrom) print xmlrpclib.dumps(f) return content = ufp.read() if content.find(linkTo) == -1: f = xmlrpclib.Fault(17,"%s does not link to %s" % (linkFrom,linkTo)) print xmlrpclib.dumps(f) return # Work out which entry linkTo points to try: import vellum.Blog matchingEntry = None for blog in vellum.Blog.all(): for entry in blog.entries(): if entry.permalink == linkTo: matchingEntry = entry if not matchingEntry: f = xmlrpclib.Fault(33,"%s is not a pingbackable resource" % linkTo) print xmlrpclib.dumps(f) return except: import StringIO,traceback catcherr = StringIO.StringIO() traceback.print_exc(file=catcherr) errtext = catcherr.getvalue() f = xmlrpclib.Fault(0,"Something bad happened while finding the local entry (%s)" % errtext) print xmlrpclib.dumps(f) return # Check that we haven't received this pingback before try: for pb in matchingEntry.pingbacks(): if pb.url == linkFrom: f = xmlrpclib.Fault(48,"This pingback has already been registered") print xmlrpclib.dumps(f) return except: import StringIO,traceback catcherr = StringIO.StringIO() traceback.print_exc(file=catcherr) errtext = catcherr.getvalue() f = xmlrpclib.Fault(0,"Something bad happened while confirming that we haven't already seen this pingback (%s)" % errtext) print xmlrpclib.dumps(f) return # Now that we have an entry, construct a pingback try: pb = Pingback() pb.url = linkFrom pb.entryid = matchingEntry.id title_re = re.compile('(?P<title>[^<]+)',re.I | re.S) titles = title_re.findall(content) if len(titles) == 0: pb.title = linkFrom else: pb.title = titles[0] except: f = xmlrpclib.Fault(0,"Something bad happened while constructing a pingback object") print xmlrpclib.dumps(f) return # and add the pingback try: pb.save() matchingEntry.save() except: import StringIO,traceback catcherr = StringIO.StringIO() traceback.print_exc(file=catcherr) errtext = catcherr.getvalue() f = xmlrpclib.Fault(0,"Something bad happened while saving the pingback (%s)" % errtext) print xmlrpclib.dumps(f) return response = "You pinged %s from %s. Thanks for the heads-up!" % (linkTo,linkFrom) print xmlrpclib.dumps((response,)) return def get_capabilities(): # We have to implement this because we use extended error codes, as stated # by http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php response = { "faults_interop": { "specURL": 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', "specVersion": 20010516 } } print xmlrpclib.dumps((response,)) return def list_methods(): response = ['pingback.ping','system.getCapabilities','system.listMethods'] print xmlrpclib.dumps((response,)) return # -------------------------------------------------------------- # Pingback client # -------------------------------------------------------------- def pingback_send_pings(entry): # REs we need pingbackElement_re = re.compile('',re.I | re.S) link_re = re.compile(']*href="(?P[^"]+)"[^>]*>',re.I | re.S) text = entry.body if entry.extended: text += entry.extended # Parse all links out of the text links = link_re.findall(text) for link in links: try: absoluteLink = urlparse.urljoin(entry.permalink,link) req = urllib2.Request(absoluteLink,None,{"Referer":entry.permalink}) ufp = urllib2.urlopen(req) pingbackServer = None # Look for an X-Pingback header (sect 2.1) hdrs = ufp.info().headers for hdr in hdrs: hl = hdr.split(':',1) if len(hl) == 2: nm,vl = hl if nm.lower() == 'x-pingback': pingbackServer = vl.strip() if not pingbackServer: # Parse the body body = ufp.read() pbElements = pingbackElement_re.findall(body) if len(pbElements)> 0: pingbackServer = pbElements[0] if pingbackServer: # Actually ping the smegger svr = xmlrpclib.Server(pingbackServer) svr.pingback.ping(entry.permalink,absoluteLink) # And ignore the result, heh heh heh ufp.close() except: # Like the raven said, carrion regardless, and # don't throw an error for the user to see pass # Run the send_pings function after rebuilding an entry vellum.hooks.register_hook("entry-rebuild-post",pingback_send_pings)

AltStyle によって変換されたページ (->オリジナル) /