Beispiel #1
0
def pns_to_xml_utf8(model, xml_types={}, xml_type=xml_dom.Element):
    # try to decode the PNS/XML element
    try:
        attr, first, children, follow = netstring.validate(model[2], 4)
    except:
        if model[0] != model[3]:
            attr = {'pns': model[0]}
        else:
            attr = None
        e = xml_types.get(model[1],
                          xml_type)(model[1]
                                    or 'http://allegra/ pns-xml-error', attr)
        e.xml_first = model[2]
        return e, None

    # decode the attributes and set the pns attribute
    if attr:
        attr = dict(
            (tuple(netstring.decode(item)) for item in netstring.decode(attr)))
        if model[0] != model[3]:
            attr['pns'] = model[0]
    elif model[0] != model[3]:
        attr = {'pns': model[0]}
    else:
        attr = None
    e = xml_types.get(model[1], xml_type)(model[1], attr)
    if first:
        e.xml_first = first
    else:
        e.xml_first = ''
    if follow:
        e.xml_follow = follow
    return e, children
 def pns_to_xml_unicode (self, resolved, model):
         self.xml_parsed, children = pns_to_xml_unicode (
                 model, self.pns_dom.xml_types, self.pns_dom.xml_type
                 )
         if children:
                 self.xml_parsed.xml_children = children = list (
                         netstring.decode (children)
                         )
                 for child in children:
                         subject, name = tuple (
                                 netstring.decode (child)
                                 )
                         if subject:
                                 context = model[0]
                         else:
                                 context = subject = model[0]
                         joined = PNS_XML_continuation (
                                 self.pns_dom, child
                                 )
                         joined.pns_context = model[3]
                         self.pns_dom.pns_statement (
                                 (subject, name, ''), context,
                                 joined.pns_to_xml_unicode
                                 )
                         joined.finalization = self
def statement_unicode (model, prefixes, encoding='ASCII'):
        e, children = pns_to_xml_unicode (model)
        if children:
                e.xml_children = []
                for child in netstring.decode (children):
                        subject, name = netstring.decode (child)
                        if subject:
                                e.xml_children.append ('<%s pns="%s"/>' % (
                                        ''.join (xml_unicode.xml_prefix_FQN (
                                                unicode (name, 'utf-8'), 
                                                prefixes, encoding
                                                )),
                                        xml_unicode.xml_attr (
                                                unicode (subject, 'utf-8'), 
                                                encoding
                                                )
                                        ))
                        else:
                                e.xml_children.append (
                                        '<%s%s'
                                        ' />' % xml_unicode.xml_prefix_FQN (
                                                unicode (name, 'utf-8'), 
                                                prefixes, encoding
                                                )
                                        )
        return xml_unicode.xml_prefixed (
                e, prefixes, 
                ' context="%s"' % xml_unicode.xml_attr (
                        unicode (model[3], 'utf-8'), encoding
                        ), encoding
                )
 def pns_to_xml_utf8 (self, resolved, model):
         if model[4][0] in ('.', '?'):
                 return False
         
         self.xml_parsed, children = pns_to_xml_utf8 (
                 model, self.pns_dom.xml_types, self.pns_dom.xml_type
                 )
         # decode the children's name and subject,
         if children:
                 self.xml_parsed.xml_children = children = list (
                         netstring.decode (children)
                         )
                 for child in children:
                         subject, name = tuple (
                                 netstring.decode (child)
                                 )
                         if subject:
                                 context = model[0]
                         else:
                                 context = subject = model[0]
                         joined = PNS_XML_continuation (
                                 self.pns_dom, child
                                 )
                         self.pns_dom.pns_statement (
                                 (subject, name, ''), context,
                                 joined.pns_to_xml_unicode
                                 )
                         joined.finalization = self
         return False
Beispiel #5
0
def statements_unicode(model, prefixes, contexts, encoding='ASCII'):
    for co in netstring.decode(model[2]):
        c, o = netstring.decode(co)
        if c not in contexts:
            continue

        for more in statement_unicode((model[0], model[1], o, c, '_'),
                                      prefixes, encoding):
            yield more
def statements_unicode (model, prefixes, contexts, encoding='ASCII'):
        for co in netstring.decode (model[2]):
                c, o = netstring.decode (co)
                if c not in contexts:
                        continue
                
                for more in statement_unicode (
                        (model[0], model[1], o, c, '_'), prefixes, encoding
                        ):
                        yield more        
Beispiel #7
0
    def pns_to_xml_unicode_a(self, resolved, model):
        if model[4] != ('_') or model[2] == '':
            return False

        self.pns_contexts = {}
        question = resolved[:2]
        for co in netstring.decode(model[2]):
            c, o = netstring.decode(co)  # TODO? fix this
            self.pns_to_xml_unicode(resolved, question + (o, c, '_'))
            self.pns_contexts[c] = self.xml_parsed
        self.xml_parsed = None
        return False
