]>
git.frykholm.com Git - friends.git/blob - friends/rd/core.py
4 RFC6415_TYPE
= 'application/xrd+xml'
5 RFC7033_TYPE
= 'application/jrd+json'
7 JRD_TYPES
= ('application/jrd+json', 'application/xrd+json', 'application/json', 'text/json')
8 XRD_TYPES
= ('application/xrd+xml', 'text/xml')
11 'activity_streams': 'http://activitystrea.ms/spec/1.0',
12 'app': ('http://apinamespace.org/atom', 'application/atomsvc+xml'),
13 'avatar': 'http://webfinger.net/rel/avatar',
14 'foaf': ('describedby', 'application/rdf+xml'),
15 'hcard': 'http://microformats.org/profile/hcard',
16 'oauth_access_token': 'http://apinamespace.org/oauth/access_token',
17 'oauth_authorize': 'http://apinamespace.org/oauth/authorize',
18 'oauth_request_token': 'http://apinamespace.org/oauth/request_token',
19 'openid': 'http://specs.openid.net/auth/2.0/provider',
20 'opensocial': 'http://ns.opensocial.org/2008/opensocial/activitystreams',
21 'portable_contacts': 'http://portablecontacts.net/spec/1.0',
22 'profile': 'http://webfinger.net/rel/profile-page',
23 'updates_from': 'http://schemas.google.com/g/2010#updates-from',
24 'ostatus_sub': 'http://ostatus.org/schema/1.0/subscribe',
25 'salmon_endpoint': 'salmon',
26 'salmon_key': 'magic-public-key',
27 'webfist': 'http://webfist.org/spec/rel',
28 'xfn': 'http://gmpg.org/xfn/11',
30 'jrd': ('lrdd', 'application/json'),
31 'webfinger': ('lrdd', 'application/jrd+json'),
32 'xrd': ('lrdd', 'application/xrd+xml'),
35 logger
= logging
.getLogger("rd")
40 return isinstance(s
, str)
42 return isinstance(s
, str)
45 def loads(content
, content_type
):
47 from rd
import jrd
, xrd
49 content_type
= content_type
.split(";")[0]
51 if content_type
in JRD_TYPES
:
52 logger
.debug("loads() loading JRD")
53 return jrd
.loads(content
)
55 elif content_type
in XRD_TYPES
:
56 logger
.debug("loads() loading XRD")
57 return xrd
.loads(content
)
59 raise TypeError('Unknown content type')
62 # helper functions for host parsing and discovery
65 def parse_uri_components(resource
, default_scheme
='https'):
67 scheme
= default_scheme
69 from urllib
.parse
import urlparse
71 parts
= urlparse(resource
)
72 if parts
.scheme
and parts
.netloc
:
74 ''' FIXME: if we have https://user@some.example/ we end up with parts.netloc='user@some.example' here. '''
75 hostname
= parts
.netloc
78 elif parts
.scheme
== 'acct' or (not parts
.scheme
and '@' in parts
.path
):
79 ''' acct: means we expect WebFinger to work, and RFC7033 requires https, so host-meta should support it too. '''
82 ''' We should just have user@site.example here, but if it instead
83 is user@site.example/whatever/else we have to split it later
84 on the first slash character, '/'.
86 hostname
= parts
.path
.split('@')[-1]
89 ''' In case we have hostname=='site.example/whatever/else' we do the split
90 on the first slash, giving us 'site.example' and 'whatever/else'.
93 (hostname
, path
) = hostname
.split('/', maxsplit
=1)
94 ''' Normalize path so it always starts with /, which is the behaviour of urlparse too. '''
99 raise ValueError('No hostname could be deduced from arguments.')
101 elif '/' in parts
.path
:
102 (hostname
, path
) = parts
.path
.split('/', maxsplit
=1)
103 ''' Normalize path so it always starts with /, which is the behaviour of urlparse too. '''
106 hostname
= parts
.path
109 return (scheme
, hostname
, path
)
115 class Attribute(object):
117 def __init__(self
, name
, value
):
121 def __cmp__(self
, other
):
122 return cmp(str(self
), str(other
))
124 def __eq__(self
, other
):
125 return str(self
) == other
128 return "%s=%s" % (self
.name
, self
.value
)
131 class Element(object):
133 def __init__(self
, name
, value
, attrs
=None):
136 self
.attrs
= attrs
or {}
141 def __init__(self
, value
, lang
=None):
145 def __cmp__(self
, other
):
146 return cmp(str(self
), str(other
))
148 def __eq__(self
, other
):
149 return str(self
) == str(other
)
153 return "%s:%s" % (self
.lang
, self
.value
)
157 class Property(object):
159 def __init__(self
, type_
, value
=None):
163 def __cmp__(self
, other
):
164 return cmp(str(self
), str(other
))
166 def __eq__(self
, other
):
167 return str(self
) == other
171 return "%s:%s" % (self
.type, self
.value
)
179 class ListLikeObject(list):
181 def __setitem__(self
, key
, value
):
182 value
= self
.item(value
)
183 super(ListLikeObject
, self
).__setitem
__(key
, value
)
185 def append(self
, value
):
186 value
= self
.item(value
)
187 super(ListLikeObject
, self
).append(value
)
189 def extend(self
, values
):
190 values
= (self
.item(value
) for value
in values
)
191 super(ListLikeObject
, self
).extend(values
)
194 class AttributeList(ListLikeObject
):
196 def __call__(self
, name
):
198 if attr
.name
== name
:
201 def item(self
, value
):
202 if isinstance(value
, (list, tuple)):
203 return Attribute(*value
)
204 elif not isinstance(value
, Attribute
):
205 raise ValueError('value must be an instance of Attribute')
209 class ElementList(ListLikeObject
):
211 def item(self
, value
):
212 if not isinstance(value
, Element
):
213 raise ValueError('value must be an instance of Type')
217 class TitleList(ListLikeObject
):
219 def item(self
, value
):
222 elif isinstance(value
, (list, tuple)):
224 elif not isinstance(value
, Title
):
225 raise ValueError('value must be an instance of Title')
229 class LinkList(ListLikeObject
):
231 def __call__(self
, rel
):
236 def item(self
, value
):
237 if not isinstance(value
, Link
):
238 raise ValueError('value must be an instance of Link')
242 class PropertyList(ListLikeObject
):
244 def __call__(self
, type_
):
246 if prop
.type == type_
:
249 def item(self
, value
):
251 return Property(value
)
252 elif isinstance(value
, (tuple, list)):
253 return Property(*value
)
254 elif not isinstance(value
, Property
):
255 raise ValueError('value must be an instance of Property')
265 def __init__(self
, rel
=None, type=None, href
=None, template
=None):
269 self
.template
= template
270 self
._titles
= TitleList()
271 self
._properties
= PropertyList()
273 def get_titles(self
):
275 titles
= property(get_titles
)
277 def get_properties(self
):
278 return self
._properties
279 properties
= property(get_properties
)
281 def apply_template(self
, uri
):
283 from urllib
.parse
import quote
285 if not self
.template
:
286 raise TypeError('This is not a template Link')
287 return self
.template
.replace('{uri}', quote(uri
, safe
=''))
291 from cgi
import escape
294 for prop
in ['rel', 'type', 'href', 'template']:
295 val
= getattr(self
, prop
)
297 attrs
+= ' {!s}="{!s}"'.format(escape(prop
), escape(val
))
299 return '<Link{!s}/>'.format(attrs
)
308 def __init__(self
, xml_id
=None, subject
=None):
311 self
.subject
= subject
314 self
._properties
= PropertyList()
315 self
._links
= LinkList()
316 self
._signatures
= []
318 self
._attributes
= AttributeList()
319 self
._elements
= ElementList()
325 return jrd
.dumps(self
)
329 return xrd
.dumps(self
)
333 def find_link(self
, rels
, attr
=None, mimetype
=None):
334 if not isinstance(rels
, (list, tuple)):
336 for link
in self
.links
:
338 if mimetype
and link
.type != mimetype
:
341 return getattr(link
, attr
, None)
344 def __getattr__(self
, name
, attr
=None):
345 if name
in KNOWN_RELS
:
347 ''' If we have a specific mimetype for this rel value '''
348 rel
, mimetype
= KNOWN_RELS
[name
]
350 rel
= KNOWN_RELS
[name
]
352 return self
.find_link(rel
, attr
=attr
, mimetype
=mimetype
)
353 raise AttributeError(name
)
355 # custom elements and attributes
357 def get_elements(self
):
358 return self
._elements
359 elements
= property(get_elements
)
362 def attributes(self
):
363 return self
._attributes
365 # defined elements and attributes
367 def get_expires(self
):
370 def set_expires(self
, expires
):
371 if not isinstance(expires
, datetime
.datetime
):
372 raise ValueError('expires must be a datetime object')
373 self
._expires
= expires
374 expires
= property(get_expires
, set_expires
)
376 def get_aliases(self
):
378 aliases
= property(get_aliases
)
380 def get_properties(self
):
381 return self
._properties
382 properties
= property(get_properties
)
386 links
= property(get_links
)
388 def get_signatures(self
):
389 return self
._signatures
390 signatures
= property(get_links
)