/** * @file lluuid.cpp * * $LicenseInfo:firstyear=2000&license=viewergpl$ * * Copyright (c) 2000-2009, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "linden_common.h" // We can't use WIN32_LEAN_AND_MEAN here, needs lots of includes. #if LL_WINDOWS #undef WIN32_LEAN_AND_MEAN #include <winsock2.h> #include <windows.h> // ugh, this is ugly. We need to straighten out our linking for this library #pragma comment(lib, "IPHLPAPI.lib") #include <iphlpapi.h> #endif #include "lldefs.h" #include "llerror.h" #include "lluuid.h" #include "llerror.h" #include "llrand.h" #include "llmd5.h" #include "llstring.h" #include "lltimer.h" const LLUUID LLUUID::null; const LLTransactionID LLTransactionID::tnull; /* NOT DONE YET!!! static char BASE85_TABLE[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '!', '#', '$', '%', '&', '(', ')', '*', '+', '-', ';', '[', '=', '>', '?', '@', '^', '_', '`', '{', '|', '}', '~', '\0' }; void encode( char * fiveChars, unsigned int word ) throw( ) { for( int ix = 0; ix < 5; ++ix ) { fiveChars[4-ix] = encodeTable[ word % 85]; word /= 85; } } To decode: unsigned int decode( char const * fiveChars ) throw( bad_input_data ) { unsigned int ret = 0; for( int ix = 0; ix < 5; ++ix ) { char * s = strchr( encodeTable, fiveChars[ ix ] ); if( s == 0 ) throw bad_input_data(); ret = ret * 85 + (s-encodeTable); } return ret; } void LLUUID::toBase85(char* out) { U32* me = (U32*)&(mData[0]); for(S32 i = 0; i < 4; ++i) { char* o = &out[i*i]; for(S32 j = 0; j < 5; ++j) { o[4-j] = BASE85_TABLE[ me[i] % 85]; word /= 85; } } } unsigned int decode( char const * fiveChars ) throw( bad_input_data ) { unsigned int ret = 0; for( S32 ix = 0; ix < 5; ++ix ) { char * s = strchr( encodeTable, fiveChars[ ix ] ); ret = ret * 85 + (s-encodeTable); } return ret; } */ #define LL_USE_JANKY_RANDOM_NUMBER_GENERATOR 0 #if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR /** * @brief a global for */ static U64 sJankyRandomSeed(LLUUID::getRandomSeed()); /** * @brief generate a random U32. */ U32 janky_fast_random_bytes() { sJankyRandomSeed = U64L(1664525) * sJankyRandomSeed + U64L(1013904223); return (U32)sJankyRandomSeed; } /** * @brief generate a random U32 from [0, val) */ U32 janky_fast_random_byes_range(U32 val) { sJankyRandomSeed = U64L(1664525) * sJankyRandomSeed + U64L(1013904223); return (U32)(sJankyRandomSeed) % val; } /** * @brief generate a random U32 from [0, val) */ U32 janky_fast_random_seeded_bytes(U32 seed, U32 val) { seed = U64L(1664525) * (U64)(seed) + U64L(1013904223); return (U32)(seed) % val; } #endif // Common to all UUID implementations void LLUUID::toString(std::string& out) const { out = llformat( "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", (U8)(mData[0]), (U8)(mData[1]), (U8)(mData[2]), (U8)(mData[3]), (U8)(mData[4]), (U8)(mData[5]), (U8)(mData[6]), (U8)(mData[7]), (U8)(mData[8]), (U8)(mData[9]), (U8)(mData[10]), (U8)(mData[11]), (U8)(mData[12]), (U8)(mData[13]), (U8)(mData[14]), (U8)(mData[15])); } // *TODO: deprecate void LLUUID::toString(char *out) const { std::string buffer; toString(buffer); strcpy(out,buffer.c_str()); /* Flawfinder: ignore */ } void LLUUID::toCompressedString(std::string& out) const { char bytes[UUID_BYTES+1]; memcpy(bytes, mData, UUID_BYTES); /* Flawfinder: ignore */ bytes[UUID_BYTES] = '\0'; out.assign(bytes, UUID_BYTES); } // *TODO: deprecate void LLUUID::toCompressedString(char *out) const { memcpy(out, mData, UUID_BYTES); /* Flawfinder: ignore */ out[UUID_BYTES] = '\0'; } std::string LLUUID::getString() const { return asString(); } std::string LLUUID::asString() const { std::string str; toString(str); return str; } BOOL LLUUID::set(const char* in_string, BOOL emit) { return set(ll_safe_string(in_string),emit); } BOOL LLUUID::set(const std::string& in_string, BOOL emit) { BOOL broken_format = FALSE; // empty strings should make NULL uuid if (in_string.empty()) { setNull(); return TRUE; } if (in_string.length() != (UUID_STR_LENGTH - 1)) /* Flawfinder: ignore */ { // I'm a moron. First implementation didn't have the right UUID format. // Shouldn't see any of these any more if (in_string.length() == (UUID_STR_LENGTH - 2)) /* Flawfinder: ignore */ { if(emit) { llwarns << "Warning! Using broken UUID string format" << llendl; } broken_format = TRUE; } else { // Bad UUID string. Spam as INFO, as most cases we don't care. if(emit) { //don't spam the logs because a resident can't spell. llwarns << "Bad UUID string: " << in_string << llendl; } setNull(); return FALSE; } } U8 cur_pos = 0; S32 i; for (i = 0; i < UUID_BYTES; i++) { if ((i == 4) || (i == 6) || (i == 8) || (i == 10)) { cur_pos++; if (broken_format && (i==10)) { // Missing - in the broken format cur_pos--; } } mData[i] = 0; if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9')) { mData[i] += (U8)(in_string[cur_pos] - '0'); } else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <='f')) { mData[i] += (U8)(10 + in_string[cur_pos] - 'a'); } else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <='F')) { mData[i] += (U8)(10 + in_string[cur_pos] - 'A'); } else { if(emit) { llwarns << "Invalid UUID string character" << llendl; } setNull(); return FALSE; } mData[i] = mData[i] << 4; cur_pos++; if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9')) { mData[i] += (U8)(in_string[cur_pos] - '0'); } else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <='f')) { mData[i] += (U8)(10 + in_string[cur_pos] - 'a'); } else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <='F')) { mData[i] += (U8)(10 + in_string[cur_pos] - 'A'); } else { if(emit) { llwarns << "Invalid UUID string character" << llendl; } setNull(); return FALSE; } cur_pos++; } return TRUE; } BOOL LLUUID::validate(const std::string& in_string) { BOOL broken_format = FALSE; if (in_string.length() != (UUID_STR_LENGTH - 1)) /* Flawfinder: ignore */ { // I'm a moron. First implementation didn't have the right UUID format. if (in_string.length() == (UUID_STR_LENGTH - 2)) /* Flawfinder: ignore */ { broken_format = TRUE; } else { return FALSE; } } U8 cur_pos = 0; for (U32 i = 0; i < 16; i++) { if ((i == 4) || (i == 6) || (i == 8) || (i == 10)) { cur_pos++; if (broken_format && (i==10)) { // Missing - in the broken format cur_pos--; } } if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9')) { } else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <='f')) { } else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <='F')) { } else { return FALSE; } cur_pos++; if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9')) { } else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <='f')) { } else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <='F')) { } else { return FALSE; } cur_pos++; } return TRUE; } const LLUUID& LLUUID::operator^=(const LLUUID& rhs) { U32* me = (U32*)&(mData[0]); const U32* other = (U32*)&(rhs.mData[0]); for(S32 i = 0; i < 4; ++i) { me[i] = me[i] ^ other[i]; } return *this; } LLUUID LLUUID::operator^(const LLUUID& rhs) const { LLUUID id(*this); id ^= rhs; return id; } void LLUUID::combine(const LLUUID& other, LLUUID& result) const { LLMD5 md5_uuid; md5_uuid.update((unsigned char*)mData, 16); md5_uuid.update((unsigned char*)other.mData, 16); md5_uuid.finalize(); md5_uuid.raw_digest(result.mData); } LLUUID LLUUID::combine(const LLUUID &other) const { LLUUID combination; combine(other, combination); return combination; } std::ostream& operator<<(std::ostream& s, const LLUUID &uuid) { std::string uuid_str; uuid.toString(uuid_str); s << uuid_str; return s; } std::istream& operator>>(std::istream &s, LLUUID &uuid) { U32 i; char uuid_str[UUID_STR_LENGTH]; /* Flawfinder: ignore */ for (i = 0; i < UUID_STR_LENGTH-1; i++) { s >> uuid_str[i]; } uuid_str[i] = '\0'; uuid.set(std::string(uuid_str)); return s; } static void get_random_bytes(void *buf, int nbytes) { int i; char *cp = (char *) buf; // *NOTE: If we are not using the janky generator ll_rand() // generates at least 3 good bytes of data since it is 0 to // RAND_MAX. This could be made more efficient by copying all the // bytes. for (i=0; i < nbytes; i++) #if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR *cp++ = janky_fast_random_bytes() & 0xFF; #else *cp++ = ll_rand() & 0xFF; #endif return; } #if LL_WINDOWS // Code copied from http://msdn.microsoft.com/en-us/library/aa365939(VS.85).aspx // This code grabs the first hardware address, rather than the first interface. // Using a VPN can cause the first returned interface to be changed. const S32 MAC_ADDRESS_BYTES=6; // static S32 LLUUID::getNodeID(unsigned char *node_id) { // Declare and initialize variables. DWORD dwSize = 0; DWORD dwRetVal = 0; int i; /* variables used for GetIfTable and GetIfEntry */ MIB_IFTABLE *pIfTable; MIB_IFROW *pIfRow; // Allocate memory for our pointers. pIfTable = (MIB_IFTABLE *) malloc(sizeof (MIB_IFTABLE)); if (pIfTable == NULL) { printf("Error allocating memory needed to call GetIfTable\n"); return 0; } // Before calling GetIfEntry, we call GetIfTable to make // sure there are entries to get and retrieve the interface index. // Make an initial call to GetIfTable to get the // necessary size into dwSize if (GetIfTable(pIfTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { free(pIfTable); pIfTable = (MIB_IFTABLE *) malloc(dwSize); if (pIfTable == NULL) { printf("Error allocating memory\n"); return 0; } } // Make a second call to GetIfTable to get the actual // data we want. if ((dwRetVal = GetIfTable(pIfTable, &dwSize, 0)) == NO_ERROR) { if (pIfTable->dwNumEntries > 0) { pIfRow = (MIB_IFROW *) malloc(sizeof (MIB_IFROW)); if (pIfRow == NULL) { printf("Error allocating memory\n"); if (pIfTable != NULL) { free(pIfTable); pIfTable = NULL; } return 0; } int limit = MAC_ADDRESS_BYTES; memcpy(node_id, "\0\0\0\0\0\0", limit); // zero out array of bytes for (i = 0; i < (int) pIfTable->dwNumEntries; i++) { pIfRow->dwIndex = pIfTable->table[i].dwIndex; if ((dwRetVal = GetIfEntry(pIfRow)) == NO_ERROR) { switch (pIfRow->dwType) { case IF_TYPE_ETHERNET_CSMACD: case IF_TYPE_IEEE80211: limit = min((int) pIfRow->dwPhysAddrLen, limit); if (pIfRow->dwPhysAddrLen == 0) break; memcpy(node_id, (UCHAR *)&pIfRow->bPhysAddr[0], limit); // just incase the PhysAddr is not the expected MAC_Address size free(pIfTable); return 1; //return first hardware device found. break; case IF_TYPE_OTHER: case IF_TYPE_PPP: case IF_TYPE_SOFTWARE_LOOPBACK: case IF_TYPE_ISO88025_TOKENRING: case IF_TYPE_IEEE1394: case IF_TYPE_ATM: case IF_TYPE_TUNNEL: default: break; } } } } } free(pIfTable); return 0; } #elif LL_DARWIN // Mac OS X version of the UUID generation code... /* * Get an ethernet hardware address, if we can find it... */ #include <unistd.h> #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #include <net/if_types.h> #include <net/if_dl.h> #include <net/route.h> #include <ifaddrs.h> // static S32 LLUUID::getNodeID(unsigned char *node_id) { int i; unsigned char *a = NULL; struct ifaddrs *ifap, *ifa; int rv; S32 result = 0; if ((rv=getifaddrs(&ifap))==-1) { return -1; } if (ifap == NULL) { return -1; } for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { // printf("Interface %s, address family %d, ", ifa->ifa_name, ifa->ifa_addr->sa_family); for(i=0; i< ifa->ifa_addr->sa_len; i++) { // printf("%02X ", (unsigned char)ifa->ifa_addr->sa_data[i]); } // printf("\n"); if(ifa->ifa_addr->sa_family == AF_LINK) { // This is a link-level address struct sockaddr_dl *lla = (struct sockaddr_dl *)ifa->ifa_addr; // printf("\tLink level address, type %02X\n", lla->sdl_type); if(lla->sdl_type == IFT_ETHER) { // Use the first ethernet MAC in the list. // For some reason, the macro LLADDR() defined in net/if_dl.h doesn't expand correctly. This is what it would do. a = (unsigned char *)&((lla)->sdl_data); a += (lla)->sdl_nlen; if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) { continue; } if (node_id) { memcpy(node_id, a, 6); result = 1; } // We found one. break; } } } freeifaddrs(ifap); return result; } #else // Linux version of the UUID generation code... /* * Get the ethernet hardware address, if we can find it... */ #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/time.h> #include <sys/stat.h> #include <sys/file.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <net/if.h> #define HAVE_NETINET_IN_H #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #if LL_SOLARIS #include <sys/sockio.h> #elif !LL_DARWIN #include <linux/sockios.h> #endif #endif // static S32 LLUUID::getNodeID(unsigned char *node_id) { int sd; struct ifreq ifr, *ifrp; struct ifconf ifc; char buf[1024]; int n, i; unsigned char *a; /* * BSD 4.4 defines the size of an ifreq to be * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len * However, under earlier systems, sa_len isn't present, so the size is * just sizeof(struct ifreq) */ #ifdef HAVE_SA_LEN #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #endif #define ifreq_size(i) max(sizeof(struct ifreq),\ sizeof((i).ifr_name)+(i).ifr_addr.sa_len) #else #define ifreq_size(i) sizeof(struct ifreq) #endif /* HAVE_SA_LEN*/ sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (sd < 0) { return -1; } memset(buf, 0, sizeof(buf)); ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) { close(sd); return -1; } n = ifc.ifc_len; for (i = 0; i < n; i+= ifreq_size(*ifr) ) { ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i); strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); /* Flawfinder: ignore */ #ifdef SIOCGIFHWADDR if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) continue; a = (unsigned char *) &ifr.ifr_hwaddr.sa_data; #else #ifdef SIOCGENADDR if (ioctl(sd, SIOCGENADDR, &ifr) < 0) continue; a = (unsigned char *) ifr.ifr_enaddr; #else /* * XXX we don't have a way of getting the hardware * address */ close(sd); return 0; #endif /* SIOCGENADDR */ #endif /* SIOCGIFHWADDR */ if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) continue; if (node_id) { memcpy(node_id, a, 6); /* Flawfinder: ignore */ close(sd); return 1; } } close(sd); return 0; } #endif S32 LLUUID::cmpTime(uuid_time_t *t1, uuid_time_t *t2) { // Compare two time values. if (t1->high < t2->high) return -1; if (t1->high > t2->high) return 1; if (t1->low < t2->low) return -1; if (t1->low > t2->low) return 1; return 0; } void LLUUID::getSystemTime(uuid_time_t *timestamp) { // Get system time with 100ns precision. Time is since Oct 15, 1582. #if LL_WINDOWS ULARGE_INTEGER time; GetSystemTimeAsFileTime((FILETIME *)&time); // NT keeps time in FILETIME format which is 100ns ticks since // Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582. // The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec) // + 18 years and 5 leap days. time.QuadPart += (unsigned __int64) (1000*1000*10) // seconds * (unsigned __int64) (60 * 60 * 24) // days * (unsigned __int64) (17+30+31+365*18+5); // # of days timestamp->high = time.HighPart; timestamp->low = time.LowPart; #else struct timeval tp; gettimeofday(&tp, 0); // Offset between UUID formatted times and Unix formatted times. // UUID UTC base time is October 15, 1582. // Unix base time is January 1, 1970. U64 uuid_time = ((U64)tp.tv_sec * 10000000) + (tp.tv_usec * 10) + U64L(0x01B21DD213814000); timestamp->high = (U32) (uuid_time >> 32); timestamp->low = (U32) (uuid_time & 0xFFFFFFFF); #endif } void LLUUID::getCurrentTime(uuid_time_t *timestamp) { // Get current time as 60 bit 100ns ticks since whenever. // Compensate for the fact that real clock resolution is less // than 100ns. const U32 uuids_per_tick = 1024; static uuid_time_t time_last; static U32 uuids_this_tick; static BOOL init = FALSE; if (!init) { getSystemTime(&time_last); uuids_this_tick = uuids_per_tick; init = TRUE; } uuid_time_t time_now = {0,0}; while (1) { getSystemTime(&time_now); // if clock reading changed since last UUID generated if (cmpTime(&time_last, &time_now)) { // reset count of uuid's generated with this clock reading uuids_this_tick = 0; break; } if (uuids_this_tick < uuids_per_tick) { uuids_this_tick++; break; } // going too fast for our clock; spin } time_last = time_now; if (uuids_this_tick != 0) { if (time_now.low & 0x80000000) { time_now.low += uuids_this_tick; if (!(time_now.low & 0x80000000)) time_now.high++; } else time_now.low += uuids_this_tick; } timestamp->high = time_now.high; timestamp->low = time_now.low; } void LLUUID::generate() { // Create a UUID. uuid_time_t timestamp; static unsigned char node_id[6]; /* Flawfinder: ignore */ static int has_init = 0; // Create a UUID. static uuid_time_t time_last = {0,0}; static U16 clock_seq = 0; #if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR static U32 seed = 0L; // dummy seed. reset it below #endif if (!has_init) { if (getNodeID(node_id) <= 0) { get_random_bytes(node_id, 6); /* * Set multicast bit, to prevent conflicts * with IEEE 802 addresses obtained from * network cards */ node_id[0] |= 0x80; } getCurrentTime(&time_last); #if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR seed = time_last.low; #endif #if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR clock_seq = (U16)janky_fast_random_seeded_bytes(seed, 65536); #else clock_seq = (U16)ll_rand(65536); #endif has_init = 1; } // get current time getCurrentTime(×tamp); // if clock went backward change clockseq if (cmpTime(×tamp, &time_last) == -1) { clock_seq = (clock_seq + 1) & 0x3FFF; if (clock_seq == 0) clock_seq++; } memcpy(mData+10, node_id, 6); /* Flawfinder: ignore */ U32 tmp; tmp = timestamp.low; mData[3] = (unsigned char) tmp; tmp >>= 8; mData[2] = (unsigned char) tmp; tmp >>= 8; mData[1] = (unsigned char) tmp; tmp >>= 8; mData[0] = (unsigned char) tmp; tmp = (U16) timestamp.high; mData[5] = (unsigned char) tmp; tmp >>= 8; mData[4] = (unsigned char) tmp; tmp = (timestamp.high >> 16) | 0x1000; mData[7] = (unsigned char) tmp; tmp >>= 8; mData[6] = (unsigned char) tmp; tmp = clock_seq; mData[9] = (unsigned char) tmp; tmp >>= 8; mData[8] = (unsigned char) tmp; LLMD5 md5_uuid; md5_uuid.update(mData,16); md5_uuid.finalize(); md5_uuid.raw_digest(mData); time_last = timestamp; } void LLUUID::generate(const std::string& hash_string) { LLMD5 md5_uuid((U8*)hash_string.c_str()); md5_uuid.raw_digest(mData); } U32 LLUUID::getRandomSeed() { static unsigned char seed[16]; /* Flawfinder: ignore */ getNodeID(&seed[0]); seed[6]='\0'; seed[7]='\0'; getSystemTime((uuid_time_t *)(&seed[8])); LLMD5 md5_seed; md5_seed.update(seed,16); md5_seed.finalize(); md5_seed.raw_digest(seed); return(*(U32 *)seed); } BOOL LLUUID::parseUUID(const std::string& buf, LLUUID* value) { if( buf.empty() || value == NULL) { return FALSE; } std::string temp( buf ); LLStringUtil::trim(temp); if( LLUUID::validate( temp ) ) { value->set( temp ); return TRUE; } return FALSE; } //static LLUUID LLUUID::generateNewID(std::string hash_string) { LLUUID new_id; if (hash_string.empty()) { new_id.generate(); } else { new_id.generate(hash_string); } return new_id; } LLAssetID LLTransactionID::makeAssetID(const LLUUID& session) const { LLAssetID result; if (isNull()) { result.setNull(); } else { combine(session, result); } return result; }