|
1 /* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "Helpers.h" |
|
7 #include "mozIStorageError.h" |
|
8 #include "plbase64.h" |
|
9 #include "prio.h" |
|
10 #include "nsString.h" |
|
11 #include "nsNavHistory.h" |
|
12 #include "mozilla/Services.h" |
|
13 |
|
14 // The length of guids that are used by history and bookmarks. |
|
15 #define GUID_LENGTH 12 |
|
16 |
|
17 namespace mozilla { |
|
18 namespace places { |
|
19 |
|
20 //////////////////////////////////////////////////////////////////////////////// |
|
21 //// AsyncStatementCallback |
|
22 |
|
23 NS_IMPL_ISUPPORTS( |
|
24 AsyncStatementCallback |
|
25 , mozIStorageStatementCallback |
|
26 ) |
|
27 |
|
28 NS_IMETHODIMP |
|
29 AsyncStatementCallback::HandleResult(mozIStorageResultSet *aResultSet) |
|
30 { |
|
31 NS_ABORT_IF_FALSE(false, "Was not expecting a resultset, but got it."); |
|
32 return NS_OK; |
|
33 } |
|
34 |
|
35 NS_IMETHODIMP |
|
36 AsyncStatementCallback::HandleCompletion(uint16_t aReason) |
|
37 { |
|
38 return NS_OK; |
|
39 } |
|
40 |
|
41 NS_IMETHODIMP |
|
42 AsyncStatementCallback::HandleError(mozIStorageError *aError) |
|
43 { |
|
44 #ifdef DEBUG |
|
45 int32_t result; |
|
46 nsresult rv = aError->GetResult(&result); |
|
47 NS_ENSURE_SUCCESS(rv, rv); |
|
48 nsAutoCString message; |
|
49 rv = aError->GetMessage(message); |
|
50 NS_ENSURE_SUCCESS(rv, rv); |
|
51 |
|
52 nsAutoCString warnMsg; |
|
53 warnMsg.Append("An error occurred while executing an async statement: "); |
|
54 warnMsg.AppendInt(result); |
|
55 warnMsg.Append(" "); |
|
56 warnMsg.Append(message); |
|
57 NS_WARNING(warnMsg.get()); |
|
58 #endif |
|
59 |
|
60 return NS_OK; |
|
61 } |
|
62 |
|
63 #define URI_TO_URLCSTRING(uri, spec) \ |
|
64 nsAutoCString spec; \ |
|
65 if (NS_FAILED(aURI->GetSpec(spec))) { \ |
|
66 return NS_ERROR_UNEXPECTED; \ |
|
67 } |
|
68 |
|
69 // Bind URI to statement by index. |
|
70 nsresult // static |
|
71 URIBinder::Bind(mozIStorageStatement* aStatement, |
|
72 int32_t aIndex, |
|
73 nsIURI* aURI) |
|
74 { |
|
75 NS_ASSERTION(aStatement, "Must have non-null statement"); |
|
76 NS_ASSERTION(aURI, "Must have non-null uri"); |
|
77 |
|
78 URI_TO_URLCSTRING(aURI, spec); |
|
79 return URIBinder::Bind(aStatement, aIndex, spec); |
|
80 } |
|
81 |
|
82 // Statement URLCString to statement by index. |
|
83 nsresult // static |
|
84 URIBinder::Bind(mozIStorageStatement* aStatement, |
|
85 int32_t index, |
|
86 const nsACString& aURLString) |
|
87 { |
|
88 NS_ASSERTION(aStatement, "Must have non-null statement"); |
|
89 return aStatement->BindUTF8StringByIndex( |
|
90 index, StringHead(aURLString, URI_LENGTH_MAX) |
|
91 ); |
|
92 } |
|
93 |
|
94 // Bind URI to statement by name. |
|
95 nsresult // static |
|
96 URIBinder::Bind(mozIStorageStatement* aStatement, |
|
97 const nsACString& aName, |
|
98 nsIURI* aURI) |
|
99 { |
|
100 NS_ASSERTION(aStatement, "Must have non-null statement"); |
|
101 NS_ASSERTION(aURI, "Must have non-null uri"); |
|
102 |
|
103 URI_TO_URLCSTRING(aURI, spec); |
|
104 return URIBinder::Bind(aStatement, aName, spec); |
|
105 } |
|
106 |
|
107 // Bind URLCString to statement by name. |
|
108 nsresult // static |
|
109 URIBinder::Bind(mozIStorageStatement* aStatement, |
|
110 const nsACString& aName, |
|
111 const nsACString& aURLString) |
|
112 { |
|
113 NS_ASSERTION(aStatement, "Must have non-null statement"); |
|
114 return aStatement->BindUTF8StringByName( |
|
115 aName, StringHead(aURLString, URI_LENGTH_MAX) |
|
116 ); |
|
117 } |
|
118 |
|
119 // Bind URI to params by index. |
|
120 nsresult // static |
|
121 URIBinder::Bind(mozIStorageBindingParams* aParams, |
|
122 int32_t aIndex, |
|
123 nsIURI* aURI) |
|
124 { |
|
125 NS_ASSERTION(aParams, "Must have non-null statement"); |
|
126 NS_ASSERTION(aURI, "Must have non-null uri"); |
|
127 |
|
128 URI_TO_URLCSTRING(aURI, spec); |
|
129 return URIBinder::Bind(aParams, aIndex, spec); |
|
130 } |
|
131 |
|
132 // Bind URLCString to params by index. |
|
133 nsresult // static |
|
134 URIBinder::Bind(mozIStorageBindingParams* aParams, |
|
135 int32_t index, |
|
136 const nsACString& aURLString) |
|
137 { |
|
138 NS_ASSERTION(aParams, "Must have non-null statement"); |
|
139 return aParams->BindUTF8StringByIndex( |
|
140 index, StringHead(aURLString, URI_LENGTH_MAX) |
|
141 ); |
|
142 } |
|
143 |
|
144 // Bind URI to params by name. |
|
145 nsresult // static |
|
146 URIBinder::Bind(mozIStorageBindingParams* aParams, |
|
147 const nsACString& aName, |
|
148 nsIURI* aURI) |
|
149 { |
|
150 NS_ASSERTION(aParams, "Must have non-null params array"); |
|
151 NS_ASSERTION(aURI, "Must have non-null uri"); |
|
152 |
|
153 URI_TO_URLCSTRING(aURI, spec); |
|
154 return URIBinder::Bind(aParams, aName, spec); |
|
155 } |
|
156 |
|
157 // Bind URLCString to params by name. |
|
158 nsresult // static |
|
159 URIBinder::Bind(mozIStorageBindingParams* aParams, |
|
160 const nsACString& aName, |
|
161 const nsACString& aURLString) |
|
162 { |
|
163 NS_ASSERTION(aParams, "Must have non-null params array"); |
|
164 |
|
165 nsresult rv = aParams->BindUTF8StringByName( |
|
166 aName, StringHead(aURLString, URI_LENGTH_MAX) |
|
167 ); |
|
168 NS_ENSURE_SUCCESS(rv, rv); |
|
169 return NS_OK; |
|
170 } |
|
171 |
|
172 #undef URI_TO_URLCSTRING |
|
173 |
|
174 nsresult |
|
175 GetReversedHostname(nsIURI* aURI, nsString& aRevHost) |
|
176 { |
|
177 nsAutoCString forward8; |
|
178 nsresult rv = aURI->GetHost(forward8); |
|
179 // Not all URIs have a host. |
|
180 if (NS_FAILED(rv)) |
|
181 return rv; |
|
182 |
|
183 // can't do reversing in UTF8, better use 16-bit chars |
|
184 GetReversedHostname(NS_ConvertUTF8toUTF16(forward8), aRevHost); |
|
185 return NS_OK; |
|
186 } |
|
187 |
|
188 void |
|
189 GetReversedHostname(const nsString& aForward, nsString& aRevHost) |
|
190 { |
|
191 ReverseString(aForward, aRevHost); |
|
192 aRevHost.Append(char16_t('.')); |
|
193 } |
|
194 |
|
195 void |
|
196 ReverseString(const nsString& aInput, nsString& aReversed) |
|
197 { |
|
198 aReversed.Truncate(0); |
|
199 for (int32_t i = aInput.Length() - 1; i >= 0; i--) { |
|
200 aReversed.Append(aInput[i]); |
|
201 } |
|
202 } |
|
203 |
|
204 static |
|
205 nsresult |
|
206 Base64urlEncode(const uint8_t* aBytes, |
|
207 uint32_t aNumBytes, |
|
208 nsCString& _result) |
|
209 { |
|
210 // SetLength does not set aside space for null termination. PL_Base64Encode |
|
211 // will not null terminate, however, nsCStrings must be null terminated. As a |
|
212 // result, we set the capacity to be one greater than what we need, and the |
|
213 // length to our desired length. |
|
214 uint32_t length = (aNumBytes + 2) / 3 * 4; // +2 due to integer math. |
|
215 NS_ENSURE_TRUE(_result.SetCapacity(length + 1, fallible_t()), |
|
216 NS_ERROR_OUT_OF_MEMORY); |
|
217 _result.SetLength(length); |
|
218 (void)PL_Base64Encode(reinterpret_cast<const char*>(aBytes), aNumBytes, |
|
219 _result.BeginWriting()); |
|
220 |
|
221 // base64url encoding is defined in RFC 4648. It replaces the last two |
|
222 // alphabet characters of base64 encoding with '-' and '_' respectively. |
|
223 _result.ReplaceChar('+', '-'); |
|
224 _result.ReplaceChar('/', '_'); |
|
225 return NS_OK; |
|
226 } |
|
227 |
|
228 #ifdef XP_WIN |
|
229 } // namespace places |
|
230 } // namespace mozilla |
|
231 |
|
232 // Included here because windows.h conflicts with the use of mozIStorageError |
|
233 // above, but make sure that these are not included inside mozilla::places. |
|
234 #include <windows.h> |
|
235 #include <wincrypt.h> |
|
236 |
|
237 namespace mozilla { |
|
238 namespace places { |
|
239 #endif |
|
240 |
|
241 static |
|
242 nsresult |
|
243 GenerateRandomBytes(uint32_t aSize, |
|
244 uint8_t* _buffer) |
|
245 { |
|
246 // On Windows, we'll use its built-in cryptographic API. |
|
247 #if defined(XP_WIN) |
|
248 HCRYPTPROV cryptoProvider; |
|
249 BOOL rc = CryptAcquireContext(&cryptoProvider, 0, 0, PROV_RSA_FULL, |
|
250 CRYPT_VERIFYCONTEXT | CRYPT_SILENT); |
|
251 if (rc) { |
|
252 rc = CryptGenRandom(cryptoProvider, aSize, _buffer); |
|
253 (void)CryptReleaseContext(cryptoProvider, 0); |
|
254 } |
|
255 return rc ? NS_OK : NS_ERROR_FAILURE; |
|
256 |
|
257 // On Unix, we'll just read in from /dev/urandom. |
|
258 #elif defined(XP_UNIX) |
|
259 NS_ENSURE_ARG_MAX(aSize, INT32_MAX); |
|
260 PRFileDesc* urandom = PR_Open("/dev/urandom", PR_RDONLY, 0); |
|
261 nsresult rv = NS_ERROR_FAILURE; |
|
262 if (urandom) { |
|
263 int32_t bytesRead = PR_Read(urandom, _buffer, aSize); |
|
264 if (bytesRead == static_cast<int32_t>(aSize)) { |
|
265 rv = NS_OK; |
|
266 } |
|
267 (void)PR_Close(urandom); |
|
268 } |
|
269 return rv; |
|
270 #endif |
|
271 } |
|
272 |
|
273 nsresult |
|
274 GenerateGUID(nsCString& _guid) |
|
275 { |
|
276 _guid.Truncate(); |
|
277 |
|
278 // Request raw random bytes and base64url encode them. For each set of three |
|
279 // bytes, we get one character. |
|
280 const uint32_t kRequiredBytesLength = |
|
281 static_cast<uint32_t>(GUID_LENGTH / 4 * 3); |
|
282 |
|
283 uint8_t buffer[kRequiredBytesLength]; |
|
284 nsresult rv = GenerateRandomBytes(kRequiredBytesLength, buffer); |
|
285 NS_ENSURE_SUCCESS(rv, rv); |
|
286 |
|
287 rv = Base64urlEncode(buffer, kRequiredBytesLength, _guid); |
|
288 NS_ENSURE_SUCCESS(rv, rv); |
|
289 |
|
290 NS_ASSERTION(_guid.Length() == GUID_LENGTH, "GUID is not the right size!"); |
|
291 return NS_OK; |
|
292 } |
|
293 |
|
294 bool |
|
295 IsValidGUID(const nsACString& aGUID) |
|
296 { |
|
297 nsCString::size_type len = aGUID.Length(); |
|
298 if (len != GUID_LENGTH) { |
|
299 return false; |
|
300 } |
|
301 |
|
302 for (nsCString::size_type i = 0; i < len; i++ ) { |
|
303 char c = aGUID[i]; |
|
304 if ((c >= 'a' && c <= 'z') || // a-z |
|
305 (c >= 'A' && c <= 'Z') || // A-Z |
|
306 (c >= '0' && c <= '9') || // 0-9 |
|
307 c == '-' || c == '_') { // - or _ |
|
308 continue; |
|
309 } |
|
310 return false; |
|
311 } |
|
312 return true; |
|
313 } |
|
314 |
|
315 void |
|
316 TruncateTitle(const nsACString& aTitle, nsACString& aTrimmed) |
|
317 { |
|
318 aTrimmed = aTitle; |
|
319 if (aTitle.Length() > TITLE_LENGTH_MAX) { |
|
320 aTrimmed = StringHead(aTitle, TITLE_LENGTH_MAX); |
|
321 } |
|
322 } |
|
323 |
|
324 void |
|
325 ForceWALCheckpoint() |
|
326 { |
|
327 nsRefPtr<Database> DB = Database::GetDatabase(); |
|
328 if (DB) { |
|
329 nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement( |
|
330 "pragma wal_checkpoint " |
|
331 ); |
|
332 if (stmt) { |
|
333 nsCOMPtr<mozIStoragePendingStatement> handle; |
|
334 (void)stmt->ExecuteAsync(nullptr, getter_AddRefs(handle)); |
|
335 } |
|
336 } |
|
337 } |
|
338 |
|
339 bool |
|
340 GetHiddenState(bool aIsRedirect, |
|
341 uint32_t aTransitionType) |
|
342 { |
|
343 return aTransitionType == nsINavHistoryService::TRANSITION_FRAMED_LINK || |
|
344 aTransitionType == nsINavHistoryService::TRANSITION_EMBED || |
|
345 aIsRedirect; |
|
346 } |
|
347 |
|
348 //////////////////////////////////////////////////////////////////////////////// |
|
349 //// PlacesEvent |
|
350 |
|
351 PlacesEvent::PlacesEvent(const char* aTopic) |
|
352 : mTopic(aTopic) |
|
353 { |
|
354 } |
|
355 |
|
356 NS_IMETHODIMP |
|
357 PlacesEvent::Run() |
|
358 { |
|
359 Notify(); |
|
360 return NS_OK; |
|
361 } |
|
362 |
|
363 void |
|
364 PlacesEvent::Notify() |
|
365 { |
|
366 NS_ASSERTION(NS_IsMainThread(), "Must only be used on the main thread!"); |
|
367 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
|
368 if (obs) { |
|
369 (void)obs->NotifyObservers(nullptr, mTopic, nullptr); |
|
370 } |
|
371 } |
|
372 |
|
373 NS_IMPL_ISUPPORTS( |
|
374 PlacesEvent |
|
375 , nsIRunnable |
|
376 ) |
|
377 |
|
378 //////////////////////////////////////////////////////////////////////////////// |
|
379 //// AsyncStatementCallbackNotifier |
|
380 |
|
381 NS_IMETHODIMP |
|
382 AsyncStatementCallbackNotifier::HandleCompletion(uint16_t aReason) |
|
383 { |
|
384 if (aReason != mozIStorageStatementCallback::REASON_FINISHED) |
|
385 return NS_ERROR_UNEXPECTED; |
|
386 |
|
387 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
|
388 if (obs) { |
|
389 (void)obs->NotifyObservers(nullptr, mTopic, nullptr); |
|
390 } |
|
391 |
|
392 return NS_OK; |
|
393 } |
|
394 |
|
395 //////////////////////////////////////////////////////////////////////////////// |
|
396 //// AsyncStatementCallbackNotifier |
|
397 |
|
398 NS_IMETHODIMP |
|
399 AsyncStatementTelemetryTimer::HandleCompletion(uint16_t aReason) |
|
400 { |
|
401 if (aReason == mozIStorageStatementCallback::REASON_FINISHED) { |
|
402 Telemetry::AccumulateTimeDelta(mHistogramId, mStart); |
|
403 } |
|
404 return NS_OK; |
|
405 } |
|
406 |
|
407 } // namespace places |
|
408 } // namespace mozilla |