2010-08-18 28 views
8

Cách chính xác để xác thực jid xmpp là gì? Cú pháp được mô tả here:, nhưng tôi thực sự không hiểu nó. Ngoài ra, nó có vẻ khá phức tạp, do đó, sử dụng một thư viện để làm điều đó có vẻ như là một ý tưởng tốt.Xác thực jid XMPP bằng python?

Tôi hiện đang sử dụng xmpppy, nhưng dường như tôi không thể tìm cách xác thực tính hợp lệ với nó. Bất kỳ trợ giúp nào được đánh giá cao!

Trả lời

20

Trước hết, tham chiếu tốt nhất hiện tại cho JID là RFC 6122.

Tôi chỉ sẽ cung cấp cho bạn các regex ở đây, nhưng có một chút mang đi, và thực hiện tất cả các spec:

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 

Có, đây là rất nhiều công việc. Có lý do chính đáng cho tất cả, nhưng chúng tôi hy vọng sẽ đơn giản hóa nó trong tương lai nếu nhóm làm việc précis có trái cây.

+0

Xin lỗi vì yêu cầu muộn; Tôi sẽ thực hiện nó theo cách bạn đã làm, nhưng tôi tự hỏi liệu lặp đi lặp lại trên codepoints là thực sự chính xác cho stringprep. Trong [stringprep RFC] (https://tools.ietf.org/html/rfc3454), chúng nói về các ký tự, không tương đương với các điểm mã hóa (suy nghĩ về việc kết hợp các dấu phụ). Hay tôi đã bỏ lỡ điều gì đó về thuật ngữ unicode? –

+0

Stringprep RFC được viết trước khi IETF có một cái nhìn sắc thái về Unicode như là cần thiết để giải quyết vấn đề. Khi RFC nói "ký tự" nghĩa là "điểm mã" ở hầu hết các nơi. Chúng tôi đang cố gắng khắc phục điều đó trong nhóm làm việc [précis] (http://tools.ietf.org/wg/precis/charters). –

+0

Vì lợi ích của việc giúp đỡ bất kỳ ai khác (như tôi!) Cố gắng sử dụng mã này trong Python 3, hai thay đổi là cần thiết: 'range()' s cần được chuyển giao cho ['itertools.chain()'] (http://stackoverflow.com/a/14099894) thay vì nối với + (và một danh sách cần phải được thực hiện một 'phạm vi()' cũng), và các cuộc gọi 'unicode()' cần phải được loại bỏ. – Kromey

Các vấn đề liên quan