def pns_to_xml_unicode (model, xml_types={}, xml_type=xml_dom.Element):
        name = unicode (model[1], 'utf-8')
        # try to decode the PNS/XML element 
        try:
                attr, first, children, follow = netstring.validate (
                        model[2], 4
                        )
        except:
                # if no PNS/XML element is encoded in the statement object,
                # consider the predicate as the element name, the object as 
                # first CDATA and the subject as only attribute if it is
                # distinct from the statement's context. in effect, translate
                # *any* PNS statement to an XML element:
                #
                # <predicate pns="subject">object</predicate>
                #
                if model[0] != model[3]:
                        attr = {u'pns': unicode (model[0], 'utf-8')}
                else:
                        attr = None
                e = xml_types.get (name, xml_type) (
                        name or u'http://allegra/ pns-xml-error', attr
                        )
                if model[2]:
                        e.xml_first = unicode (model[2], 'utf-8')
                else:
                        e.xml_first = u''
                return e, None
                
        # decode the attributes and set the pns attribute
        if attr:
                attr = dict ((
                        tuple ((
                                unicode (s, 'utf-8')
                                for s in netstring.decode (item)
                                ))
                        for item in netstring.decode (attr)
                        ))
                if model[0] != model[3]:
                        attr[u'pns'] = unicode (model[0], 'utf-8')
        elif model[0] != model[3]:
                attr = {u'pns': unicode (model[0], 'utf-8')}
        else:
                attr = None
        # decode the name and instanciate an XML element
        e = xml_types.get (name, xml_type) (name, attr)
        if first:
                e.xml_first = unicode (first, 'utf-8')
        else:
                e.xml_first = u''
        if follow:
                e.xml_follow = unicode (follow, 'utf-8')
        return e, children
 def pns_to_xml_unicode_a (self, resolved, model):
         if model[4] != ('_') or model[2] == '':
                 return False
         
         self.pns_contexts = {}
         question = resolved[:2]
         for co in netstring.decode (model[2]):
                 c, o = netstring.decode (co) # TODO? fix this
                 self.pns_to_xml_unicode (
                         resolved, question+(o, c, '_')
                         )
                 self.pns_contexts[c] = self.xml_parsed
         self.xml_parsed = None
         return False
Beispiel #10
0
	def pns_timeout (self, sp):
		if sp == self.PNS_SP:
			# protocol
			self.pns_tcp_continue ((
				self.pns_name, '', '', self.pns_name
				), '_')
			if self.pns_statements.has_key (sp):
				# timed-out
				o = self.pns_statements.pop (sp)
				if o == '':
					if self.pns_right and self.pns_left:
						# quitted defered until joined
						self.pns_quit ()
					else:
						# quitting
						self.pns_quitted ()
				else:
					pass # joining
			return
				
		if self.pns_buffer.has_key (sp):
			# delete timeouted out buffered subject and predicate
			o = self.pns_buffer[sp]
			del self.pns_buffer[sp]
		else:
			o = ''
		# echo the non-protocol timeouts to all subscribers
		model = list (netstring.decode (sp))
		model.append (o)
		model.append (self.pns_name)
		self.pns_tcp_continue (model, '_')
		# quit if timeouts on a statement
		if self.pns_statements.has_key (sp):
			self.pns_quit ()
Beispiel #11
0
def statement_utf8(model, prefixes):
    e, children = pns_to_xml_utf8(model)
    if children:
        e.xml_children = []
        for child in netstring.decode(children):
            subject, name = netstring.decode(child)
            if subject:
                e.xml_children.append(
                    '<%s pns="%s"/>' %
                    (''.join(xml_utf8.xml_prefix_FQN(
                        name, prefixes)), xml_utf8.xml_attr(subject)))
            else:
                e.xml_children.append('<%s />' %
                                      xml_utf8.xml_prefix_FQN(name, prefixes))
    return xml_utf8.xml_prefixed(e, prefixes,
                                 ' context="%s"' % xml_utf8.xml_attr(model[3]))
Beispiel #12
0
def name_utf8(name, tag='public'):
    names = tuple(netstring.decode(name)) or (name, )
    if len(names) > 1:
        return '<%s names="%s">%s</%s>' % (tag, xml_utf8.xml_attr(
            name), ''.join([name_utf8(n, tag) for n in names]), tag)

    return '<%s>%s</%s>' % (tag, xml_tf8.xml_cdata(name), tag)
        def pns_articulate (self):
                try:
                        encoded = self.pns_pipe.next ()
                        
                except StopIteration:
                        # no more statements to handle, close
                        self.async_net_push (('0:,0:,0:,0:,',))
                        # self.close_when_done ()
                        return

                model = list (netstring.decode (encoded))
                if '' == model[0]:
                        if '' == model[1] == model[2]:
                                self.pns_quit (model[3])
                        else:
                                self.pns_command (tuple (model[:3]))
                elif '' == model[1] and model[0] == model[3]:
                        if model[2]:
                                self.pns_join (model[3], model[2])
                        else:
                                self.pns_subscribe (model[3])
                else:
                        self.pns_statement (
                                tuple (model[:3]), model[3]
                                )
