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