Back to index

lightning-sunbird  0.9+nobinonly
nsUUIDGenerator.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * mozilla.org
00019  * Portions created by the Initial Developer are Copyright (C) 2005
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Vladimir Vukicevic <vladimir@pobox.com> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #if defined(XP_WIN)
00040 #include <windows.h>
00041 #include <objbase.h>
00042 #elif defined(XP_MACOSX)
00043 #include <CoreFoundation/CoreFoundation.h>
00044 #else
00045 #include <stdlib.h>
00046 #include "prrng.h"
00047 #endif
00048 
00049 #include "nsMemory.h"
00050 
00051 #include "nsAutoLock.h"
00052 
00053 #include "nsUUIDGenerator.h"
00054 
00055 NS_IMPL_THREADSAFE_ISUPPORTS1(nsUUIDGenerator, nsIUUIDGenerator)
00056 
00057 nsUUIDGenerator::nsUUIDGenerator()
00058     : mLock(nsnull)
00059 {
00060 }
00061 
00062 nsUUIDGenerator::~nsUUIDGenerator()
00063 {
00064     if (mLock) {
00065         PR_DestroyLock(mLock);
00066     }
00067 }
00068 
00069 nsresult
00070 nsUUIDGenerator::Init()
00071 {
00072     mLock = PR_NewLock();
00073 
00074     NS_ENSURE_TRUE(mLock, NS_ERROR_OUT_OF_MEMORY);
00075 
00076     // No need to acquire our lock while we mess with stuff here.  If we can't
00077     // depend on reasonable locking around Init we're screwed with the
00078     // assign-and-check above, anyway.
00079     
00080 #if !defined(XP_WIN) && !defined(XP_MACOSX)
00081     /* initialize random number generator using NSPR random noise */
00082     unsigned int seed;
00083 
00084     PRSize bytes = 0;
00085     while (bytes < sizeof(seed)) {
00086         PRSize nbytes = PR_GetRandomNoise(((unsigned char *)&seed)+bytes,
00087                                           sizeof(seed)-bytes);
00088         if (nbytes == 0) {
00089             return NS_ERROR_FAILURE;
00090         }
00091         bytes += nbytes;
00092     }
00093 
00094     /* Initialize a new RNG state, and immediately switch
00095      * back to the previous one -- we want to use mState
00096      * only for our own calls to random().
00097      */
00098     mSavedState = initstate(seed, mState, sizeof(mState));
00099     setstate(mSavedState);
00100 
00101     mRBytes = 4;
00102 #ifdef RAND_MAX
00103     if ((unsigned long) RAND_MAX < (unsigned long)0xffffffff)
00104         mRBytes = 3;
00105     if ((unsigned long) RAND_MAX < (unsigned long)0x00ffffff)
00106         mRBytes = 2;
00107     if ((unsigned long) RAND_MAX < (unsigned long)0x0000ffff)
00108         mRBytes = 1;
00109     if ((unsigned long) RAND_MAX < (unsigned long)0x000000ff)
00110         return NS_ERROR_FAILURE;
00111 #endif
00112 
00113 #endif /* non XP_WIN and non XP_MACOSX */
00114 
00115     return NS_OK;
00116 }
00117 
00118 NS_IMETHODIMP
00119 nsUUIDGenerator::GenerateUUID(nsID** ret)
00120 {
00121     nsID *id = NS_STATIC_CAST(nsID*, NS_Alloc(sizeof(nsID)));
00122     if (id == nsnull)
00123         return NS_ERROR_OUT_OF_MEMORY;
00124 
00125     nsresult rv = GenerateUUIDInPlace(id);
00126     if (NS_FAILED(rv)) {
00127         NS_Free(id);
00128         return rv;
00129     }
00130 
00131     *ret = id;
00132     return rv;
00133 }
00134 
00135 NS_IMETHODIMP
00136 nsUUIDGenerator::GenerateUUIDInPlace(nsID* id)
00137 {
00138     // The various code in this method is probably not threadsafe, so lock
00139     // across the whole method.
00140     nsAutoLock lock(mLock);
00141     
00142 #if defined(XP_WIN)
00143     HRESULT hr = CoCreateGuid((GUID*)id);
00144     if (NS_FAILED(hr))
00145         return NS_ERROR_FAILURE;
00146 #elif defined(XP_MACOSX)
00147     CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
00148     if (!uuid)
00149         return NS_ERROR_FAILURE;
00150 
00151     CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
00152     memcpy(id, &bytes, sizeof(nsID));
00153 
00154     CFRelease(uuid);
00155 #else /* not windows or OS X; generate randomness using random(). */
00156     /* XXX we should be saving the return of setstate here and switching
00157      * back to it; instead, we use the value returned when we called
00158      * initstate, since older glibc's have broken setstate() return values
00159      */
00160     setstate(mState);
00161 
00162     PRSize bytesLeft = sizeof(nsID);
00163     while (bytesLeft > 0) {
00164         long rval = random();
00165 
00166         PRUint8 *src = (PRUint8*)&rval;
00167         // We want to grab the mRBytes least significant bytes of rval, since
00168         // mRBytes less than sizeof(rval) means the high bytes are 0.
00169 #ifdef IS_BIG_ENDIAN
00170         src += sizeof(rval) - mRBytes;
00171 #endif
00172         PRUint8 *dst = ((PRUint8*) id) + (sizeof(nsID) - bytesLeft);
00173         PRSize toWrite = (bytesLeft < mRBytes ? bytesLeft : mRBytes);
00174         for (PRSize i = 0; i < toWrite; i++)
00175             dst[i] = src[i];
00176 
00177         bytesLeft -= toWrite;
00178     }
00179 
00180     /* Put in the version */
00181     id->m2 &= 0x0fff;
00182     id->m2 |= 0x4000;
00183 
00184     /* Put in the variant */
00185     id->m3[0] &= 0x3f;
00186     id->m3[0] |= 0x80;
00187 
00188     /* Restore the previous RNG state */
00189     setstate(mSavedState);
00190 #endif
00191 
00192     return NS_OK;
00193 }