1 : /* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Places code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * the Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2009
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "Helpers.h"
40 : #include "mozIStorageError.h"
41 : #include "plbase64.h"
42 : #include "prio.h"
43 : #include "nsString.h"
44 : #include "nsNavHistory.h"
45 : #include "mozilla/Services.h"
46 : #if defined(XP_OS2)
47 : #include "nsIRandomGenerator.h"
48 : #endif
49 : #include "nsContentUtils.h"
50 :
51 : // The length of guids that are used by history and bookmarks.
52 : #define GUID_LENGTH 12
53 :
54 : namespace mozilla {
55 : namespace places {
56 :
57 : ////////////////////////////////////////////////////////////////////////////////
58 : //// AsyncStatementCallback
59 :
60 25740 : NS_IMPL_ISUPPORTS1(
61 : AsyncStatementCallback
62 : , mozIStorageStatementCallback
63 : )
64 :
65 : NS_IMETHODIMP
66 0 : AsyncStatementCallback::HandleResult(mozIStorageResultSet *aResultSet)
67 : {
68 0 : NS_ABORT_IF_FALSE(false, "Was not expecting a resultset, but got it.");
69 0 : return NS_OK;
70 : }
71 :
72 : NS_IMETHODIMP
73 1252 : AsyncStatementCallback::HandleCompletion(PRUint16 aReason)
74 : {
75 1252 : return NS_OK;
76 : }
77 :
78 : NS_IMETHODIMP
79 0 : AsyncStatementCallback::HandleError(mozIStorageError *aError)
80 : {
81 : #ifdef DEBUG
82 : PRInt32 result;
83 0 : nsresult rv = aError->GetResult(&result);
84 0 : NS_ENSURE_SUCCESS(rv, rv);
85 0 : nsCAutoString message;
86 0 : rv = aError->GetMessage(message);
87 0 : NS_ENSURE_SUCCESS(rv, rv);
88 :
89 0 : nsCAutoString warnMsg;
90 0 : warnMsg.Append("An error occurred while executing an async statement: ");
91 0 : warnMsg.AppendInt(result);
92 0 : warnMsg.Append(" ");
93 0 : warnMsg.Append(message);
94 0 : NS_WARNING(warnMsg.get());
95 : #endif
96 :
97 0 : return NS_OK;
98 : }
99 :
100 : #define URI_TO_URLCSTRING(uri, spec) \
101 : nsCAutoString spec; \
102 : if (NS_FAILED(aURI->GetSpec(spec))) { \
103 : return NS_ERROR_UNEXPECTED; \
104 : }
105 :
106 : // Bind URI to statement by index.
107 : nsresult // static
108 0 : URIBinder::Bind(mozIStorageStatement* aStatement,
109 : PRInt32 aIndex,
110 : nsIURI* aURI)
111 : {
112 0 : NS_ASSERTION(aStatement, "Must have non-null statement");
113 0 : NS_ASSERTION(aURI, "Must have non-null uri");
114 :
115 0 : URI_TO_URLCSTRING(aURI, spec);
116 0 : return URIBinder::Bind(aStatement, aIndex, spec);
117 : }
118 :
119 : // Statement URLCString to statement by index.
120 : nsresult // static
121 149 : URIBinder::Bind(mozIStorageStatement* aStatement,
122 : PRInt32 index,
123 : const nsACString& aURLString)
124 : {
125 149 : NS_ASSERTION(aStatement, "Must have non-null statement");
126 : return aStatement->BindUTF8StringByIndex(
127 149 : index, StringHead(aURLString, URI_LENGTH_MAX)
128 149 : );
129 : }
130 :
131 : // Bind URI to statement by name.
132 : nsresult // static
133 7944 : URIBinder::Bind(mozIStorageStatement* aStatement,
134 : const nsACString& aName,
135 : nsIURI* aURI)
136 : {
137 7944 : NS_ASSERTION(aStatement, "Must have non-null statement");
138 7944 : NS_ASSERTION(aURI, "Must have non-null uri");
139 :
140 15888 : URI_TO_URLCSTRING(aURI, spec);
141 7944 : return URIBinder::Bind(aStatement, aName, spec);
142 : }
143 :
144 : // Bind URLCString to statement by name.
145 : nsresult // static
146 10022 : URIBinder::Bind(mozIStorageStatement* aStatement,
147 : const nsACString& aName,
148 : const nsACString& aURLString)
149 : {
150 10022 : NS_ASSERTION(aStatement, "Must have non-null statement");
151 : return aStatement->BindUTF8StringByName(
152 10022 : aName, StringHead(aURLString, URI_LENGTH_MAX)
153 10022 : );
154 : }
155 :
156 : // Bind URI to params by index.
157 : nsresult // static
158 342 : URIBinder::Bind(mozIStorageBindingParams* aParams,
159 : PRInt32 aIndex,
160 : nsIURI* aURI)
161 : {
162 342 : NS_ASSERTION(aParams, "Must have non-null statement");
163 342 : NS_ASSERTION(aURI, "Must have non-null uri");
164 :
165 684 : URI_TO_URLCSTRING(aURI, spec);
166 342 : return URIBinder::Bind(aParams, aIndex, spec);
167 : }
168 :
169 : // Bind URLCString to params by index.
170 : nsresult // static
171 342 : URIBinder::Bind(mozIStorageBindingParams* aParams,
172 : PRInt32 index,
173 : const nsACString& aURLString)
174 : {
175 342 : NS_ASSERTION(aParams, "Must have non-null statement");
176 : return aParams->BindUTF8StringByIndex(
177 342 : index, StringHead(aURLString, URI_LENGTH_MAX)
178 342 : );
179 : }
180 :
181 : // Bind URI to params by name.
182 : nsresult // static
183 81 : URIBinder::Bind(mozIStorageBindingParams* aParams,
184 : const nsACString& aName,
185 : nsIURI* aURI)
186 : {
187 81 : NS_ASSERTION(aParams, "Must have non-null params array");
188 81 : NS_ASSERTION(aURI, "Must have non-null uri");
189 :
190 162 : URI_TO_URLCSTRING(aURI, spec);
191 81 : return URIBinder::Bind(aParams, aName, spec);
192 : }
193 :
194 : // Bind URLCString to params by name.
195 : nsresult // static
196 1546 : URIBinder::Bind(mozIStorageBindingParams* aParams,
197 : const nsACString& aName,
198 : const nsACString& aURLString)
199 : {
200 1546 : NS_ASSERTION(aParams, "Must have non-null params array");
201 :
202 : nsresult rv = aParams->BindUTF8StringByName(
203 1546 : aName, StringHead(aURLString, URI_LENGTH_MAX)
204 1546 : );
205 1546 : NS_ENSURE_SUCCESS(rv, rv);
206 1546 : return NS_OK;
207 : }
208 :
209 : #undef URI_TO_URLCSTRING
210 :
211 : nsresult
212 1757 : GetReversedHostname(nsIURI* aURI, nsString& aRevHost)
213 : {
214 3514 : nsCAutoString forward8;
215 1757 : nsresult rv = aURI->GetHost(forward8);
216 : // Not all URIs have a host.
217 1757 : if (NS_FAILED(rv))
218 148 : return rv;
219 :
220 : // can't do reversing in UTF8, better use 16-bit chars
221 1609 : GetReversedHostname(NS_ConvertUTF8toUTF16(forward8), aRevHost);
222 1609 : return NS_OK;
223 : }
224 :
225 : void
226 1755 : GetReversedHostname(const nsString& aForward, nsString& aRevHost)
227 : {
228 1755 : ReverseString(aForward, aRevHost);
229 1755 : aRevHost.Append(PRUnichar('.'));
230 1755 : }
231 :
232 : void
233 23363 : ReverseString(const nsString& aInput, nsString& aReversed)
234 : {
235 23363 : aReversed.Truncate(0);
236 292014 : for (PRInt32 i = aInput.Length() - 1; i >= 0; i--) {
237 268651 : aReversed.Append(aInput[i]);
238 : }
239 23363 : }
240 :
241 : static
242 : nsresult
243 5336 : Base64urlEncode(const PRUint8* aBytes,
244 : PRUint32 aNumBytes,
245 : nsCString& _result)
246 : {
247 : // SetLength does not set aside space for NULL termination. PL_Base64Encode
248 : // will not NULL terminate, however, nsCStrings must be NULL terminated. As a
249 : // result, we set the capacity to be one greater than what we need, and the
250 : // length to our desired length.
251 5336 : PRUint32 length = (aNumBytes + 2) / 3 * 4; // +2 due to integer math.
252 5336 : NS_ENSURE_TRUE(_result.SetCapacity(length + 1), NS_ERROR_OUT_OF_MEMORY);
253 5336 : _result.SetLength(length);
254 : (void)PL_Base64Encode(reinterpret_cast<const char*>(aBytes), aNumBytes,
255 5336 : _result.BeginWriting());
256 :
257 : // base64url encoding is defined in RFC 4648. It replaces the last two
258 : // alphabet characters of base64 encoding with '-' and '_' respectively.
259 5336 : _result.ReplaceChar('+', '-');
260 5336 : _result.ReplaceChar('/', '_');
261 5336 : return NS_OK;
262 : }
263 :
264 : #ifdef XP_WIN
265 : // Included here because windows.h conflicts with the use of mozIStorageError
266 : // above.
267 : #include <windows.h>
268 : #include <wincrypt.h>
269 : #endif
270 :
271 : static
272 : nsresult
273 5336 : GenerateRandomBytes(PRUint32 aSize,
274 : PRUint8* _buffer)
275 : {
276 : // On Windows, we'll use its built-in cryptographic API.
277 : #if defined(XP_WIN)
278 : HCRYPTPROV cryptoProvider;
279 : BOOL rc = CryptAcquireContext(&cryptoProvider, 0, 0, PROV_RSA_FULL,
280 : CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
281 : if (rc) {
282 : rc = CryptGenRandom(cryptoProvider, aSize, _buffer);
283 : (void)CryptReleaseContext(cryptoProvider, 0);
284 : }
285 : return rc ? NS_OK : NS_ERROR_FAILURE;
286 :
287 : // On Unix, we'll just read in from /dev/urandom.
288 : #elif defined(XP_UNIX)
289 5336 : NS_ENSURE_ARG_MAX(aSize, PR_INT32_MAX);
290 5336 : PRFileDesc* urandom = PR_Open("/dev/urandom", PR_RDONLY, 0);
291 5336 : nsresult rv = NS_ERROR_FAILURE;
292 5336 : if (urandom) {
293 5336 : PRInt32 bytesRead = PR_Read(urandom, _buffer, aSize);
294 5336 : if (bytesRead == static_cast<PRInt32>(aSize)) {
295 5336 : rv = NS_OK;
296 : }
297 5336 : (void)PR_Close(urandom);
298 : }
299 5336 : return rv;
300 : #elif defined(XP_OS2)
301 : nsCOMPtr<nsIRandomGenerator> rg =
302 : do_GetService("@mozilla.org/security/random-generator;1");
303 : NS_ENSURE_STATE(rg);
304 :
305 : PRUint8* temp;
306 : nsresult rv = rg->GenerateRandomBytes(aSize, &temp);
307 : NS_ENSURE_SUCCESS(rv, rv);
308 : memcpy(_buffer, temp, aSize);
309 : NS_Free(temp);
310 : return NS_OK;
311 : #endif
312 : }
313 :
314 : nsresult
315 5336 : GenerateGUID(nsCString& _guid)
316 : {
317 5336 : _guid.Truncate();
318 :
319 : // Request raw random bytes and base64url encode them. For each set of three
320 : // bytes, we get one character.
321 : const PRUint32 kRequiredBytesLength =
322 5336 : static_cast<PRUint32>(GUID_LENGTH / 4 * 3);
323 :
324 : PRUint8 buffer[kRequiredBytesLength];
325 5336 : nsresult rv = GenerateRandomBytes(kRequiredBytesLength, buffer);
326 5336 : NS_ENSURE_SUCCESS(rv, rv);
327 :
328 5336 : rv = Base64urlEncode(buffer, kRequiredBytesLength, _guid);
329 5336 : NS_ENSURE_SUCCESS(rv, rv);
330 :
331 5336 : NS_ASSERTION(_guid.Length() == GUID_LENGTH, "GUID is not the right size!");
332 5336 : return NS_OK;
333 : }
334 :
335 : bool
336 471 : IsValidGUID(const nsCString& aGUID)
337 : {
338 471 : nsCString::size_type len = aGUID.Length();
339 471 : if (len != GUID_LENGTH) {
340 211 : return false;
341 : }
342 :
343 6750 : for (nsCString::size_type i = 0; i < len; i++ ) {
344 3118 : char c = aGUID[i];
345 3118 : if ((c >= 'a' && c <= 'z') || // a-z
346 : (c >= 'A' && c <= 'Z') || // A-Z
347 : (c >= '0' && c <= '9') || // 0-9
348 : c == '-' || c == '_') { // - or _
349 3115 : continue;
350 : }
351 3 : return false;
352 : }
353 257 : return true;
354 : }
355 :
356 : void
357 1782 : TruncateTitle(const nsACString& aTitle, nsACString& aTrimmed)
358 : {
359 1782 : aTrimmed = aTitle;
360 1782 : if (aTitle.Length() > TITLE_LENGTH_MAX) {
361 4 : aTrimmed = StringHead(aTitle, TITLE_LENGTH_MAX);
362 : }
363 1782 : }
364 :
365 : void
366 265 : ForceWALCheckpoint()
367 : {
368 530 : nsRefPtr<Database> DB = Database::GetDatabase();
369 265 : if (DB) {
370 : nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
371 : "pragma wal_checkpoint "
372 530 : );
373 265 : if (stmt) {
374 530 : nsCOMPtr<mozIStoragePendingStatement> handle;
375 265 : (void)stmt->ExecuteAsync(nsnull, getter_AddRefs(handle));
376 : }
377 : }
378 265 : }
379 :
380 : bool
381 695 : GetHiddenState(bool aIsRedirect,
382 : PRUint32 aTransitionType)
383 : {
384 : return aTransitionType == nsINavHistoryService::TRANSITION_FRAMED_LINK ||
385 : aTransitionType == nsINavHistoryService::TRANSITION_EMBED ||
386 695 : aIsRedirect;
387 : }
388 :
389 : ////////////////////////////////////////////////////////////////////////////////
390 : //// PlacesEvent
391 :
392 266 : PlacesEvent::PlacesEvent(const char* aTopic)
393 266 : : mTopic(aTopic)
394 : {
395 266 : }
396 :
397 : NS_IMETHODIMP
398 266 : PlacesEvent::Run()
399 : {
400 266 : Notify();
401 266 : return NS_OK;
402 : }
403 :
404 : void
405 266 : PlacesEvent::Notify()
406 : {
407 266 : NS_ASSERTION(NS_IsMainThread(), "Must only be used on the main thread!");
408 532 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
409 266 : if (obs) {
410 266 : (void)obs->NotifyObservers(nsnull, mTopic, nsnull);
411 : }
412 266 : }
413 :
414 1862 : NS_IMPL_THREADSAFE_ISUPPORTS1(
415 : PlacesEvent
416 : , nsIRunnable
417 : )
418 :
419 : ////////////////////////////////////////////////////////////////////////////////
420 : //// AsyncStatementCallbackNotifier
421 :
422 : NS_IMETHODIMP
423 4678 : AsyncStatementCallbackNotifier::HandleCompletion(PRUint16 aReason)
424 : {
425 4678 : if (aReason != mozIStorageStatementCallback::REASON_FINISHED)
426 0 : return NS_ERROR_UNEXPECTED;
427 :
428 9356 : nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
429 4678 : if (obs) {
430 4678 : (void)obs->NotifyObservers(nsnull, mTopic, nsnull);
431 : }
432 :
433 4678 : return NS_OK;
434 : }
435 :
436 : ////////////////////////////////////////////////////////////////////////////////
437 : //// AsyncStatementCallbackNotifier
438 :
439 : NS_IMETHODIMP
440 21 : AsyncStatementTelemetryTimer::HandleCompletion(PRUint16 aReason)
441 : {
442 21 : if (aReason == mozIStorageStatementCallback::REASON_FINISHED) {
443 21 : Telemetry::AccumulateTimeDelta(mHistogramId, mStart);
444 : }
445 21 : return NS_OK;
446 : }
447 :
448 : // This is a temporary converter used by nsPlacesImportExportService until
449 : // bug 482911 completes its js rewrite.
450 : jsval
451 11 : livemarkInfoToJSVal(PRInt64 aId,
452 : const nsACString& aGUID,
453 : const nsAString& aTitle,
454 : PRInt64 aParentId,
455 : PRInt32 aIndex,
456 : nsCOMPtr<nsIURI>& aFeedURI,
457 : nsCOMPtr<nsIURI>& aSiteURI)
458 : {
459 22 : nsCOMPtr<nsIXPConnect> xpc = mozilla::services::GetXPConnect();
460 11 : NS_ENSURE_TRUE(xpc, JSVAL_NULL);
461 :
462 : nsAXPCNativeCallContext *ncc;
463 11 : nsresult rv = xpc->GetCurrentNativeCallContext(&ncc);
464 11 : NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
465 11 : JSContext *cx = nsnull;
466 11 : rv = ncc->GetJSContext(&cx);
467 11 : NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
468 11 : JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
469 11 : NS_ENSURE_TRUE(obj, JSVAL_NULL);
470 :
471 : jsval id;
472 11 : NS_ENSURE_TRUE(JS_NewNumberValue(cx, double(aId), &id), JSVAL_NULL);
473 :
474 11 : JSString* guid = JS_NewStringCopyN(cx, PromiseFlatCString(aGUID).get(),
475 11 : aGUID.Length());
476 11 : NS_ENSURE_TRUE(guid, JSVAL_NULL);
477 :
478 11 : JSString* title = JS_NewUCStringCopyN(cx, PromiseFlatString(aTitle).get(),
479 11 : aTitle.Length());
480 11 : NS_ENSURE_TRUE(title, JSVAL_NULL);
481 :
482 : jsval parentId;
483 11 : NS_ENSURE_TRUE(JS_NewNumberValue(cx, double(aParentId), &parentId), JSVAL_NULL);
484 :
485 : jsval feedURI;
486 : rv = nsContentUtils::WrapNative(cx, JS_GetGlobalForScopeChain(cx),
487 11 : NS_ISUPPORTS_CAST(nsIURI*, aFeedURI), &feedURI);
488 11 : NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
489 :
490 : jsval siteURI;
491 : rv = nsContentUtils::WrapNative(cx, JS_GetGlobalForScopeChain(cx),
492 11 : NS_ISUPPORTS_CAST(nsIURI*, aSiteURI), &siteURI);
493 11 : NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
494 :
495 77 : if (!JS_DefineProperty(cx, obj, "id", id, NULL, NULL, JSPROP_ENUMERATE) ||
496 11 : !JS_DefineProperty(cx, obj, "guid", STRING_TO_JSVAL(guid), NULL, NULL, JSPROP_ENUMERATE) ||
497 11 : !JS_DefineProperty(cx, obj, "title", STRING_TO_JSVAL(title), NULL, NULL, JSPROP_ENUMERATE) ||
498 11 : !JS_DefineProperty(cx, obj, "parentId", parentId, NULL, NULL, JSPROP_ENUMERATE) ||
499 11 : !JS_DefineProperty(cx, obj, "index", INT_TO_JSVAL(aIndex), NULL, NULL, JSPROP_ENUMERATE) ||
500 11 : !JS_DefineProperty(cx, obj, "feedURI", feedURI, NULL, NULL, JSPROP_ENUMERATE) ||
501 11 : !JS_DefineProperty(cx, obj, "siteURI", siteURI, NULL, NULL, JSPROP_ENUMERATE)) {
502 0 : return JSVAL_NULL;
503 : }
504 11 : return OBJECT_TO_JSVAL(obj);
505 : }
506 :
507 : } // namespace places
508 : } // namespace mozilla
|