Beispiel #14
0
def name_unicode(name, tag='public', encoding='ASCII'):
    names = tuple(netstring.decode(name)) or (name, )
    if len(names) > 1:
        return '<%s names="%s">%s</%s>' % (
            tag, xml_unicode.xml_attr(unicode(name, 'UTF-8')), ''.join(
                [name_unicode(n, tag, encoding) for n in names]), tag)

    return '<%s>%s</%s>' % (tag, xml_unicode.xml_cdata(unicode(name,
                                                               'UTF-8')), tag)


# Note about this implementation
#
# it may be possible to get rid of the last level of PNS/XML articulation
# and make articulation sparser, by allowing un-named child element to
# be completely encoded in their parent. Yet this practically makes encoding
# from XML to PNS also more complex to support sensible trunking.
#
# Most XML leaf elements do not require a PNS/XML statement but the
# presence of one too large in their midst suppose a more elaborated
# algorithm for sparse articulation, counting every bytes in the parent's
# PNS/XML statement to decide which elements to "inline" as:
#
#         :subject,:name,::attributes,:first,:follow,,
#
# and which to reference only:
#
#         :subject,:name,:,
#
# it just might not be worth the trouble.
#
# TODO: add a depth limit to PNS_XML, avoid the possible infinite loop
#       when there is a circular PNS/XML statement in the metabase for
#       the articulated XML element tree.
Beispiel #15
0
def pns_to_xml_unicode(model, xml_types={}, xml_type=xml_dom.Element):
    name = unicode(model[1], 'utf-8')
    # try to decode the PNS/XML element
    try:
        attr, first, children, follow = netstring.validate(model[2], 4)
    except:
        # if no PNS/XML element is encoded in the statement object,
        # consider the predicate as the element name, the object as
        # first CDATA and the subject as only attribute if it is
        # distinct from the statement's context. in effect, translate
        # *any* PNS statement to an XML element:
        #
        # <predicate pns="subject">object</predicate>
        #
        if model[0] != model[3]:
            attr = {u'pns': unicode(model[0], 'utf-8')}
        else:
            attr = None
        e = xml_types.get(name,
                          xml_type)(name or u'http://allegra/ pns-xml-error',
                                    attr)
        if model[2]:
            e.xml_first = unicode(model[2], 'utf-8')
        else:
            e.xml_first = u''
        return e, None

    # decode the attributes and set the pns attribute
    if attr:
        attr = dict((tuple((unicode(s, 'utf-8')
                            for s in netstring.decode(item)))
                     for item in netstring.decode(attr)))
        if model[0] != model[3]:
            attr[u'pns'] = unicode(model[0], 'utf-8')
    elif model[0] != model[3]:
        attr = {u'pns': unicode(model[0], 'utf-8')}
    else:
        attr = None
    # decode the name and instanciate an XML element
    e = xml_types.get(name, xml_type)(name, attr)
    if first:
        e.xml_first = unicode(first, 'utf-8')
    else:
        e.xml_first = u''
    if follow:
        e.xml_follow = unicode(follow, 'utf-8')
    return e, children
Beispiel #16
0
    def async_net_continue(self, encoded):
        "handle the following PNS/TCP peer statement"
        # Note that there is no validation of the PNS statements
        # because they are trusted to be valid. A client *may*
        # validate, the peer *must* anyway.
        #
        self.pns_received += 1
        model = list(netstring.decode(encoded))
        if len(model) != 5:
            assert None == self.log(encoded, 'invalid-peer-statement')
            self.handle_close()
            return

        self.pns_multiplex(model)
        resolved = tuple(model[:3])
        if '' == model[0]:
            # close, index, context and route
            handlers = self.pns_commands.get(resolved)
            if handlers == None:
                assert None == self.log(encoded,
                                        'unsollicited-command-response')
                self.handle_close()
                return

            self.pns_commands[resolved] = [
                h for h in handlers if h(resolved, model)
            ]
            if len(self.pns_commands[resolved]) == 0:
                del self.pns_commands[resolved]
        elif '' == model[1] and model[0] == model[3]:
            # TODO: protocol response to join/subscribe
            pass
        else:
            # statements
            statements = self.pns_contexts.setdefault(model[3], {})
            handlers = statements.get(resolved)
            if handlers == None:
                # test for a question's answer handler
                resolved = (model[0], model[1], '')
                handlers = statements.get(resolved)
                if handlers == None:
                    # irrelevant statement
                    self.pns_noise(model)
                    return

            # relevant statements, handle and refresh the handlers list
            statements[resolved] = [h for h in handlers if h(resolved, model)]
            # then clean up ...
            if len(statements[resolved]) == 0:
                del statements[resolved]
            if len(statements) == 0:
                del self.pns_contexts[model[3]]
        if (self.pns_close_when_done and len(self.pns_commands) == 0
                and len(self.pns_contexts) == 0
                and len(self.pns_subscribed) == 0):
            assert None == self.log('done', 'debug')
            self.handle_close()
