michael@0: /* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/storage.h" michael@0: #include "mozilla/storage/Variant.h" michael@0: #include "mozilla/mozalloc.h" michael@0: #include "nsString.h" michael@0: #include "SQLFunctions.h" michael@0: #include "nsUTF8Utils.h" michael@0: #include "plbase64.h" michael@0: #include "prio.h" michael@0: michael@0: // The length of guids that are used by the download manager michael@0: #define GUID_LENGTH 12 michael@0: michael@0: namespace mozilla { michael@0: namespace downloads { michael@0: michael@0: // Keep this file in sync with the GUID-related code in toolkit/places/SQLFunctions.cpp michael@0: // and toolkit/places/Helpers.cpp! michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// GUID Creation Function michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: //// GenerateGUIDFunction michael@0: michael@0: /* static */ michael@0: nsresult michael@0: GenerateGUIDFunction::create(mozIStorageConnection *aDBConn) michael@0: { michael@0: nsRefPtr function = new GenerateGUIDFunction(); michael@0: nsresult rv = aDBConn->CreateFunction( michael@0: NS_LITERAL_CSTRING("generate_guid"), 0, function michael@0: ); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS( michael@0: GenerateGUIDFunction, michael@0: mozIStorageFunction michael@0: ) michael@0: michael@0: static michael@0: nsresult michael@0: Base64urlEncode(const uint8_t* aBytes, michael@0: uint32_t aNumBytes, michael@0: nsCString& _result) michael@0: { michael@0: // SetLength does not set aside space for null termination. PL_Base64Encode michael@0: // will not null terminate, however, nsCStrings must be null terminated. As a michael@0: // result, we set the capacity to be one greater than what we need, and the michael@0: // length to our desired length. michael@0: uint32_t length = (aNumBytes + 2) / 3 * 4; // +2 due to integer math. michael@0: NS_ENSURE_TRUE(_result.SetCapacity(length + 1, mozilla::fallible_t()), michael@0: NS_ERROR_OUT_OF_MEMORY); michael@0: _result.SetLength(length); michael@0: (void)PL_Base64Encode(reinterpret_cast(aBytes), aNumBytes, michael@0: _result.BeginWriting()); michael@0: michael@0: // base64url encoding is defined in RFC 4648. It replaces the last two michael@0: // alphabet characters of base64 encoding with '-' and '_' respectively. michael@0: _result.ReplaceChar('+', '-'); michael@0: _result.ReplaceChar('/', '_'); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: // Included here because windows.h conflicts with the use of mozIStorageError michael@0: // above. michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: static michael@0: nsresult michael@0: GenerateRandomBytes(uint32_t aSize, michael@0: uint8_t* _buffer) michael@0: { michael@0: // On Windows, we'll use its built-in cryptographic API. michael@0: #if defined(XP_WIN) michael@0: HCRYPTPROV cryptoProvider; michael@0: BOOL rc = CryptAcquireContext(&cryptoProvider, 0, 0, PROV_RSA_FULL, michael@0: CRYPT_VERIFYCONTEXT | CRYPT_SILENT); michael@0: if (rc) { michael@0: rc = CryptGenRandom(cryptoProvider, aSize, _buffer); michael@0: (void)CryptReleaseContext(cryptoProvider, 0); michael@0: } michael@0: return rc ? NS_OK : NS_ERROR_FAILURE; michael@0: michael@0: // On Unix, we'll just read in from /dev/urandom. michael@0: #elif defined(XP_UNIX) michael@0: NS_ENSURE_ARG_MAX(aSize, INT32_MAX); michael@0: PRFileDesc* urandom = PR_Open("/dev/urandom", PR_RDONLY, 0); michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: if (urandom) { michael@0: int32_t bytesRead = PR_Read(urandom, _buffer, aSize); michael@0: if (bytesRead == static_cast(aSize)) { michael@0: rv = NS_OK; michael@0: } michael@0: (void)PR_Close(urandom); michael@0: } michael@0: return rv; michael@0: #endif michael@0: } michael@0: michael@0: nsresult michael@0: GenerateGUID(nsCString& _guid) michael@0: { michael@0: _guid.Truncate(); michael@0: michael@0: // Request raw random bytes and base64url encode them. For each set of three michael@0: // bytes, we get one character. michael@0: const uint32_t kRequiredBytesLength = michael@0: static_cast(GUID_LENGTH / 4 * 3); michael@0: michael@0: uint8_t buffer[kRequiredBytesLength]; michael@0: nsresult rv = GenerateRandomBytes(kRequiredBytesLength, buffer); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = Base64urlEncode(buffer, kRequiredBytesLength, _guid); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ASSERTION(_guid.Length() == GUID_LENGTH, "GUID is not the right size!"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: //// mozIStorageFunction michael@0: michael@0: NS_IMETHODIMP michael@0: GenerateGUIDFunction::OnFunctionCall(mozIStorageValueArray *aArguments, michael@0: nsIVariant **_result) michael@0: { michael@0: nsAutoCString guid; michael@0: nsresult rv = GenerateGUID(guid); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ADDREF(*_result = new mozilla::storage::UTF8TextVariant(guid)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: } // namespace downloads