2010-08-18 13 views
8

Qual è il modo corretto per convalidare un xmpp jid? La sintassi è descritta here:, ma non la capisco davvero. Inoltre, sembra piuttosto complicato, quindi usare una libreria per farlo sembrerebbe una buona idea.Convalida un XMPP jid con python?

Attualmente sto usando xmpppy, ma non riesco a trovare come convalidare un jid con esso. Qualsiasi aiuto apprezzato!

risposta

20

Prima di tutto, il riferimento migliore corrente per JID è RFC 6122.

Stavo per darvi l'espressione regolare qui, ma ha un po 'portato via, e implementato tutte le specifiche:

import re 
import sys 
import socket 
import encodings.idna 
import stringprep 

# These characters aren't allowed in domain names that are used 
# in XMPP 
BAD_DOMAIN_ASCII = "".join([chr(c) for c in range(0,0x2d) + 
        [0x2e, 0x2f] + 
        range(0x3a,0x41) + 
        range(0x5b,0x61) + 
        range(0x7b, 0x80)]) 

# check bi-directional character validity 
def bidi(chars): 
    RandAL = map(stringprep.in_table_d1, chars) 
    for c in RandAL: 
     if c: 
      # There is a RandAL char in the string. Must perform further 
      # tests: 
      # 1) The characters in section 5.8 MUST be prohibited. 
      # This is table C.8, which was already checked 
      # 2) If a string contains any RandALCat character, the string 
      # MUST NOT contain any LCat character. 
      if filter(stringprep.in_table_d2, chars): 
       raise UnicodeError("Violation of BIDI requirement 2") 

      # 3) If a string contains any RandALCat character, a 
      # RandALCat character MUST be the first character of the 
      # string, and a RandALCat character MUST be the last 
      # character of the string. 
      if not RandAL[0] or not RandAL[-1]: 
       raise UnicodeError("Violation of BIDI requirement 3") 

def nodeprep(u): 
    chars = list(unicode(u)) 
    i = 0 
    while i < len(chars): 
     c = chars[i] 
     # map to nothing 
     if stringprep.in_table_b1(c): 
      del chars[i] 
     else: 
      # case fold 
      chars[i] = stringprep.map_table_b2(c) 
      i += 1 
    # NFKC 
    chars = stringprep.unicodedata.normalize("NFKC", "".join(chars)) 
    for c in chars: 
     if (stringprep.in_table_c11(c) or 
      stringprep.in_table_c12(c) or 
      stringprep.in_table_c21(c) or 
      stringprep.in_table_c22(c) or 
      stringprep.in_table_c3(c) or 
      stringprep.in_table_c4(c) or 
      stringprep.in_table_c5(c) or 
      stringprep.in_table_c6(c) or 
      stringprep.in_table_c7(c) or 
      stringprep.in_table_c8(c) or 
      stringprep.in_table_c9(c) or 
      c in "\"&'/:<>@"): 
      raise UnicodeError("Invalid node character") 

    bidi(chars) 

    return chars 

def resourceprep(res): 
    chars = list(unicode(res)) 
    i = 0 
    while i < len(chars): 
     c = chars[i] 
     # map to nothing 
     if stringprep.in_table_b1(c): 
      del chars[i] 
     else: 
      i += 1 
    # NFKC 
    chars = stringprep.unicodedata.normalize("NFKC", "".join(chars)) 
    for c in chars: 
     if (stringprep.in_table_c12(c) or 
      stringprep.in_table_c21(c) or 
      stringprep.in_table_c22(c) or 
      stringprep.in_table_c3(c) or 
      stringprep.in_table_c4(c) or 
      stringprep.in_table_c5(c) or 
      stringprep.in_table_c6(c) or 
      stringprep.in_table_c7(c) or 
      stringprep.in_table_c8(c) or 
      stringprep.in_table_c9(c)): 
      raise UnicodeError("Invalid node character") 

    bidi(chars) 

    return chars 

def parse_jid(jid): 
    # first pass 
    m = re.match("^(?:([^\"&'/:<>@]{1,1023})@)?([^/@]{1,1023})(?:/(.{1,1023}))?$", jid) 
    if not m: 
     return False 

    (node, domain, resource) = m.groups() 
    try: 
     # ipv4 address? 
     socket.inet_pton(socket.AF_INET, domain) 
    except socket.error: 
     # ipv6 address? 
     try: 
      socket.inet_pton(socket.AF_INET6, domain) 
     except socket.error: 
      # domain name 
      dom = [] 
      for label in domain.split("."): 
       try: 
        label = encodings.idna.nameprep(unicode(label)) 
        encodings.idna.ToASCII(label) 
       except UnicodeError: 
        return False 

       # UseSTD3ASCIIRules is set, but Python's nameprep doesn't enforce it. 
       # a) Verify the absence of non-LDH ASCII code points; that is, the 
       for c in label: 
        if c in BAD_DOMAIN_ASCII: 
         return False 
       # Verify the absence of leading and trailing hyphen-minus 
       if label[0] == '-' or label[-1] == "-": 
        return False 
       dom.append(label) 
      domain = ".".join(dom) 
    try: 
     if node is not None: 
      node = nodeprep(node) 
     if resource is not None: 
      resource = resourceprep(resource) 
    except UnicodeError: 
     return False 

    return node, domain, resource 

if __name__ == "__main__": 
    results = parse_jid(sys.argv[1]) 
    if not results: 
     print "FAIL" 
    else: 
     print results 

Sì, questo è un sacco di lavoro. Ci sono buone ragioni per tutto questo, ma speriamo di semplificarlo in futuro, se il gruppo di lavoro précis dà i suoi frutti.

+0

Siamo spiacenti per la richiesta tardiva; Stavo per implementarlo come hai fatto tu, ma mi chiedo se l'iterazione sui codepoint sia effettivamente corretta per stringprep. Nel [stringprep RFC] (https://tools.ietf.org/html/rfc3454), parlano di personaggi, che non sono necessariamente equivalenti ai codepoint (pensate a combinare i diacritici). O mi sono perso qualcosa sulla terminologia Unicode? –

+0

L'RFC di stringprep è stato scritto prima che IETF avesse una vista sfumata su Unicode come era necessario per risolvere il problema. Quando la RFC dice "carattere" significa "codepoint" nella maggior parte dei luoghi. Stiamo cercando di risolverlo nel gruppo di lavoro [précis] (http://tools.ietf.org/wg/precis/charters). –

+0

Nell'interesse di aiutare qualcun altro (come me!) A provare a usare questo codice in Python 3, sono necessarie due modifiche: il 'range()' s deve essere passato a ['itertools.chain()'] (http://stackoverflow.com/a/14099894) invece di concatenare con + (e l'elenco deve essere anch'esso reso un 'range()', e le chiamate 'unicode()' devono essere rimosse. – Kromey