def pns_quatuor (encoded, contexts, max=1024):
        # a valid quatuor must have subject, predicate, object and context
        #
        assert type (max) == int and max % 2 == 0
        model = list (netstring.decode (encoded))
        if len (model) != 4:
                return None, '1 not a quatuor'
        
        if model[3]:
                # Validate the context as a public name, but allways check
                # the supplied cache of valid PNS names first!
                #
                if public_names.valid_in_utf8 (model[3], contexts) != None:
                        return None, '2 invalid context'
                        
                
        if model[0]:
                # the predicate length and subject length are limited to 
                # or 512 bytes minus the ten needed to encode them
                #
                sp_len = len (model[0]) + len (model[1]) + len (
                        '%d%d' % (len (model[0]), len (model[1]))
                        )
                if sp_len > max/2:
                        return None, '3 invalid statement length'

                # validate the statement subject as a public name
                if model[0] != model[3] and public_names.valid_in_utf8 (
                        subject, contexts
                        ) == None:
                        return None, '4 invalid subject'
                        
                if model[2]:
                        # non-empty objects are trunked to fit the 1024 bytes
                        # PNS/UDP answer datagram. Sorry guys, but there is
                        # no way to fit more and rationning the number of
                        # statement per user *does* limit potential abuses.
                        #
                        model[2] = model[2][:(
                                max - sp_len - len ('%d' % (max - sp_len))
                                )]
                        #
                        # this is not an error condition, its a welcome
                        # feature of any PNS peer, the only "optional"
                        # error handling that allows use agent to be sloppy
                        # in their validation of PNS statements. simplistic
                        # clients should be tolerated ;-)
        elif model[1]:
                if public_names.valid_in_utf8 (command, contexts) == None:
                        return None, '5 invalid command predicate'
                           
        elif model[2] and public_names.valid_in_utf8 (
                model[2], contexts
                ) == None:
                return None, '6 invalid command object'
                        
        return model, ''
Beispiel #18
0
 def pns_to_xml_unicode(self, resolved, model):
     self.xml_parsed, children = pns_to_xml_unicode(model,
                                                    self.pns_dom.xml_types,
                                                    self.pns_dom.xml_type)
     if children:
         self.xml_parsed.xml_children = children = list(
             netstring.decode(children))
         for child in children:
             subject, name = tuple(netstring.decode(child))
             if subject:
                 context = model[0]
             else:
                 context = subject = model[0]
             joined = PNS_XML_continuation(self.pns_dom, child)
             joined.pns_context = model[3]
             self.pns_dom.pns_statement((subject, name, ''), context,
                                        joined.pns_to_xml_unicode)
             joined.finalization = self
Beispiel #19
0
def name_utf8 (name, tag='public'):
        names = tuple (netstring.decode (name)) or (name,)
        if len (names) > 1:
                return '<%s names="%s">%s</%s>' % (
                        tag,
                        xml_utf8.xml_attr (name), 
                        ''.join ([name_utf8 (n, tag) for n in names]),
                        tag
                        )

        return '<%s>%s</%s>' % (tag, xml_tf8.xml_cdata (name), tag)
