"""$Id$"""
__author__ = "Sam Ruby and Mark Pilgrim "
__version__ = "$Revision$"
__copyright__ = "Copyright (c) 2002 Sam Ruby and Mark Pilgrim"
from base import validatorBase
from validators import *
#
# Atom link element
#
class link(nonblank,xmlbase,iso639,nonhtml,nonNegativeInteger,rfc3339,nonblank):
validRelations = [
# http://www.iana.org/assignments/link-relations.html
'alternate', # RFC4287
'current', # RFC5005
'describedby', # http://www.w3.org/TR/powder-dr/#assoc-linking
'edit', # RFC-ietf-atompub-protocol-17.txt
'edit-media', # RFC-ietf-atompub-protocol-17.txt
'enclosure', # RFC4287
'first', # RFC5005
'hub', # http://pubsubhubbub.googlecode.com/
'last', # RFC5005
'license', # RFC4946
'next', # RFC5005
'next-archive', # RFC5005
'payment', # Kinberg
'prev-archive', # RFC5005
'previous', # RFC5005
'related', # RFC4287
'replies', # RFC4685
'self', # RFC4287
'service', # Snell
'up', # Slater
'via' # RFC4287
]
rfc5005 = [
'current', # RFC5005
'first', # RFC5005
'last', # RFC5005
'next', # RFC5005
'next-archive', # RFC5005
'prev-archive', # RFC5005
'previous', # RFC5005
]
def getExpectedAttrNames(self):
return [(None, u'type'), (None, u'title'), (None, u'rel'),
(None, u'href'), (None, u'length'), (None, u'hreflang'),
(u'http://www.w3.org/1999/02/22-rdf-syntax-ns#', u'type'),
(u'http://www.w3.org/1999/02/22-rdf-syntax-ns#', u'resource'),
(u'http://purl.org/syndication/thread/1.0', u'count'),
(u'http://purl.org/syndication/thread/1.0', u'when'),
(u'http://purl.org/syndication/thread/1.0', u'updated')]
def validate(self):
self.type = ""
self.rel = "alternate"
self.href = ""
self.hreflang = ""
self.title = ""
if self.attrs.has_key((None, "rel")):
self.value = self.rel = self.attrs.getValue((None, "rel"))
if self.rel.startswith('http://www.iana.org/assignments/relation/'):
self.rel=self.rel[len('http://www.iana.org/assignments/relation/'):]
if self.rel in self.validRelations:
self.log(ValidAtomLinkRel({"parent":self.parent.name, "element":self.name, "attr":"rel", "value":self.rel}))
elif rfc2396_full.rfc2396_re.match(self.rel.encode('idna')):
self.log(ValidAtomLinkRel({"parent":self.parent.name, "element":self.name, "attr":"rel", "value":self.rel}))
else:
self.log(UnregisteredAtomLinkRel({"parent":self.parent.name, "element":self.name, "attr":"rel", "value":self.rel}))
nonblank.validate(self, errorClass=AttrNotBlank, extraParams={"attr": "rel"})
if self.rel in self.rfc5005 and self.parent.name == 'entry':
self.log(FeedHistoryRelInEntry({"rel":self.rel}))
if self.attrs.has_key((None, "type")):
self.value = self.type = self.attrs.getValue((None, "type"))
if not mime_re.match(self.type):
self.log(InvalidMIMEType({"parent":self.parent.name, "element":self.name, "attr":"type", "value":self.type}))
elif self.rel == "self" and self.type not in ["application/atom+xml", "application/rss+xml", "application/rdf+xml"]:
self.log(SelfNotAtom({"parent":self.parent.name, "element":self.name, "attr":"type", "value":self.type}))
else:
self.log(ValidMIMEAttribute({"parent":self.parent.name, "element":self.name, "attr":"type", "value":self.type}))
if self.attrs.has_key((None, "title")):
self.log(ValidTitle({"parent":self.parent.name, "element":self.name, "attr":"title"}))
self.value = self.title = self.attrs.getValue((None, "title"))
nonblank.validate(self, errorClass=AttrNotBlank, extraParams={"attr": "title"})
nonhtml.validate(self)
if self.attrs.has_key((None, "length")):
self.name = 'length'
self.value = self.attrs.getValue((None, "length"))
nonNegativeInteger.validate(self)
nonblank.validate(self)
if self.attrs.has_key((None, "hreflang")):
self.name = 'hreflang'
self.value = self.hreflang = self.attrs.getValue((None, "hreflang"))
iso639.validate(self)
if self.attrs.has_key((None, "href")):
self.name = 'href'
self.value = self.href = self.attrs.getValue((None, "href"))
xmlbase.validate(self, extraParams={"attr": "href"})
if self.rel == "self" and self.parent.name in ["feed","channel"]:
# detect relative self values
from urlparse import urlparse
from xml.dom import XML_NAMESPACE
absolute = urlparse(self.href)[1]
element = self
while not absolute and element and hasattr(element,'attrs'):
pattrs = element.attrs
if pattrs and pattrs.has_key((XML_NAMESPACE, u'base')):
absolute=urlparse(pattrs.getValue((XML_NAMESPACE, u'base')))[1]
element = element.parent
if not absolute:
self.log(RelativeSelf({"value":self.href}))
from urlparse import urljoin
if urljoin(self.xmlBase,self.value) not in self.dispatcher.selfURIs:
if urljoin(self.xmlBase,self.value).split('#')[0] != self.xmlBase.split('#')[0]:
from uri import Uri
if self.value.startswith('http://feeds.feedburner.com/'):
if self.value.endswith('?format=xml'):
self.value = self.value.split('?')[0]
value = Uri(self.value)
for docbase in self.dispatcher.selfURIs:
if value == Uri(docbase): break
# don't complain when validating feedburner's xml view
if docbase.startswith('http://feeds.feedburner.com/'):
if docbase.endswith('?format=xml'):
if value == Uri(docbase.split('?')[0]): break
else:
self.log(SelfDoesntMatchLocation({"parent":self.parent.name, "element":self.name}))
self.dispatcher.selfURIs.append(urljoin(self.xmlBase,self.value))
else:
self.log(MissingHref({"parent":self.parent.name, "element":self.name, "attr":"href"}))
if self.attrs.has_key((u'http://purl.org/syndication/thread/1.0', u'count')):
if self.rel != "replies":
self.log(UnexpectedAttribute({"parent":self.parent.name, "element":self.name, "attribute":"thr:count"}))
self.value = self.attrs.getValue((u'http://purl.org/syndication/thread/1.0', u'count'))
self.name="thr:count"
nonNegativeInteger.validate(self)
if self.attrs.has_key((u'http://purl.org/syndication/thread/1.0', u'when')):
self.log(NoThrWhen({"parent":self.parent.name, "element":self.name, "attribute":"thr:when"}))
if self.attrs.has_key((u'http://purl.org/syndication/thread/1.0', u'updated')):
if self.rel != "replies":
self.log(UnexpectedAttribute({"parent":self.parent.name, "element":self.name, "attribute":"thr:updated"}))
self.value = self.attrs.getValue((u'http://purl.org/syndication/thread/1.0', u'updated'))
self.name="thr:updated"
rfc3339.validate(self)
def startElementNS(self, name, qname, attrs):
self.push(eater(), name, attrs)
def characters(self, text):
if text.strip():
self.log(AtomLinkNotEmpty({"parent":self.parent.name, "element":self.name}))