|
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 "mozilla/storage.h" |
|
7 #include "mozilla/storage/Variant.h" |
|
8 #include "mozilla/mozalloc.h" |
|
9 #include "nsString.h" |
|
10 #include "SQLFunctions.h" |
|
11 #include "nsUTF8Utils.h" |
|
12 #include "plbase64.h" |
|
13 #include "prio.h" |
|
14 |
|
15 // The length of guids that are used by the download manager |
|
16 #define GUID_LENGTH 12 |
|
17 |
|
18 namespace mozilla { |
|
19 namespace downloads { |
|
20 |
|
21 // Keep this file in sync with the GUID-related code in toolkit/places/SQLFunctions.cpp |
|
22 // and toolkit/places/Helpers.cpp! |
|
23 |
|
24 //////////////////////////////////////////////////////////////////////////////// |
|
25 //// GUID Creation Function |
|
26 |
|
27 ////////////////////////////////////////////////////////////////////////////// |
|
28 //// GenerateGUIDFunction |
|
29 |
|
30 /* static */ |
|
31 nsresult |
|
32 GenerateGUIDFunction::create(mozIStorageConnection *aDBConn) |
|
33 { |
|
34 nsRefPtr<GenerateGUIDFunction> function = new GenerateGUIDFunction(); |
|
35 nsresult rv = aDBConn->CreateFunction( |
|
36 NS_LITERAL_CSTRING("generate_guid"), 0, function |
|
37 ); |
|
38 NS_ENSURE_SUCCESS(rv, rv); |
|
39 |
|
40 return NS_OK; |
|
41 } |
|
42 |
|
43 NS_IMPL_ISUPPORTS( |
|
44 GenerateGUIDFunction, |
|
45 mozIStorageFunction |
|
46 ) |
|
47 |
|
48 static |
|
49 nsresult |
|
50 Base64urlEncode(const uint8_t* aBytes, |
|
51 uint32_t aNumBytes, |
|
52 nsCString& _result) |
|
53 { |
|
54 // SetLength does not set aside space for null termination. PL_Base64Encode |
|
55 // will not null terminate, however, nsCStrings must be null terminated. As a |
|
56 // result, we set the capacity to be one greater than what we need, and the |
|
57 // length to our desired length. |
|
58 uint32_t length = (aNumBytes + 2) / 3 * 4; // +2 due to integer math. |
|
59 NS_ENSURE_TRUE(_result.SetCapacity(length + 1, mozilla::fallible_t()), |
|
60 NS_ERROR_OUT_OF_MEMORY); |
|
61 _result.SetLength(length); |
|
62 (void)PL_Base64Encode(reinterpret_cast<const char*>(aBytes), aNumBytes, |
|
63 _result.BeginWriting()); |
|
64 |
|
65 // base64url encoding is defined in RFC 4648. It replaces the last two |
|
66 // alphabet characters of base64 encoding with '-' and '_' respectively. |
|
67 _result.ReplaceChar('+', '-'); |
|
68 _result.ReplaceChar('/', '_'); |
|
69 return NS_OK; |
|
70 } |
|
71 |
|
72 #ifdef XP_WIN |
|
73 // Included here because windows.h conflicts with the use of mozIStorageError |
|
74 // above. |
|
75 #include <windows.h> |
|
76 #include <wincrypt.h> |
|
77 #endif |
|
78 |
|
79 static |
|
80 nsresult |
|
81 GenerateRandomBytes(uint32_t aSize, |
|
82 uint8_t* _buffer) |
|
83 { |
|
84 // On Windows, we'll use its built-in cryptographic API. |
|
85 #if defined(XP_WIN) |
|
86 HCRYPTPROV cryptoProvider; |
|
87 BOOL rc = CryptAcquireContext(&cryptoProvider, 0, 0, PROV_RSA_FULL, |
|
88 CRYPT_VERIFYCONTEXT | CRYPT_SILENT); |
|
89 if (rc) { |
|
90 rc = CryptGenRandom(cryptoProvider, aSize, _buffer); |
|
91 (void)CryptReleaseContext(cryptoProvider, 0); |
|
92 } |
|
93 return rc ? NS_OK : NS_ERROR_FAILURE; |
|
94 |
|
95 // On Unix, we'll just read in from /dev/urandom. |
|
96 #elif defined(XP_UNIX) |
|
97 NS_ENSURE_ARG_MAX(aSize, INT32_MAX); |
|
98 PRFileDesc* urandom = PR_Open("/dev/urandom", PR_RDONLY, 0); |
|
99 nsresult rv = NS_ERROR_FAILURE; |
|
100 if (urandom) { |
|
101 int32_t bytesRead = PR_Read(urandom, _buffer, aSize); |
|
102 if (bytesRead == static_cast<int32_t>(aSize)) { |
|
103 rv = NS_OK; |
|
104 } |
|
105 (void)PR_Close(urandom); |
|
106 } |
|
107 return rv; |
|
108 #endif |
|
109 } |
|
110 |
|
111 nsresult |
|
112 GenerateGUID(nsCString& _guid) |
|
113 { |
|
114 _guid.Truncate(); |
|
115 |
|
116 // Request raw random bytes and base64url encode them. For each set of three |
|
117 // bytes, we get one character. |
|
118 const uint32_t kRequiredBytesLength = |
|
119 static_cast<uint32_t>(GUID_LENGTH / 4 * 3); |
|
120 |
|
121 uint8_t buffer[kRequiredBytesLength]; |
|
122 nsresult rv = GenerateRandomBytes(kRequiredBytesLength, buffer); |
|
123 NS_ENSURE_SUCCESS(rv, rv); |
|
124 |
|
125 rv = Base64urlEncode(buffer, kRequiredBytesLength, _guid); |
|
126 NS_ENSURE_SUCCESS(rv, rv); |
|
127 |
|
128 NS_ASSERTION(_guid.Length() == GUID_LENGTH, "GUID is not the right size!"); |
|
129 return NS_OK; |
|
130 } |
|
131 |
|
132 ////////////////////////////////////////////////////////////////////////////// |
|
133 //// mozIStorageFunction |
|
134 |
|
135 NS_IMETHODIMP |
|
136 GenerateGUIDFunction::OnFunctionCall(mozIStorageValueArray *aArguments, |
|
137 nsIVariant **_result) |
|
138 { |
|
139 nsAutoCString guid; |
|
140 nsresult rv = GenerateGUID(guid); |
|
141 NS_ENSURE_SUCCESS(rv, rv); |
|
142 |
|
143 NS_ADDREF(*_result = new mozilla::storage::UTF8TextVariant(guid)); |
|
144 return NS_OK; |
|
145 } |
|
146 |
|
147 } // namespace mozilla |
|
148 } // namespace downloads |