def pns_quatuor(encoded, pns_names, PNS_LENGTH=1024):
    # a valid quatuor must have subject, predicate, object and context
    #
    model = list(netstring.decode(encoded))
    if len(model) != 4:
        return None, '1 not a quatuor'

    if model[3]:
        # Validate the context as a public name, but allways check
        # the supplied cache of valid PNS names first!
        #
        if not (model[3] in pns_names
                or model[3] == pns_name(model[3], set())):
            return None, '2 invalid context'

    if model[0]:
        # the predicate length and subject length are limited to
        # or 512 bytes minus the ten needed to encode them
        #
        sp_len = len(model[0]) + len(model[1]) + len(
            '%d%d' % (len(model[0]), len(model[1])))
        if sp_len > PNS_LENGTH / 2:
            return None, '3 invalid statement length'

        # validate the statement subject as a public name
        if model[0] != model[3] and not (model[0] in pns_names or model[0]
                                         == pns_name(model[0], set())):
            return None, '4 invalid subject'

        if model[2]:
            # non-empty objects are trunked to fit the 1024 bytes
            # PNS/UDP answer datagram. Sorry guys, but there is
            # no way to fit more and rationning the number of
            # statement per user *does* limit potential abuses.
            #
            model[2] = model[2][:(PNS_LENGTH - sp_len -
                                  len('%d' % (PNS_LENGTH - sp_len)))]
            #
            # this is not an error condition, its a welcome
            # feature of any PNS peer, the only "optional"
            # error handling that allows use agent to be sloppy
            # in their validation of PNS statements. simplistic
            # clients should be tolerated ;-)
    elif model[1]:
        if not (model[1] in pns_names
                or model[1] == pns_name(model[1], set())):
            return None, '5 invalid command predicate'

    elif model[2] and not (model[2] in pns_names
                           or model[2] == pns_name(model[2], set())):
        return None, '6 invalid command object'

    return model, ''
Beispiel #21
0
def statement_unicode(model, prefixes, encoding='ASCII'):
    e, children = pns_to_xml_unicode(model)
    if children:
        e.xml_children = []
        for child in netstring.decode(children):
            subject, name = netstring.decode(child)
            if subject:
                e.xml_children.append(
                    '<%s pns="%s"/>' %
                    (''.join(
                        xml_unicode.xml_prefix_FQN(unicode(name, 'utf-8'),
                                                   prefixes, encoding)),
                     xml_unicode.xml_attr(unicode(subject, 'utf-8'),
                                          encoding)))
            else:
                e.xml_children.append(
                    '<%s%s'
                    ' />' % xml_unicode.xml_prefix_FQN(unicode(name, 'utf-8'),
                                                       prefixes, encoding))
    return xml_unicode.xml_prefixed(
        e, prefixes, ' context="%s"' %
        xml_unicode.xml_attr(unicode(model[3], 'utf-8'), encoding), encoding)
Beispiel #22
0
    def pns_to_xml_utf8(self, resolved, model):
        if model[4][0] in ('.', '?'):
            return False

        self.xml_parsed, children = pns_to_xml_utf8(model,
                                                    self.pns_dom.xml_types,
                                                    self.pns_dom.xml_type)
        # decode the children's name and subject,
        if children:
            self.xml_parsed.xml_children = children = list(
                netstring.decode(children))
            for child in children:
                subject, name = tuple(netstring.decode(child))
                if subject:
                    context = model[0]
                else:
                    context = subject = model[0]
                joined = PNS_XML_continuation(self.pns_dom, child)
                self.pns_dom.pns_statement((subject, name, ''), context,
                                           joined.pns_to_xml_unicode)
                joined.finalization = self
        return False
Beispiel #23
0
def statement_utf8 (model, prefixes):
        e, children = pns_to_xml_utf8 (model)
        if children:
                e.xml_children = []
                for child in netstring.decode (children):
                        subject, name = netstring.decode (child)
                        if subject:
                                e.xml_children.append ('<%s pns="%s"/>' % (
                                        ''.join (xml_utf8.xml_prefix_FQN (
                                                name, prefixes
                                                )),
                                        xml_utf8.xml_attr (subject)
                                        ))
                        else:
                                e.xml_children.append (
                                        '<%s />' % xml_utf8.xml_prefix_FQN (
                                                name, prefixes
                                                )
                                        )
        return xml_utf8.xml_prefixed (
                e, prefixes, 
                ' context="%s"' % xml_utf8.xml_attr (model[3])
                )
Beispiel #24
0
def pns_to_xml_utf8 (model, xml_types={}, xml_type=xml_dom.Element):
        # try to decode the PNS/XML element 
        try:
                attr, first, children, follow = netstring.validate (
                        model[2], 4
                        )
        except:
                if model[0] != model[3]:
                        attr = {'pns': model[0]}
                else:
                        attr = None
                e = xml_types.get (model[1], xml_type) (
                        model[1] or 'http://allegra/ pns-xml-error', attr
                        )
                e.xml_first = model[2]
                return e, None
                
        # decode the attributes and set the pns attribute
        if attr:
                attr = dict ((
                        tuple (netstring.decode (item))
                        for item in netstring.decode (attr)
                        ))
                if model[0] != model[3]:
                        attr['pns'] = model[0]
        elif model[0] != model[3]:
                attr = {'pns': model[0]}
        else:
                attr = None
        e = xml_types.get (model[1], xml_type) (model[1], attr)
        if first:
                e.xml_first = first
        else:
                e.xml_first = ''
        if follow:
                e.xml_follow = follow
        return e, children
