PageRenderTime 42ms CodeModel.GetById 2ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/lib/python/uuid.py

https://bitbucket.org/lindenlab/viewer-beta/
Python | 508 lines | 487 code | 0 blank | 21 comment | 0 complexity | 090164c5a52d6aa0efc1b83b80f63fc4 MD5 | raw file
  1#!/usr/bin/python
  2## $LicenseInfo:firstyear=2011&license=viewerlgpl$
  3## Second Life Viewer Source Code
  4## Copyright (C) 2011, Linden Research, Inc.
  5## 
  6## This library is free software; you can redistribute it and/or
  7## modify it under the terms of the GNU Lesser General Public
  8## License as published by the Free Software Foundation;
  9## version 2.1 of the License only.
 10## 
 11## This library is distributed in the hope that it will be useful,
 12## but WITHOUT ANY WARRANTY; without even the implied warranty of
 13## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 14## Lesser General Public License for more details.
 15## 
 16## You should have received a copy of the GNU Lesser General Public
 17## License along with this library; if not, write to the Free Software
 18## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 19## 
 20## Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 21## $/LicenseInfo$
 22r"""UUID objects (universally unique identifiers) according to RFC 4122.
 23
 24This module provides immutable UUID objects (class UUID) and the functions
 25uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5
 26UUIDs as specified in RFC 4122.
 27
 28If all you want is a unique ID, you should probably call uuid1() or uuid4().
 29Note that uuid1() may compromise privacy since it creates a UUID containing
 30the computer's network address.  uuid4() creates a random UUID.
 31
 32Typical usage:
 33
 34    >>> import uuid
 35
 36    # make a UUID based on the host ID and current time
 37    >>> uuid.uuid1()
 38    UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
 39
 40    # make a UUID using an MD5 hash of a namespace UUID and a name
 41    >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
 42    UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
 43
 44    # make a random UUID
 45    >>> uuid.uuid4()
 46    UUID('16fd2706-8baf-433b-82eb-8c7fada847da')
 47
 48    # make a UUID using a SHA-1 hash of a namespace UUID and a name
 49    >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
 50    UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
 51
 52    # make a UUID from a string of hex digits (braces and hyphens ignored)
 53    >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')
 54
 55    # convert a UUID to a string of hex digits in standard form
 56    >>> str(x)
 57    '00010203-0405-0607-0809-0a0b0c0d0e0f'
 58
 59    # get the raw 16 bytes of the UUID
 60    >>> x.bytes
 61    '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
 62
 63    # make a UUID from a 16-byte string
 64    >>> uuid.UUID(bytes=x.bytes)
 65    UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
 66
 67This module works with Python 2.3 or higher."""
 68
 69__author__ = 'Ka-Ping Yee <ping@zesty.ca>'
 70__date__ = '$Date: 2006/06/12 23:15:40 $'.split()[1].replace('/', '-')
 71__version__ = '$Revision: 1.30 $'.split()[1]
 72
 73RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
 74    'reserved for NCS compatibility', 'specified in RFC 4122',
 75    'reserved for Microsoft compatibility', 'reserved for future definition']
 76
 77class UUID(object):
 78    """Instances of the UUID class represent UUIDs as specified in RFC 4122.
 79    UUID objects are immutable, hashable, and usable as dictionary keys.
 80    Converting a UUID to a string with str() yields something in the form
 81    '12345678-1234-1234-1234-123456789abc'.  The UUID constructor accepts
 82    four possible forms: a similar string of hexadecimal digits, or a
 83    string of 16 raw bytes as an argument named 'bytes', or a tuple of
 84    six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and
 85    48-bit values respectively) as an argument named 'fields', or a single
 86    128-bit integer as an argument named 'int'.
 87    
 88    UUIDs have these read-only attributes:
 89
 90        bytes       the UUID as a 16-byte string
 91
 92        fields      a tuple of the six integer fields of the UUID,
 93                    which are also available as six individual attributes
 94                    and two derived attributes:
 95
 96            time_low                the first 32 bits of the UUID
 97            time_mid                the next 16 bits of the UUID
 98            time_hi_version         the next 16 bits of the UUID
 99            clock_seq_hi_variant    the next 8 bits of the UUID
100            clock_seq_low           the next 8 bits of the UUID
101            node                    the last 48 bits of the UUID
102
103            time                    the 60-bit timestamp
104            clock_seq               the 14-bit sequence number
105
106        hex         the UUID as a 32-character hexadecimal string
107
108        int         the UUID as a 128-bit integer
109
110        urn         the UUID as a URN as specified in RFC 4122
111
112        variant     the UUID variant (one of the constants RESERVED_NCS,
113                    RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)
114
115        version     the UUID version number (1 through 5, meaningful only
116                    when the variant is RFC_4122)
117    """
118
119    def __init__(self, hex=None, bytes=None, fields=None, int=None,
120                       version=None):
121        r"""Create a UUID from either a string of 32 hexadecimal digits,
122        a string of 16 bytes as the 'bytes' argument, a tuple of six
123        integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
124        8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
125        the 'fields' argument, or a single 128-bit integer as the 'int'
126        argument.  When a string of hex digits is given, curly braces,
127        hyphens, and a URN prefix are all optional.  For example, these
128        expressions all yield the same UUID:
129
130        UUID('{12345678-1234-5678-1234-567812345678}')
131        UUID('12345678123456781234567812345678')
132        UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
133        UUID(bytes='\x12\x34\x56\x78'*4)
134        UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
135        UUID(int=0x12345678123456781234567812345678)
136
137        Exactly one of 'hex', 'bytes', 'fields', or 'int' must be given.
138        The 'version' argument is optional; if given, the resulting UUID
139        will have its variant and version number set according to RFC 4122,
140        overriding bits in the given 'hex', 'bytes', 'fields', or 'int'.
141        """
142
143        if [hex, bytes, fields, int].count(None) != 3:
144            raise TypeError('need just one of hex, bytes, fields, or int')
145        if hex is not None:
146            hex = hex.replace('urn:', '').replace('uuid:', '')
147            hex = hex.strip('{}').replace('-', '')
148            if len(hex) != 32:
149                raise ValueError('badly formed hexadecimal UUID string')
150            int = long(hex, 16)
151        if bytes is not None:
152            if len(bytes) != 16:
153                raise ValueError('bytes is not a 16-char string')
154            int = long(('%02x'*16) % tuple(map(ord, bytes)), 16)
155        if fields is not None:
156            if len(fields) != 6:
157                raise ValueError('fields is not a 6-tuple')
158            (time_low, time_mid, time_hi_version,
159             clock_seq_hi_variant, clock_seq_low, node) = fields
160            if not 0 <= time_low < 1<<32L:
161                raise ValueError('field 1 out of range (need a 32-bit value)')
162            if not 0 <= time_mid < 1<<16L:
163                raise ValueError('field 2 out of range (need a 16-bit value)')
164            if not 0 <= time_hi_version < 1<<16L:
165                raise ValueError('field 3 out of range (need a 16-bit value)')
166            if not 0 <= clock_seq_hi_variant < 1<<8L:
167                raise ValueError('field 4 out of range (need an 8-bit value)')
168            if not 0 <= clock_seq_low < 1<<8L:
169                raise ValueError('field 5 out of range (need an 8-bit value)')
170            if not 0 <= node < 1<<48L:
171                raise ValueError('field 6 out of range (need a 48-bit value)')
172            clock_seq = (clock_seq_hi_variant << 8L) | clock_seq_low
173            int = ((time_low << 96L) | (time_mid << 80L) |
174                   (time_hi_version << 64L) | (clock_seq << 48L) | node)
175        if int is not None:
176            if not 0 <= int < 1<<128L:
177                raise ValueError('int is out of range (need a 128-bit value)')
178        if version is not None:
179            if not 1 <= version <= 5:
180                raise ValueError('illegal version number')
181            # Set the variant to RFC 4122.
182            int &= ~(0xc000 << 48L)
183            int |= 0x8000 << 48L
184            # Set the version number.
185            int &= ~(0xf000 << 64L)
186            int |= version << 76L
187        self.__dict__['int'] = int
188
189    def __cmp__(self, other):
190        if isinstance(other, UUID):
191            return cmp(self.int, other.int)
192        return NotImplemented
193
194    def __hash__(self):
195        return hash(self.int)
196
197    def __int__(self):
198        return self.int
199
200    def __repr__(self):
201        return 'UUID(%r)' % str(self)
202
203    def __setattr__(self, name, value):
204        raise TypeError('UUID objects are immutable')
205
206    def __str__(self):
207        hex = '%032x' % self.int
208        return '%s-%s-%s-%s-%s' % (
209            hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:])
210
211    def get_bytes(self):
212        bytes = ''
213        for shift in range(0, 128, 8):
214            bytes = chr((self.int >> shift) & 0xff) + bytes
215        return bytes
216
217    bytes = property(get_bytes)
218
219    def get_fields(self):
220        return (self.time_low, self.time_mid, self.time_hi_version,
221                self.clock_seq_hi_variant, self.clock_seq_low, self.node)
222
223    fields = property(get_fields)
224
225    def get_time_low(self):
226        return self.int >> 96L
227   
228    time_low = property(get_time_low)
229
230    def get_time_mid(self):
231        return (self.int >> 80L) & 0xffff
232
233    time_mid = property(get_time_mid)
234
235    def get_time_hi_version(self):
236        return (self.int >> 64L) & 0xffff
237    
238    time_hi_version = property(get_time_hi_version)
239
240    def get_clock_seq_hi_variant(self):
241        return (self.int >> 56L) & 0xff
242
243    clock_seq_hi_variant = property(get_clock_seq_hi_variant)
244    
245    def get_clock_seq_low(self):
246        return (self.int >> 48L) & 0xff
247
248    clock_seq_low = property(get_clock_seq_low)
249
250    def get_time(self):
251        return (((self.time_hi_version & 0x0fffL) << 48L) |
252                (self.time_mid << 32L) | self.time_low)
253
254    time = property(get_time)
255
256    def get_clock_seq(self):
257        return (((self.clock_seq_hi_variant & 0x3fL) << 8L) |
258                self.clock_seq_low)
259
260    clock_seq = property(get_clock_seq)
261    
262    def get_node(self):
263        return self.int & 0xffffffffffff
264
265    node = property(get_node)
266
267    def get_hex(self):
268        return '%032x' % self.int
269
270    hex = property(get_hex)
271
272    def get_urn(self):
273        return 'urn:uuid:' + str(self)
274
275    urn = property(get_urn)
276
277    def get_variant(self):
278        if not self.int & (0x8000 << 48L):
279            return RESERVED_NCS
280        elif not self.int & (0x4000 << 48L):
281            return RFC_4122
282        elif not self.int & (0x2000 << 48L):
283            return RESERVED_MICROSOFT
284        else:
285            return RESERVED_FUTURE
286
287    variant = property(get_variant)
288
289    def get_version(self):
290        # The version bits are only meaningful for RFC 4122 UUIDs.
291        if self.variant == RFC_4122:
292            return int((self.int >> 76L) & 0xf)
293
294    version = property(get_version)
295
296def _ifconfig_getnode():
297    """Get the hardware address on Unix by running ifconfig."""
298    import os
299    for dir in ['', '/sbin/', '/usr/sbin']:
300        try:
301            path = os.path.join(dir, 'ifconfig')
302            if os.path.exists(path):
303                pipe = os.popen(path)
304            else:
305                continue
306        except IOError:
307            continue
308        for line in pipe:
309            words = line.lower().split()
310            for i in range(len(words)):
311                if words[i] in ['hwaddr', 'ether']:
312                    return int(words[i + 1].replace(':', ''), 16)
313
314def _ipconfig_getnode():
315    """Get the hardware address on Windows by running ipconfig.exe."""
316    import os, re
317    dirs = ['', r'c:\windows\system32', r'c:\winnt\system32']
318    try:
319        import ctypes
320        buffer = ctypes.create_string_buffer(300)
321        ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300)
322        dirs.insert(0, buffer.value.decode('mbcs'))
323    except:
324        pass
325    for dir in dirs:
326        try:
327            pipe = os.popen(os.path.join(dir, 'ipconfig') + ' /all')
328        except IOError:
329            continue
330        for line in pipe:
331            value = line.split(':')[-1].strip().lower()
332            if re.match('([0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value):
333                return int(value.replace('-', ''), 16)
334
335def _netbios_getnode():
336    """Get the hardware address on Windows using NetBIOS calls.
337    See http://support.microsoft.com/kb/118623 for details."""
338    import win32wnet, netbios
339    ncb = netbios.NCB()
340    ncb.Command = netbios.NCBENUM
341    ncb.Buffer = adapters = netbios.LANA_ENUM()
342    adapters._pack()
343    if win32wnet.Netbios(ncb) != 0:
344        return
345    adapters._unpack()
346    for i in range(adapters.length):
347        ncb.Reset()
348        ncb.Command = netbios.NCBRESET
349        ncb.Lana_num = ord(adapters.lana[i])
350        if win32wnet.Netbios(ncb) != 0:
351            continue
352        ncb.Reset()
353        ncb.Command = netbios.NCBASTAT
354        ncb.Lana_num = ord(adapters.lana[i])
355        ncb.Callname = '*'.ljust(16)
356        ncb.Buffer = status = netbios.ADAPTER_STATUS()
357        if win32wnet.Netbios(ncb) != 0:
358            continue
359        status._unpack()
360        bytes = map(ord, status.adapter_address)
361        return ((bytes[0]<<40L) + (bytes[1]<<32L) + (bytes[2]<<24L) +
362                (bytes[3]<<16L) + (bytes[4]<<8L) + bytes[5])
363
364# Thanks to Thomas Heller for ctypes and for his help with its use here.
365
366# If ctypes is available, use it to find system routines for UUID generation.
367_uuid_generate_random = _uuid_generate_time = _UuidCreate = None
368try:
369    import ctypes, ctypes.util
370    _buffer = ctypes.create_string_buffer(16)
371
372    # The uuid_generate_* routines are provided by libuuid on at least
373    # Linux and FreeBSD, and provided by libc on Mac OS X.
374    for libname in ['uuid', 'c']:
375        try:
376            lib = ctypes.CDLL(ctypes.util.find_library(libname))
377        except:
378            continue
379        if hasattr(lib, 'uuid_generate_random'):
380            _uuid_generate_random = lib.uuid_generate_random
381        if hasattr(lib, 'uuid_generate_time'):
382            _uuid_generate_time = lib.uuid_generate_time
383
384    # On Windows prior to 2000, UuidCreate gives a UUID containing the
385    # hardware address.  On Windows 2000 and later, UuidCreate makes a
386    # random UUID and UuidCreateSequential gives a UUID containing the
387    # hardware address.  These routines are provided by the RPC runtime.
388    try:
389        lib = ctypes.windll.rpcrt4
390    except:
391        lib = None
392    _UuidCreate = getattr(lib, 'UuidCreateSequential',
393                          getattr(lib, 'UuidCreate', None))
394except:
395    pass
396
397def _unixdll_getnode():
398    """Get the hardware address on Unix using ctypes."""
399    _uuid_generate_time(_buffer)
400    return UUID(bytes=_buffer.raw).node
401
402def _windll_getnode():
403    """Get the hardware address on Windows using ctypes."""
404    if _UuidCreate(_buffer) == 0:
405        return UUID(bytes=_buffer.raw).node
406
407def _random_getnode():
408    """Get a random node ID, with eighth bit set as suggested by RFC 4122."""
409    import random
410    return random.randrange(0, 1<<48L) | 0x010000000000L
411
412_node = None
413
414def getnode():
415    """Get the hardware address as a 48-bit integer.  The first time this
416    runs, it may launch a separate program, which could be quite slow.  If
417    all attempts to obtain the hardware address fail, we choose a random
418    48-bit number with its eighth bit set to 1 as recommended in RFC 4122."""
419
420    global _node
421    if _node is not None:
422        return _node
423
424    import sys
425    if sys.platform == 'win32':
426        getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode]
427    else:
428        getters = [_unixdll_getnode, _ifconfig_getnode]
429
430    for getter in getters + [_random_getnode]:
431        try:
432            _node = getter()
433        except:
434            continue
435        if _node is not None:
436            return _node
437
438def uuid1(node=None, clock_seq=None):
439    """Generate a UUID from a host ID, sequence number, and the current time.
440    If 'node' is not given, getnode() is used to obtain the hardware
441    address.  If 'clock_seq' is given, it is used as the sequence number;
442    otherwise a random 14-bit sequence number is chosen."""
443
444    # When the system provides a version-1 UUID generator, use it (but don't
445    # use UuidCreate here because its UUIDs don't conform to RFC 4122).
446    if _uuid_generate_time and node is clock_seq is None:
447        _uuid_generate_time(_buffer)
448        return UUID(bytes=_buffer.raw)
449
450    import time
451    nanoseconds = int(time.time() * 1e9)
452    # 0x01b21dd213814000 is the number of 100-ns intervals between the
453    # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
454    timestamp = int(nanoseconds/100) + 0x01b21dd213814000L
455    if clock_seq is None:
456        import random
457        clock_seq = random.randrange(1<<14L) # instead of stable storage
458    time_low = timestamp & 0xffffffffL
459    time_mid = (timestamp >> 32L) & 0xffffL
460    time_hi_version = (timestamp >> 48L) & 0x0fffL
461    clock_seq_low = clock_seq & 0xffL
462    clock_seq_hi_variant = (clock_seq >> 8L) & 0x3fL
463    if node is None:
464        node = getnode()
465    return UUID(fields=(time_low, time_mid, time_hi_version,
466                        clock_seq_hi_variant, clock_seq_low, node), version=1)
467
468def uuid3(namespace, name):
469    """Generate a UUID from the MD5 hash of a namespace UUID and a name."""
470    try:
471        # Python 2.6
472        from hashlib import md5
473    except ImportError:
474        # Python 2.5 and earlier
475        from md5 import new as md5
476        
477    hash = md5(namespace.bytes + name).digest()
478    return UUID(bytes=hash[:16], version=3)
479
480def uuid4():
481    """Generate a random UUID."""
482
483    # When the system provides a version-4 UUID generator, use it.
484    if _uuid_generate_random:
485        _uuid_generate_random(_buffer)
486        return UUID(bytes=_buffer.raw)
487
488    # Otherwise, get randomness from urandom or the 'random' module.
489    try:
490        import os
491        return UUID(bytes=os.urandom(16), version=4)
492    except:
493        import random
494        bytes = [chr(random.randrange(256)) for i in range(16)]
495        return UUID(bytes=bytes, version=4)
496
497def uuid5(namespace, name):
498    """Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
499    import sha
500    hash = sha.sha(namespace.bytes + name).digest()
501    return UUID(bytes=hash[:16], version=5)
502
503# The following standard UUIDs are for use with uuid3() or uuid5().
504
505NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
506NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8')
507NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')
508NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')