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 "Helpers.h" michael@0: #include "mozIStorageError.h" michael@0: #include "plbase64.h" michael@0: #include "prio.h" michael@0: #include "nsString.h" michael@0: #include "nsNavHistory.h" michael@0: #include "mozilla/Services.h" michael@0: michael@0: // The length of guids that are used by history and bookmarks. michael@0: #define GUID_LENGTH 12 michael@0: michael@0: namespace mozilla { michael@0: namespace places { michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// AsyncStatementCallback michael@0: michael@0: NS_IMPL_ISUPPORTS( michael@0: AsyncStatementCallback michael@0: , mozIStorageStatementCallback michael@0: ) michael@0: michael@0: NS_IMETHODIMP michael@0: AsyncStatementCallback::HandleResult(mozIStorageResultSet *aResultSet) michael@0: { michael@0: NS_ABORT_IF_FALSE(false, "Was not expecting a resultset, but got it."); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AsyncStatementCallback::HandleCompletion(uint16_t aReason) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AsyncStatementCallback::HandleError(mozIStorageError *aError) michael@0: { michael@0: #ifdef DEBUG michael@0: int32_t result; michael@0: nsresult rv = aError->GetResult(&result); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsAutoCString message; michael@0: rv = aError->GetMessage(message); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString warnMsg; michael@0: warnMsg.Append("An error occurred while executing an async statement: "); michael@0: warnMsg.AppendInt(result); michael@0: warnMsg.Append(" "); michael@0: warnMsg.Append(message); michael@0: NS_WARNING(warnMsg.get()); michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #define URI_TO_URLCSTRING(uri, spec) \ michael@0: nsAutoCString spec; \ michael@0: if (NS_FAILED(aURI->GetSpec(spec))) { \ michael@0: return NS_ERROR_UNEXPECTED; \ michael@0: } michael@0: michael@0: // Bind URI to statement by index. michael@0: nsresult // static michael@0: URIBinder::Bind(mozIStorageStatement* aStatement, michael@0: int32_t aIndex, michael@0: nsIURI* aURI) michael@0: { michael@0: NS_ASSERTION(aStatement, "Must have non-null statement"); michael@0: NS_ASSERTION(aURI, "Must have non-null uri"); michael@0: michael@0: URI_TO_URLCSTRING(aURI, spec); michael@0: return URIBinder::Bind(aStatement, aIndex, spec); michael@0: } michael@0: michael@0: // Statement URLCString to statement by index. michael@0: nsresult // static michael@0: URIBinder::Bind(mozIStorageStatement* aStatement, michael@0: int32_t index, michael@0: const nsACString& aURLString) michael@0: { michael@0: NS_ASSERTION(aStatement, "Must have non-null statement"); michael@0: return aStatement->BindUTF8StringByIndex( michael@0: index, StringHead(aURLString, URI_LENGTH_MAX) michael@0: ); michael@0: } michael@0: michael@0: // Bind URI to statement by name. michael@0: nsresult // static michael@0: URIBinder::Bind(mozIStorageStatement* aStatement, michael@0: const nsACString& aName, michael@0: nsIURI* aURI) michael@0: { michael@0: NS_ASSERTION(aStatement, "Must have non-null statement"); michael@0: NS_ASSERTION(aURI, "Must have non-null uri"); michael@0: michael@0: URI_TO_URLCSTRING(aURI, spec); michael@0: return URIBinder::Bind(aStatement, aName, spec); michael@0: } michael@0: michael@0: // Bind URLCString to statement by name. michael@0: nsresult // static michael@0: URIBinder::Bind(mozIStorageStatement* aStatement, michael@0: const nsACString& aName, michael@0: const nsACString& aURLString) michael@0: { michael@0: NS_ASSERTION(aStatement, "Must have non-null statement"); michael@0: return aStatement->BindUTF8StringByName( michael@0: aName, StringHead(aURLString, URI_LENGTH_MAX) michael@0: ); michael@0: } michael@0: michael@0: // Bind URI to params by index. michael@0: nsresult // static michael@0: URIBinder::Bind(mozIStorageBindingParams* aParams, michael@0: int32_t aIndex, michael@0: nsIURI* aURI) michael@0: { michael@0: NS_ASSERTION(aParams, "Must have non-null statement"); michael@0: NS_ASSERTION(aURI, "Must have non-null uri"); michael@0: michael@0: URI_TO_URLCSTRING(aURI, spec); michael@0: return URIBinder::Bind(aParams, aIndex, spec); michael@0: } michael@0: michael@0: // Bind URLCString to params by index. michael@0: nsresult // static michael@0: URIBinder::Bind(mozIStorageBindingParams* aParams, michael@0: int32_t index, michael@0: const nsACString& aURLString) michael@0: { michael@0: NS_ASSERTION(aParams, "Must have non-null statement"); michael@0: return aParams->BindUTF8StringByIndex( michael@0: index, StringHead(aURLString, URI_LENGTH_MAX) michael@0: ); michael@0: } michael@0: michael@0: // Bind URI to params by name. michael@0: nsresult // static michael@0: URIBinder::Bind(mozIStorageBindingParams* aParams, michael@0: const nsACString& aName, michael@0: nsIURI* aURI) michael@0: { michael@0: NS_ASSERTION(aParams, "Must have non-null params array"); michael@0: NS_ASSERTION(aURI, "Must have non-null uri"); michael@0: michael@0: URI_TO_URLCSTRING(aURI, spec); michael@0: return URIBinder::Bind(aParams, aName, spec); michael@0: } michael@0: michael@0: // Bind URLCString to params by name. michael@0: nsresult // static michael@0: URIBinder::Bind(mozIStorageBindingParams* aParams, michael@0: const nsACString& aName, michael@0: const nsACString& aURLString) michael@0: { michael@0: NS_ASSERTION(aParams, "Must have non-null params array"); michael@0: michael@0: nsresult rv = aParams->BindUTF8StringByName( michael@0: aName, StringHead(aURLString, URI_LENGTH_MAX) michael@0: ); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #undef URI_TO_URLCSTRING michael@0: michael@0: nsresult michael@0: GetReversedHostname(nsIURI* aURI, nsString& aRevHost) michael@0: { michael@0: nsAutoCString forward8; michael@0: nsresult rv = aURI->GetHost(forward8); michael@0: // Not all URIs have a host. michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // can't do reversing in UTF8, better use 16-bit chars michael@0: GetReversedHostname(NS_ConvertUTF8toUTF16(forward8), aRevHost); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: GetReversedHostname(const nsString& aForward, nsString& aRevHost) michael@0: { michael@0: ReverseString(aForward, aRevHost); michael@0: aRevHost.Append(char16_t('.')); michael@0: } michael@0: michael@0: void michael@0: ReverseString(const nsString& aInput, nsString& aReversed) michael@0: { michael@0: aReversed.Truncate(0); michael@0: for (int32_t i = aInput.Length() - 1; i >= 0; i--) { michael@0: aReversed.Append(aInput[i]); michael@0: } 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, 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: } // namespace places michael@0: } // namespace mozilla michael@0: michael@0: // Included here because windows.h conflicts with the use of mozIStorageError michael@0: // above, but make sure that these are not included inside mozilla::places. michael@0: #include michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: namespace places { 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: bool michael@0: IsValidGUID(const nsACString& aGUID) michael@0: { michael@0: nsCString::size_type len = aGUID.Length(); michael@0: if (len != GUID_LENGTH) { michael@0: return false; michael@0: } michael@0: michael@0: for (nsCString::size_type i = 0; i < len; i++ ) { michael@0: char c = aGUID[i]; michael@0: if ((c >= 'a' && c <= 'z') || // a-z michael@0: (c >= 'A' && c <= 'Z') || // A-Z michael@0: (c >= '0' && c <= '9') || // 0-9 michael@0: c == '-' || c == '_') { // - or _ michael@0: continue; michael@0: } michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: TruncateTitle(const nsACString& aTitle, nsACString& aTrimmed) michael@0: { michael@0: aTrimmed = aTitle; michael@0: if (aTitle.Length() > TITLE_LENGTH_MAX) { michael@0: aTrimmed = StringHead(aTitle, TITLE_LENGTH_MAX); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ForceWALCheckpoint() michael@0: { michael@0: nsRefPtr DB = Database::GetDatabase(); michael@0: if (DB) { michael@0: nsCOMPtr stmt = DB->GetAsyncStatement( michael@0: "pragma wal_checkpoint " michael@0: ); michael@0: if (stmt) { michael@0: nsCOMPtr handle; michael@0: (void)stmt->ExecuteAsync(nullptr, getter_AddRefs(handle)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: GetHiddenState(bool aIsRedirect, michael@0: uint32_t aTransitionType) michael@0: { michael@0: return aTransitionType == nsINavHistoryService::TRANSITION_FRAMED_LINK || michael@0: aTransitionType == nsINavHistoryService::TRANSITION_EMBED || michael@0: aIsRedirect; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// PlacesEvent michael@0: michael@0: PlacesEvent::PlacesEvent(const char* aTopic) michael@0: : mTopic(aTopic) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PlacesEvent::Run() michael@0: { michael@0: Notify(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: PlacesEvent::Notify() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Must only be used on the main thread!"); michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: if (obs) { michael@0: (void)obs->NotifyObservers(nullptr, mTopic, nullptr); michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS( michael@0: PlacesEvent michael@0: , nsIRunnable michael@0: ) michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// AsyncStatementCallbackNotifier michael@0: michael@0: NS_IMETHODIMP michael@0: AsyncStatementCallbackNotifier::HandleCompletion(uint16_t aReason) michael@0: { michael@0: if (aReason != mozIStorageStatementCallback::REASON_FINISHED) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: if (obs) { michael@0: (void)obs->NotifyObservers(nullptr, mTopic, nullptr); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// AsyncStatementCallbackNotifier michael@0: michael@0: NS_IMETHODIMP michael@0: AsyncStatementTelemetryTimer::HandleCompletion(uint16_t aReason) michael@0: { michael@0: if (aReason == mozIStorageStatementCallback::REASON_FINISHED) { michael@0: Telemetry::AccumulateTimeDelta(mHistogramId, mStart); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace places michael@0: } // namespace mozilla