Beispiel #25
0
	def handle_read (self):
		datagram, peer = self.recvfrom (pns_datagram_size)
		if peer == None:
			assert None == self.log ('nop', 'debug')
			return
		
		if self.pns_joined.has_key (peer):
			# in circle question ...
			self.pns_joined[peer].pns_question (datagram, peer)
			return
			
		if self.pns_accepted.has_key (peer[0]):
			# question from a new peer accepted at right
			self.pns_accepted[peer[0]].pns_question (
				datagram, peer
				)
			return

		# out of circle
		model = list (netstring.decode (datagram))
		if (
			len (model) != 2 or # TODO: ? 3 instead ?
			model[0] == '' or
			not public_names.valid_utf8 (model[0])
			):
			# log and drop invalid out-of-circle question ...
			self.log (
				'invalid-peer ip="%s"' % peer[0], 'error'
				)
			return

		# if subscribed, joined ...
		if self.pns_peer.pns_subscribed.has_key (model[0]):
			self.pns_peer.pns_subscribed[
				model[0]
				].pns_joined (peer)
			return
			
		# not subscribed, resolve a PIRP answer >>>
		if not self.pns_peers.has_key (peer):
			self.pns_peer.pns_resolution.pns_udp_pirp (
				model[0], peer
				)
Beispiel #26
0
        def pns_resolve_context (self, resolved, model):
                if model != None:
                        # context(s) found
                        self.pns_walk_out (resolved[1], model[1])
                        return # stop walking.

                # no context known
                for name in tuple (netstring.decode (resolved[1])):
                        if name in self.pns_walked:
                                continue
                                
                        if len (self.pns_walked) >= self.PNS_HORIZON:
                                break # beyond the horizon, stop walking.

                        # new and below the horizon 
                        self.pns_walked.add (name)
                        self.pns_articulator.pns_command (
                                ('', '', name), 
                                self.pns_resolve_index
                                ) # walk up the indexes ...
Beispiel #27
0
 def pns_resolve_index (self, resolved, model):
         if model != None:
                 # known name
                 name = model[0]
                 if (
                         len (self.pns_walked) > self.PNS_HORIZON or 
                         name in self.pns_walked
                         ):
                         return
                 
                 # new and below the horizon
                 self.pns_walked.add (name)
                 self.pns_articulator.pns_command (
                         ('', name, ''), 
                         self.pns_resolve_context
                         ) # walk the contexts ...
 
         # unknown name
         names = tuple (netstring.decode (resolved[2]))
         if len (names) == 0:
                 # inarticulated
                 self.pns_articulator.pns_command (
                         ('', name, ''), 
                         self.pns_resolve_context
                         ) # walk the contexts ...
                 return             
                 
         # for each name articulated
         for name in names:
                 if name in self.pns_walked:
                         continue
                 
                 if len (self.pns_walked) > self.PNS_HORIZON:
                         break
                 
                 # new and below the horizon
                 self.pns_walked.add (name)
                 self.pns_articulator.pns_command (
                         ('', name, ''), 
                         self.pns_resolve_context
                         ) # walk the contexts ...                        
Beispiel #28
0
def name_unicode (name, tag='public', encoding='ASCII'):
        names = tuple (netstring.decode (name)) or (name,)
        if len (names) > 1:
                return '<%s names="%s">%s</%s>' % (
                        tag,
                        xml_unicode.xml_attr (unicode (name, 'UTF-8')), 
                        ''.join ([name_unicode (
                                n, tag, encoding
                                ) for n in names]),
                        tag
                        )

        return '<%s>%s</%s>' % (
                tag, xml_unicode.xml_cdata (unicode (name, 'UTF-8')), tag
                )


# Note about this implementation 
#
# it may be possible to get rid of the last level of PNS/XML articulation
# and make articulation sparser, by allowing un-named child element to
# be completely encoded in their parent. Yet this practically makes encoding
# from XML to PNS also more complex to support sensible trunking.
#
# Most XML leaf elements do not require a PNS/XML statement but the
# presence of one too large in their midst suppose a more elaborated
# algorithm for sparse articulation, counting every bytes in the parent's
# PNS/XML statement to decide which elements to "inline" as:
#
#         :subject,:name,::attributes,:first,:follow,,
#
# and which to reference only:
#
#         :subject,:name,:,
#
# it just might not be worth the trouble.
#
# TODO: add a depth limit to PNS_XML, avoid the possible infinite loop 
#       when there is a circular PNS/XML statement in the metabase for
#       the articulated XML element tree.
Beispiel #29
0
    def pns_articulate(self):
        try:
            encoded = self.pns_pipe.next()

        except StopIteration:
            # no more statements to handle, close
            self.async_net_push(('0:,0:,0:,0:,', ))
            # self.close_when_done ()
            return

        model = list(netstring.decode(encoded))
        if '' == model[0]:
            if '' == model[1] == model[2]:
                self.pns_quit(model[3])
            else:
                self.pns_command(tuple(model[:3]))
        elif '' == model[1] and model[0] == model[3]:
            if model[2]:
                self.pns_join(model[3], model[2])
            else:
                self.pns_subscribe(model[3])
        else:
            self.pns_statement(tuple(model[:3]), model[3])
def pns_name(encoded, horizon, HORIZON=126):
    # Recursively validate a Public Name, returns the empty string if
    # the name encoded is invalid or inside the horizon. This function
    # does more than just assert that the encoded 8-bit byte string is
    # a valid public name: it transform it to a valid public name.
    #
    # try to decode the articulated public names
    names = [n for n in netstring.decode(encoded) if n]
    if not names:
        if encoded not in horizon and pns_name_clean(encoded):
            # clean name new in this horizon
            horizon.add(encoded)
            return encoded

        # unsafe 8-bit byte string or Public Name in the in horizon
        return ''

    # articulated Public Names
    valid = []
    for name in names:
        # recursively validate each articulated name in this horizon
        name = pns_name(name, horizon, HORIZON)
        if name:
            valid.append(name)
            if len(horizon) >= HORIZON:
                break  # but only under this HORIZON

    if len(valid) > 1:
        # sort Public Names and encode
        valid.sort()
        return netstring.encode(valid)

    if len(valid) > 0:
        # return a "singleton"
        return valid[0]

    return ''  # nothing valid to articulate in this horizon
def pns_name (encoded, horizon, HORIZON=126):
        # Recursively validate a Public Name, returns the empty string if
        # the name encoded is invalid or inside the horizon. This function
        # does more than just assert that the encoded 8-bit byte string is
        # a valid public name: it transform it to a valid public name.
        #
        # try to decode the articulated public names
        names = [n for n in netstring.decode (encoded) if n]
        if not names:
                if encoded not in horizon and pns_name_clean (encoded):
                        # clean name new in this horizon
                        horizon.add (encoded)
                        return encoded
                        
                # unsafe 8-bit byte string or Public Name in the in horizon
                return ''

        # articulated Public Names
        valid = []
        for name in names:
                # recursively validate each articulated name in this horizon
                name = pns_name (name, horizon, HORIZON)
                if name:
                        valid.append (name)
                        if len (horizon) >= HORIZON:
                                break # but only under this HORIZON
                                
        if len (valid) > 1:
                # sort Public Names and encode
                valid.sort ()
                return netstring.encode (valid)
                
        if len (valid) > 0:
                # return a "singleton"
                return valid[0]
                
        return '' # nothing valid to articulate in this horizon
Beispiel #32
0
	def pns_question (self, datagram, peer):
		# handle in-circle question
		if datagram.startswith ('0:,'):
			# command: quit right
			model = list (netstring.decode (datagram))
			if len (model) == 2 and ip_peer.is_ip (model[1]):
				self.pns_quit_right (model[1])
				return

			assert None == self.log (
				datagram, 'invalid-protocol-command'
				)
			self.pns_quitted ()
			return
			
		if datagram.startswith (self.PNS_SP):
			# protocol question for this circle
			if self.pns_right:
				# in-circle, forward quit and then close
				self.sendto (self.PNS_SP, (
					self.pns_left, 3534
					))
				self.pns_quitted ()
			elif self.pns_left:
				# ? joining
				pass
			else:
				# ? accept
				pass
			return

		# validate question
		model = list (netstring.decode (datagram))
		if (len (model) != 2 or not public_names.valid_utf8 (
                        model[0], self.pns_horizon
                        )):
			assert None == self.log (
				datagram, 'invalid-question'
				)
			# TODO: ban IP addresses that send invalid datagrams!
			self.pns_quit ()
			return

		sp = netstring.encode (model)
		if (
			self.pns_buffer.get (sp) == '' and
			not self.pns_statements.has_key (sp)
			):
			# buffered not stated, drop
			assert None == self.log (datagram, 'drop')
			return
			
		# echo the statement to the PNS/TCP subscribers
		model.append ('')
		model.append (self.pns_name)
		self.pns_tcp_continue (model, '?')
		if self.pns_statements.get (sp) == '':
			# question circled, clear the statement, do not relay
			del self.pns_statements[sp]
			assert None == self.log (datagram, 'circle')
			return

		# relay left
		self.sendto (sp, (self.pns_left, 3534))
		if (
			model[1] == '' and
			self.pns_peer.pns_subscribed.has_key (model[0])
			):
			# protocol question for a subscribed circle, bounce
			# this peer's IP address as the answer.
			left = self.pns_peer.pns_udp.addr[0]
			self.pns_sendto_right (
				'%s%d:%s' % (sp, len(left), left),
				self.pns_right
				)
			return
			
		# resolve ...
		self.pns_peer.pns_resolution.pns_udp_question (model)
Beispiel #33
0
	def pns_answer (self, datagram, peer):
		# handle in-circle and out-of-circle answers
		if datagram.startswith ('0:,'):
			if not self.pns_right:
				# out-of-circle
				model = list (netstring.decode (datagram))
				if (
					len (model) == 3 and
					model[1] == '' and
					ip_peer.is_ip (model[2])
					):
					# bounce to join left or right of L
					self.pns_join (model[2])
					return
					
			assert None == self.log (
				datagram, 'invalid-command-answer'
				)
			self.pns_quit ()
			return
				
		if datagram.startswith (self.PNS_SP):
			# handle protocol statement
			model = list (netstring.decode (datagram))
			if not (len (model) == 3 and (
				model[2] == '' or ip_peer.is_ip (model[2])
				)):
				assert None == self.log (
					datagram, 'invalid-protocol-answer'
					)
				self.pns_quit ()
				return

			if self.pns_right:
				# in-circle, quitted at left!
				return

			# out-of-circle
			if model[2]:
				# accept questions from R
				self.pns_peer.pns_udp.pns_accepted[
					model[2]
					] = self
			else:
				# root join
				self.pns_in_circle (peer)
			return

		# validate answer
		model = list (netstring.decode (datagram))
        	if (
        		(len (model[0]) + len (model[1]) + len (
        			'%d%d' % (len (model[0]), len (model[1]))
                        	)) > 512 
                        or not public_names.valid_utf8 (
                                model[0], self.pns_horizon
                                )
        		):
			assert None == self.log (
				datagram, 'invalid-question'
				)
			self.pns_quit ()
			return

		sp = netstring.encode (model[:2])
		if (
			self.pns_buffer.has_key (sp) and
			not self.pns_statements.has_key (sp)
			):
			# buffered not stated, drop
			assert None == self.log (datagram, 'drop')
			return

		# echo to all subscribers
		model.append (self.pns_name)
		self.pns_tcp_continue (model, '!')
		if self.pns_statements.get (sp) == model[2]:
			# answer circled, clear the statement, do not relay
			del self.pns_statements [sp]
			assert None == self.log (datagram, 'circle')
			return

		# relay right
		self.pns_sendto_right (
			netstring.encode (model), self.pns_right
			)
		if (
			model[1] == '' and
			self.pns_peer.pns_subscribed.has_key (model[0])
			):
			# protocol answer, let the named circle handle it
			if self.pns_peer.self.pns_peer.pns_subscribed[
				model[0]
				].pns_protocol_answer (model[2]):
				return
				
		# resolve ...
		self.pns_peer.pns_resolution.pns_udp_answer (model)
        def async_net_continue (self, encoded):
                "handle the following PNS/TCP peer statement"
                # Note that there is no validation of the PNS statements
                # because they are trusted to be valid. A client *may*
                # validate, the peer *must* anyway.
                # 
                self.pns_received += 1
                model = list (netstring.decode (encoded))
                if len (model) != 5:
                        assert None == self.log (
                                encoded, 'invalid-peer-statement'
                                )
                        self.handle_close ()
                        return
                        
                self.pns_multiplex (model)
                resolved = tuple (model[:3])
                if '' == model[0]:
                        # close, index, context and route
                        handlers = self.pns_commands.get (resolved)
                        if handlers == None:
                                assert None == self.log (
                                        encoded, 
                                        'unsollicited-command-response'
                                        )
                                self.handle_close ()
                                return

                        self.pns_commands[resolved] = [
                                h for h in handlers if h (resolved, model)
                                ]
                        if len (self.pns_commands[resolved]) == 0:
                                del self.pns_commands[resolved]
                elif '' == model[1] and model[0] == model[3]:
                        # TODO: protocol response to join/subscribe
                        pass
                else:
                        # statements
                        statements = self.pns_contexts.setdefault (
                                model[3], {}
                                )
                        handlers = statements.get (resolved)
                        if handlers == None:
                                # test for a question's answer handler
                                resolved = (model[0], model[1], '')
                                handlers = statements.get (resolved)
                                if handlers == None:
                                        # irrelevant statement
                                        self.pns_noise (model)
                                        return
                                
                        # relevant statements, handle and refresh the handlers list
                        statements[resolved] = [
                                h for h in handlers if h (resolved, model)
                                ]
                        # then clean up ...
                        if len (statements[resolved]) == 0:
                                del statements[resolved]
                        if len (statements) == 0:
                                del self.pns_contexts[model[3]]
                if (
                        self.pns_close_when_done and 
                        len (self.pns_commands) == 0 and
                        len (self.pns_contexts) == 0 and
                        len (self.pns_subscribed) == 0
                        ):
                        assert None == self.log ('done', 'debug')
                        self.handle_close ()