Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "mozilla/Attributes.h" |
michael@0 | 7 | #include "mozilla/DebugOnly.h" |
michael@0 | 8 | |
michael@0 | 9 | #include "mozilla/dom/ContentParent.h" |
michael@0 | 10 | #include "mozilla/dom/ContentChild.h" |
michael@0 | 11 | #include "mozilla/unused.h" |
michael@0 | 12 | #include "nsPermissionManager.h" |
michael@0 | 13 | #include "nsPermission.h" |
michael@0 | 14 | #include "nsCRT.h" |
michael@0 | 15 | #include "nsNetUtil.h" |
michael@0 | 16 | #include "nsCOMArray.h" |
michael@0 | 17 | #include "nsArrayEnumerator.h" |
michael@0 | 18 | #include "nsTArray.h" |
michael@0 | 19 | #include "nsReadableUtils.h" |
michael@0 | 20 | #include "nsILineInputStream.h" |
michael@0 | 21 | #include "nsIIDNService.h" |
michael@0 | 22 | #include "nsAppDirectoryServiceDefs.h" |
michael@0 | 23 | #include "prprf.h" |
michael@0 | 24 | #include "mozilla/storage.h" |
michael@0 | 25 | #include "mozilla/Attributes.h" |
michael@0 | 26 | #include "nsXULAppAPI.h" |
michael@0 | 27 | #include "nsIPrincipal.h" |
michael@0 | 28 | #include "nsContentUtils.h" |
michael@0 | 29 | #include "nsIScriptSecurityManager.h" |
michael@0 | 30 | #include "nsIAppsService.h" |
michael@0 | 31 | #include "mozIApplication.h" |
michael@0 | 32 | #include "nsIEffectiveTLDService.h" |
michael@0 | 33 | #include "nsPIDOMWindow.h" |
michael@0 | 34 | #include "nsIDocument.h" |
michael@0 | 35 | #include "nsCOMPtr.h" |
michael@0 | 36 | #include "nsIPrefService.h" |
michael@0 | 37 | #include "nsIPrefBranch.h" |
michael@0 | 38 | #include "nsIPrefBranch2.h" |
michael@0 | 39 | #include "mozilla/net/NeckoMessageUtils.h" |
michael@0 | 40 | |
michael@0 | 41 | static nsPermissionManager *gPermissionManager = nullptr; |
michael@0 | 42 | |
michael@0 | 43 | using mozilla::dom::ContentParent; |
michael@0 | 44 | using mozilla::dom::ContentChild; |
michael@0 | 45 | using mozilla::unused; // ha! |
michael@0 | 46 | |
michael@0 | 47 | static bool |
michael@0 | 48 | IsChildProcess() |
michael@0 | 49 | { |
michael@0 | 50 | return XRE_GetProcessType() == GeckoProcessType_Content; |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | /** |
michael@0 | 54 | * @returns The child process object, or if we are not in the child |
michael@0 | 55 | * process, nullptr. |
michael@0 | 56 | */ |
michael@0 | 57 | static ContentChild* |
michael@0 | 58 | ChildProcess() |
michael@0 | 59 | { |
michael@0 | 60 | if (IsChildProcess()) { |
michael@0 | 61 | ContentChild* cpc = ContentChild::GetSingleton(); |
michael@0 | 62 | if (!cpc) |
michael@0 | 63 | NS_RUNTIMEABORT("Content Process is nullptr!"); |
michael@0 | 64 | return cpc; |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | return nullptr; |
michael@0 | 68 | } |
michael@0 | 69 | |
michael@0 | 70 | |
michael@0 | 71 | #define ENSURE_NOT_CHILD_PROCESS_(onError) \ |
michael@0 | 72 | PR_BEGIN_MACRO \ |
michael@0 | 73 | if (IsChildProcess()) { \ |
michael@0 | 74 | NS_ERROR("Cannot perform action in content process!"); \ |
michael@0 | 75 | onError \ |
michael@0 | 76 | } \ |
michael@0 | 77 | PR_END_MACRO |
michael@0 | 78 | |
michael@0 | 79 | #define ENSURE_NOT_CHILD_PROCESS \ |
michael@0 | 80 | ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; }) |
michael@0 | 81 | |
michael@0 | 82 | #define ENSURE_NOT_CHILD_PROCESS_NORET \ |
michael@0 | 83 | ENSURE_NOT_CHILD_PROCESS_(;) |
michael@0 | 84 | |
michael@0 | 85 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 86 | |
michael@0 | 87 | namespace { |
michael@0 | 88 | |
michael@0 | 89 | nsresult |
michael@0 | 90 | GetPrincipal(const nsACString& aHost, uint32_t aAppId, bool aIsInBrowserElement, |
michael@0 | 91 | nsIPrincipal** aPrincipal) |
michael@0 | 92 | { |
michael@0 | 93 | nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); |
michael@0 | 94 | NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE); |
michael@0 | 95 | |
michael@0 | 96 | nsCOMPtr<nsIURI> uri; |
michael@0 | 97 | nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost); |
michael@0 | 98 | if (NS_FAILED(rv)) { |
michael@0 | 99 | // NOTE: most callers will end up here because we don't append "http://" for |
michael@0 | 100 | // hosts. It's fine to arbitrary use "http://" because, for those entries, |
michael@0 | 101 | // we will actually just use the host. If we end up here, but the host looks |
michael@0 | 102 | // like an email address, we use mailto: instead. |
michael@0 | 103 | nsCString scheme; |
michael@0 | 104 | if (aHost.FindChar('@') == -1) |
michael@0 | 105 | scheme = NS_LITERAL_CSTRING("http://"); |
michael@0 | 106 | else |
michael@0 | 107 | scheme = NS_LITERAL_CSTRING("mailto:"); |
michael@0 | 108 | rv = NS_NewURI(getter_AddRefs(uri), scheme + aHost); |
michael@0 | 109 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 110 | } |
michael@0 | 111 | |
michael@0 | 112 | return secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, aPrincipal); |
michael@0 | 113 | } |
michael@0 | 114 | |
michael@0 | 115 | nsresult |
michael@0 | 116 | GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal) |
michael@0 | 117 | { |
michael@0 | 118 | nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); |
michael@0 | 119 | NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE); |
michael@0 | 120 | |
michael@0 | 121 | return secMan->GetNoAppCodebasePrincipal(aURI, aPrincipal); |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | nsresult |
michael@0 | 125 | GetPrincipal(const nsACString& aHost, nsIPrincipal** aPrincipal) |
michael@0 | 126 | { |
michael@0 | 127 | return GetPrincipal(aHost, nsIScriptSecurityManager::NO_APP_ID, false, aPrincipal); |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | nsresult |
michael@0 | 131 | GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost) |
michael@0 | 132 | { |
michael@0 | 133 | nsCOMPtr<nsIURI> uri; |
michael@0 | 134 | nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); |
michael@0 | 135 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 136 | |
michael@0 | 137 | uri = NS_GetInnermostURI(uri); |
michael@0 | 138 | NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); |
michael@0 | 139 | |
michael@0 | 140 | rv = uri->GetAsciiHost(aHost); |
michael@0 | 141 | if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) { |
michael@0 | 142 | return NS_OK; |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | // For the mailto scheme, we use the path of the URI. We have to chop off the |
michael@0 | 146 | // query part if one exists, so we eliminate everything after a ?. |
michael@0 | 147 | bool isMailTo = false; |
michael@0 | 148 | if (NS_SUCCEEDED(uri->SchemeIs("mailto", &isMailTo)) && isMailTo) { |
michael@0 | 149 | rv = uri->GetPath(aHost); |
michael@0 | 150 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 151 | |
michael@0 | 152 | int32_t spart = aHost.FindChar('?', 0); |
michael@0 | 153 | if (spart >= 0) { |
michael@0 | 154 | aHost.Cut(spart, aHost.Length() - spart); |
michael@0 | 155 | } |
michael@0 | 156 | return NS_OK; |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | // Some entries like "file://" uses the origin. |
michael@0 | 160 | rv = aPrincipal->GetOrigin(getter_Copies(aHost)); |
michael@0 | 161 | if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) { |
michael@0 | 162 | return NS_OK; |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | return NS_ERROR_UNEXPECTED; |
michael@0 | 166 | } |
michael@0 | 167 | |
michael@0 | 168 | nsCString |
michael@0 | 169 | GetNextSubDomainForHost(const nsACString& aHost) |
michael@0 | 170 | { |
michael@0 | 171 | nsCOMPtr<nsIEffectiveTLDService> tldService = |
michael@0 | 172 | do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); |
michael@0 | 173 | if (!tldService) { |
michael@0 | 174 | NS_ERROR("Should have a tld service!"); |
michael@0 | 175 | return EmptyCString(); |
michael@0 | 176 | } |
michael@0 | 177 | |
michael@0 | 178 | nsCString subDomain; |
michael@0 | 179 | nsresult rv = tldService->GetNextSubDomain(aHost, subDomain); |
michael@0 | 180 | // We can fail if there is no more subdomain or if the host can't have a |
michael@0 | 181 | // subdomain. |
michael@0 | 182 | if (NS_FAILED(rv)) { |
michael@0 | 183 | return EmptyCString(); |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | return subDomain; |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | class AppClearDataObserver MOZ_FINAL : public nsIObserver { |
michael@0 | 190 | public: |
michael@0 | 191 | NS_DECL_ISUPPORTS |
michael@0 | 192 | |
michael@0 | 193 | // nsIObserver implementation. |
michael@0 | 194 | NS_IMETHODIMP |
michael@0 | 195 | Observe(nsISupports *aSubject, const char *aTopic, const char16_t *data) |
michael@0 | 196 | { |
michael@0 | 197 | MOZ_ASSERT(!nsCRT::strcmp(aTopic, "webapps-clear-data")); |
michael@0 | 198 | |
michael@0 | 199 | nsCOMPtr<mozIApplicationClearPrivateDataParams> params = |
michael@0 | 200 | do_QueryInterface(aSubject); |
michael@0 | 201 | if (!params) { |
michael@0 | 202 | NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams"); |
michael@0 | 203 | return NS_ERROR_UNEXPECTED; |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | uint32_t appId; |
michael@0 | 207 | nsresult rv = params->GetAppId(&appId); |
michael@0 | 208 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 209 | |
michael@0 | 210 | bool browserOnly; |
michael@0 | 211 | rv = params->GetBrowserOnly(&browserOnly); |
michael@0 | 212 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 213 | |
michael@0 | 214 | nsCOMPtr<nsIPermissionManager> permManager = do_GetService("@mozilla.org/permissionmanager;1"); |
michael@0 | 215 | return permManager->RemovePermissionsForApp(appId, browserOnly); |
michael@0 | 216 | } |
michael@0 | 217 | }; |
michael@0 | 218 | |
michael@0 | 219 | NS_IMPL_ISUPPORTS(AppClearDataObserver, nsIObserver) |
michael@0 | 220 | |
michael@0 | 221 | static bool |
michael@0 | 222 | IsExpandedPrincipal(nsIPrincipal* aPrincipal) |
michael@0 | 223 | { |
michael@0 | 224 | nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal); |
michael@0 | 225 | return !!ep; |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | } // anonymous namespace |
michael@0 | 229 | |
michael@0 | 230 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 231 | |
michael@0 | 232 | nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal) |
michael@0 | 233 | { |
michael@0 | 234 | MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost))); |
michael@0 | 235 | MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId))); |
michael@0 | 236 | MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement))); |
michael@0 | 237 | } |
michael@0 | 238 | |
michael@0 | 239 | /** |
michael@0 | 240 | * Simple callback used by |AsyncClose| to trigger a treatment once |
michael@0 | 241 | * the database is closed. |
michael@0 | 242 | * |
michael@0 | 243 | * Note: Beware that, if you hold onto a |CloseDatabaseListener| from a |
michael@0 | 244 | * |nsPermissionManager|, this will create a cycle. |
michael@0 | 245 | * |
michael@0 | 246 | * Note: Once the callback has been called this DeleteFromMozHostListener cannot |
michael@0 | 247 | * be reused. |
michael@0 | 248 | */ |
michael@0 | 249 | class CloseDatabaseListener MOZ_FINAL : public mozIStorageCompletionCallback |
michael@0 | 250 | { |
michael@0 | 251 | public: |
michael@0 | 252 | NS_DECL_ISUPPORTS |
michael@0 | 253 | NS_DECL_MOZISTORAGECOMPLETIONCALLBACK |
michael@0 | 254 | /** |
michael@0 | 255 | * @param aManager The owning manager. |
michael@0 | 256 | * @param aRebuildOnSuccess If |true|, reinitialize the database once |
michael@0 | 257 | * it has been closed. Otherwise, do nothing such. |
michael@0 | 258 | */ |
michael@0 | 259 | CloseDatabaseListener(nsPermissionManager* aManager, |
michael@0 | 260 | bool aRebuildOnSuccess); |
michael@0 | 261 | |
michael@0 | 262 | protected: |
michael@0 | 263 | nsRefPtr<nsPermissionManager> mManager; |
michael@0 | 264 | bool mRebuildOnSuccess; |
michael@0 | 265 | }; |
michael@0 | 266 | |
michael@0 | 267 | NS_IMPL_ISUPPORTS(CloseDatabaseListener, mozIStorageCompletionCallback) |
michael@0 | 268 | |
michael@0 | 269 | CloseDatabaseListener::CloseDatabaseListener(nsPermissionManager* aManager, |
michael@0 | 270 | bool aRebuildOnSuccess) |
michael@0 | 271 | : mManager(aManager) |
michael@0 | 272 | , mRebuildOnSuccess(aRebuildOnSuccess) |
michael@0 | 273 | { |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | NS_IMETHODIMP |
michael@0 | 277 | CloseDatabaseListener::Complete(nsresult, nsISupports*) |
michael@0 | 278 | { |
michael@0 | 279 | // Help breaking cycles |
michael@0 | 280 | nsRefPtr<nsPermissionManager> manager = mManager.forget(); |
michael@0 | 281 | if (mRebuildOnSuccess && !manager->mIsShuttingDown) { |
michael@0 | 282 | return manager->InitDB(true); |
michael@0 | 283 | } |
michael@0 | 284 | return NS_OK; |
michael@0 | 285 | } |
michael@0 | 286 | |
michael@0 | 287 | |
michael@0 | 288 | /** |
michael@0 | 289 | * Simple callback used by |RemoveAllInternal| to trigger closing |
michael@0 | 290 | * the database and reinitializing it. |
michael@0 | 291 | * |
michael@0 | 292 | * Note: Beware that, if you hold onto a |DeleteFromMozHostListener| from a |
michael@0 | 293 | * |nsPermissionManager|, this will create a cycle. |
michael@0 | 294 | * |
michael@0 | 295 | * Note: Once the callback has been called this DeleteFromMozHostListener cannot |
michael@0 | 296 | * be reused. |
michael@0 | 297 | */ |
michael@0 | 298 | class DeleteFromMozHostListener MOZ_FINAL : public mozIStorageStatementCallback |
michael@0 | 299 | { |
michael@0 | 300 | public: |
michael@0 | 301 | NS_DECL_ISUPPORTS |
michael@0 | 302 | NS_DECL_MOZISTORAGESTATEMENTCALLBACK |
michael@0 | 303 | |
michael@0 | 304 | /** |
michael@0 | 305 | * @param aManager The owning manager. |
michael@0 | 306 | */ |
michael@0 | 307 | DeleteFromMozHostListener(nsPermissionManager* aManager); |
michael@0 | 308 | |
michael@0 | 309 | protected: |
michael@0 | 310 | nsRefPtr<nsPermissionManager> mManager; |
michael@0 | 311 | }; |
michael@0 | 312 | |
michael@0 | 313 | NS_IMPL_ISUPPORTS(DeleteFromMozHostListener, mozIStorageStatementCallback) |
michael@0 | 314 | |
michael@0 | 315 | DeleteFromMozHostListener:: |
michael@0 | 316 | DeleteFromMozHostListener(nsPermissionManager* aManager) |
michael@0 | 317 | : mManager(aManager) |
michael@0 | 318 | { |
michael@0 | 319 | } |
michael@0 | 320 | |
michael@0 | 321 | NS_IMETHODIMP DeleteFromMozHostListener::HandleResult(mozIStorageResultSet *) |
michael@0 | 322 | { |
michael@0 | 323 | MOZ_CRASH("Should not get any results"); |
michael@0 | 324 | } |
michael@0 | 325 | |
michael@0 | 326 | NS_IMETHODIMP DeleteFromMozHostListener::HandleError(mozIStorageError *) |
michael@0 | 327 | { |
michael@0 | 328 | // Errors are handled in |HandleCompletion| |
michael@0 | 329 | return NS_OK; |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | NS_IMETHODIMP DeleteFromMozHostListener::HandleCompletion(uint16_t aReason) |
michael@0 | 333 | { |
michael@0 | 334 | // Help breaking cycles |
michael@0 | 335 | nsRefPtr<nsPermissionManager> manager = mManager.forget(); |
michael@0 | 336 | |
michael@0 | 337 | if (aReason == REASON_ERROR) { |
michael@0 | 338 | manager->CloseDB(true); |
michael@0 | 339 | } |
michael@0 | 340 | |
michael@0 | 341 | return NS_OK; |
michael@0 | 342 | } |
michael@0 | 343 | |
michael@0 | 344 | /* static */ void |
michael@0 | 345 | nsPermissionManager::AppClearDataObserverInit() |
michael@0 | 346 | { |
michael@0 | 347 | nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1"); |
michael@0 | 348 | observerService->AddObserver(new AppClearDataObserver(), "webapps-clear-data", /* holdsWeak= */ false); |
michael@0 | 349 | } |
michael@0 | 350 | |
michael@0 | 351 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 352 | // nsPermissionManager Implementation |
michael@0 | 353 | |
michael@0 | 354 | static const char kPermissionsFileName[] = "permissions.sqlite"; |
michael@0 | 355 | #define HOSTS_SCHEMA_VERSION 3 |
michael@0 | 356 | |
michael@0 | 357 | static const char kHostpermFileName[] = "hostperm.1"; |
michael@0 | 358 | |
michael@0 | 359 | static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION; |
michael@0 | 360 | |
michael@0 | 361 | NS_IMPL_ISUPPORTS(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference) |
michael@0 | 362 | |
michael@0 | 363 | nsPermissionManager::nsPermissionManager() |
michael@0 | 364 | : mLargestID(0) |
michael@0 | 365 | , mIsShuttingDown(false) |
michael@0 | 366 | { |
michael@0 | 367 | } |
michael@0 | 368 | |
michael@0 | 369 | nsPermissionManager::~nsPermissionManager() |
michael@0 | 370 | { |
michael@0 | 371 | RemoveAllFromMemory(); |
michael@0 | 372 | gPermissionManager = nullptr; |
michael@0 | 373 | } |
michael@0 | 374 | |
michael@0 | 375 | // static |
michael@0 | 376 | nsIPermissionManager* |
michael@0 | 377 | nsPermissionManager::GetXPCOMSingleton() |
michael@0 | 378 | { |
michael@0 | 379 | if (gPermissionManager) { |
michael@0 | 380 | NS_ADDREF(gPermissionManager); |
michael@0 | 381 | return gPermissionManager; |
michael@0 | 382 | } |
michael@0 | 383 | |
michael@0 | 384 | // Create a new singleton nsPermissionManager. |
michael@0 | 385 | // We AddRef only once since XPCOM has rules about the ordering of module |
michael@0 | 386 | // teardowns - by the time our module destructor is called, it's too late to |
michael@0 | 387 | // Release our members, since GC cycles have already been completed and |
michael@0 | 388 | // would result in serious leaks. |
michael@0 | 389 | // See bug 209571. |
michael@0 | 390 | gPermissionManager = new nsPermissionManager(); |
michael@0 | 391 | if (gPermissionManager) { |
michael@0 | 392 | NS_ADDREF(gPermissionManager); |
michael@0 | 393 | if (NS_FAILED(gPermissionManager->Init())) { |
michael@0 | 394 | NS_RELEASE(gPermissionManager); |
michael@0 | 395 | } |
michael@0 | 396 | } |
michael@0 | 397 | |
michael@0 | 398 | return gPermissionManager; |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | nsresult |
michael@0 | 402 | nsPermissionManager::Init() |
michael@0 | 403 | { |
michael@0 | 404 | nsresult rv; |
michael@0 | 405 | |
michael@0 | 406 | mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv); |
michael@0 | 407 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 408 | mObserverService->AddObserver(this, "profile-before-change", true); |
michael@0 | 409 | mObserverService->AddObserver(this, "profile-do-change", true); |
michael@0 | 410 | } |
michael@0 | 411 | |
michael@0 | 412 | |
michael@0 | 413 | nsCOMPtr<nsIPrefBranch2> pbi = do_GetService(NS_PREFSERVICE_CONTRACTID); |
michael@0 | 414 | if (pbi) { |
michael@0 | 415 | pbi->AddObserver("permissions.", this, PR_FALSE); |
michael@0 | 416 | } |
michael@0 | 417 | |
michael@0 | 418 | if (IsChildProcess()) { |
michael@0 | 419 | // Get the permissions from the parent process |
michael@0 | 420 | InfallibleTArray<IPC::Permission> perms; |
michael@0 | 421 | ChildProcess()->SendReadPermissions(&perms); |
michael@0 | 422 | |
michael@0 | 423 | for (uint32_t i = 0; i < perms.Length(); i++) { |
michael@0 | 424 | const IPC::Permission &perm = perms[i]; |
michael@0 | 425 | |
michael@0 | 426 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 427 | rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal)); |
michael@0 | 428 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 429 | |
michael@0 | 430 | AddInternal(principal, perm.type, perm.capability, 0, perm.expireType, |
michael@0 | 431 | perm.expireTime, eNotify, eNoDBOperation); |
michael@0 | 432 | } |
michael@0 | 433 | |
michael@0 | 434 | // Stop here; we don't need the DB in the child process |
michael@0 | 435 | return NS_OK; |
michael@0 | 436 | } |
michael@0 | 437 | |
michael@0 | 438 | // ignore failure here, since it's non-fatal (we can run fine without |
michael@0 | 439 | // persistent storage - e.g. if there's no profile). |
michael@0 | 440 | // XXX should we tell the user about this? |
michael@0 | 441 | InitDB(false); |
michael@0 | 442 | |
michael@0 | 443 | return NS_OK; |
michael@0 | 444 | } |
michael@0 | 445 | |
michael@0 | 446 | nsresult |
michael@0 | 447 | nsPermissionManager::InitDB(bool aRemoveFile) |
michael@0 | 448 | { |
michael@0 | 449 | nsCOMPtr<nsIFile> permissionsFile; |
michael@0 | 450 | nsresult rv = NS_GetSpecialDirectory(NS_APP_PERMISSION_PARENT_DIR, getter_AddRefs(permissionsFile)); |
michael@0 | 451 | if (NS_FAILED(rv)) { |
michael@0 | 452 | rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile)); |
michael@0 | 453 | } |
michael@0 | 454 | NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED); |
michael@0 | 455 | |
michael@0 | 456 | rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kPermissionsFileName)); |
michael@0 | 457 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 458 | |
michael@0 | 459 | if (aRemoveFile) { |
michael@0 | 460 | bool exists = false; |
michael@0 | 461 | rv = permissionsFile->Exists(&exists); |
michael@0 | 462 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 463 | if (exists) { |
michael@0 | 464 | rv = permissionsFile->Remove(false); |
michael@0 | 465 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 466 | } |
michael@0 | 467 | } |
michael@0 | 468 | |
michael@0 | 469 | nsCOMPtr<mozIStorageService> storage = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); |
michael@0 | 470 | if (!storage) |
michael@0 | 471 | return NS_ERROR_UNEXPECTED; |
michael@0 | 472 | |
michael@0 | 473 | bool memory_db = false; |
michael@0 | 474 | nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); |
michael@0 | 475 | if (prefs) { |
michael@0 | 476 | prefs->GetBoolPref("permissions.memory_only", &memory_db); |
michael@0 | 477 | } |
michael@0 | 478 | |
michael@0 | 479 | // cache a connection to the hosts database |
michael@0 | 480 | if (memory_db) { |
michael@0 | 481 | rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn)); |
michael@0 | 482 | } else { |
michael@0 | 483 | rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn)); |
michael@0 | 484 | } |
michael@0 | 485 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 486 | |
michael@0 | 487 | bool ready; |
michael@0 | 488 | mDBConn->GetConnectionReady(&ready); |
michael@0 | 489 | if (!ready) { |
michael@0 | 490 | // delete and try again |
michael@0 | 491 | rv = permissionsFile->Remove(false); |
michael@0 | 492 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 493 | |
michael@0 | 494 | if (memory_db) { |
michael@0 | 495 | rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn)); |
michael@0 | 496 | } else { |
michael@0 | 497 | rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn)); |
michael@0 | 498 | } |
michael@0 | 499 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 500 | |
michael@0 | 501 | mDBConn->GetConnectionReady(&ready); |
michael@0 | 502 | if (!ready) |
michael@0 | 503 | return NS_ERROR_UNEXPECTED; |
michael@0 | 504 | } |
michael@0 | 505 | |
michael@0 | 506 | bool tableExists = false; |
michael@0 | 507 | mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists); |
michael@0 | 508 | if (!tableExists) { |
michael@0 | 509 | rv = CreateTable(); |
michael@0 | 510 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 511 | |
michael@0 | 512 | } else { |
michael@0 | 513 | // table already exists; check the schema version before reading |
michael@0 | 514 | int32_t dbSchemaVersion; |
michael@0 | 515 | rv = mDBConn->GetSchemaVersion(&dbSchemaVersion); |
michael@0 | 516 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 517 | |
michael@0 | 518 | switch (dbSchemaVersion) { |
michael@0 | 519 | // upgrading. |
michael@0 | 520 | // every time you increment the database schema, you need to implement |
michael@0 | 521 | // the upgrading code from the previous version to the new one. |
michael@0 | 522 | // fall through to current version |
michael@0 | 523 | |
michael@0 | 524 | case 1: |
michael@0 | 525 | { |
michael@0 | 526 | // previous non-expiry version of database. Upgrade it by adding the |
michael@0 | 527 | // expiration columns |
michael@0 | 528 | rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
michael@0 | 529 | "ALTER TABLE moz_hosts ADD expireType INTEGER")); |
michael@0 | 530 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 531 | |
michael@0 | 532 | rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
michael@0 | 533 | "ALTER TABLE moz_hosts ADD expireTime INTEGER")); |
michael@0 | 534 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 535 | } |
michael@0 | 536 | |
michael@0 | 537 | // fall through to the next upgrade |
michael@0 | 538 | |
michael@0 | 539 | // TODO: we want to make default version as version 2 in order to fix bug 784875. |
michael@0 | 540 | case 0: |
michael@0 | 541 | case 2: |
michael@0 | 542 | { |
michael@0 | 543 | // Add appId/isInBrowserElement fields. |
michael@0 | 544 | rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
michael@0 | 545 | "ALTER TABLE moz_hosts ADD appId INTEGER")); |
michael@0 | 546 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 547 | |
michael@0 | 548 | rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
michael@0 | 549 | "ALTER TABLE moz_hosts ADD isInBrowserElement INTEGER")); |
michael@0 | 550 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 551 | |
michael@0 | 552 | rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); |
michael@0 | 553 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 554 | } |
michael@0 | 555 | |
michael@0 | 556 | // fall through to the next upgrade |
michael@0 | 557 | |
michael@0 | 558 | // current version. |
michael@0 | 559 | case HOSTS_SCHEMA_VERSION: |
michael@0 | 560 | break; |
michael@0 | 561 | |
michael@0 | 562 | // downgrading. |
michael@0 | 563 | // if columns have been added to the table, we can still use the ones we |
michael@0 | 564 | // understand safely. if columns have been deleted or altered, just |
michael@0 | 565 | // blow away the table and start from scratch! if you change the way |
michael@0 | 566 | // a column is interpreted, make sure you also change its name so this |
michael@0 | 567 | // check will catch it. |
michael@0 | 568 | default: |
michael@0 | 569 | { |
michael@0 | 570 | // check if all the expected columns exist |
michael@0 | 571 | nsCOMPtr<mozIStorageStatement> stmt; |
michael@0 | 572 | rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( |
michael@0 | 573 | "SELECT host, type, permission, expireType, expireTime, appId, isInBrowserElement FROM moz_hosts"), |
michael@0 | 574 | getter_AddRefs(stmt)); |
michael@0 | 575 | if (NS_SUCCEEDED(rv)) |
michael@0 | 576 | break; |
michael@0 | 577 | |
michael@0 | 578 | // our columns aren't there - drop the table! |
michael@0 | 579 | rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts")); |
michael@0 | 580 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 581 | |
michael@0 | 582 | rv = CreateTable(); |
michael@0 | 583 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 584 | } |
michael@0 | 585 | break; |
michael@0 | 586 | } |
michael@0 | 587 | } |
michael@0 | 588 | |
michael@0 | 589 | // make operations on the table asynchronous, for performance |
michael@0 | 590 | mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF")); |
michael@0 | 591 | |
michael@0 | 592 | // cache frequently used statements (for insertion, deletion, and updating) |
michael@0 | 593 | rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( |
michael@0 | 594 | "INSERT INTO moz_hosts " |
michael@0 | 595 | "(id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) " |
michael@0 | 596 | "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"), getter_AddRefs(mStmtInsert)); |
michael@0 | 597 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 598 | |
michael@0 | 599 | rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( |
michael@0 | 600 | "DELETE FROM moz_hosts " |
michael@0 | 601 | "WHERE id = ?1"), getter_AddRefs(mStmtDelete)); |
michael@0 | 602 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 603 | |
michael@0 | 604 | rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( |
michael@0 | 605 | "UPDATE moz_hosts " |
michael@0 | 606 | "SET permission = ?2, expireType= ?3, expireTime = ?4 WHERE id = ?1"), |
michael@0 | 607 | getter_AddRefs(mStmtUpdate)); |
michael@0 | 608 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 609 | |
michael@0 | 610 | // check whether to import or just read in the db |
michael@0 | 611 | if (tableExists) |
michael@0 | 612 | return Read(); |
michael@0 | 613 | |
michael@0 | 614 | return Import(); |
michael@0 | 615 | } |
michael@0 | 616 | |
michael@0 | 617 | // sets the schema version and creates the moz_hosts table. |
michael@0 | 618 | nsresult |
michael@0 | 619 | nsPermissionManager::CreateTable() |
michael@0 | 620 | { |
michael@0 | 621 | // set the schema version, before creating the table |
michael@0 | 622 | nsresult rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); |
michael@0 | 623 | if (NS_FAILED(rv)) return rv; |
michael@0 | 624 | |
michael@0 | 625 | // create the table |
michael@0 | 626 | // SQL also lives in automation.py.in. If you change this SQL change that |
michael@0 | 627 | // one too. |
michael@0 | 628 | return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( |
michael@0 | 629 | "CREATE TABLE moz_hosts (" |
michael@0 | 630 | " id INTEGER PRIMARY KEY" |
michael@0 | 631 | ",host TEXT" |
michael@0 | 632 | ",type TEXT" |
michael@0 | 633 | ",permission INTEGER" |
michael@0 | 634 | ",expireType INTEGER" |
michael@0 | 635 | ",expireTime INTEGER" |
michael@0 | 636 | ",appId INTEGER" |
michael@0 | 637 | ",isInBrowserElement INTEGER" |
michael@0 | 638 | ")")); |
michael@0 | 639 | } |
michael@0 | 640 | |
michael@0 | 641 | NS_IMETHODIMP |
michael@0 | 642 | nsPermissionManager::Add(nsIURI *aURI, |
michael@0 | 643 | const char *aType, |
michael@0 | 644 | uint32_t aPermission, |
michael@0 | 645 | uint32_t aExpireType, |
michael@0 | 646 | int64_t aExpireTime) |
michael@0 | 647 | { |
michael@0 | 648 | NS_ENSURE_ARG_POINTER(aURI); |
michael@0 | 649 | |
michael@0 | 650 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 651 | nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal)); |
michael@0 | 652 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 653 | |
michael@0 | 654 | return AddFromPrincipal(principal, aType, aPermission, aExpireType, aExpireTime); |
michael@0 | 655 | } |
michael@0 | 656 | |
michael@0 | 657 | NS_IMETHODIMP |
michael@0 | 658 | nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal, |
michael@0 | 659 | const char* aType, uint32_t aPermission, |
michael@0 | 660 | uint32_t aExpireType, int64_t aExpireTime) |
michael@0 | 661 | { |
michael@0 | 662 | ENSURE_NOT_CHILD_PROCESS; |
michael@0 | 663 | NS_ENSURE_ARG_POINTER(aPrincipal); |
michael@0 | 664 | NS_ENSURE_ARG_POINTER(aType); |
michael@0 | 665 | NS_ENSURE_TRUE(aExpireType == nsIPermissionManager::EXPIRE_NEVER || |
michael@0 | 666 | aExpireType == nsIPermissionManager::EXPIRE_TIME || |
michael@0 | 667 | aExpireType == nsIPermissionManager::EXPIRE_SESSION, |
michael@0 | 668 | NS_ERROR_INVALID_ARG); |
michael@0 | 669 | |
michael@0 | 670 | // Skip addition if the permission is already expired. Note that EXPIRE_SESSION only |
michael@0 | 671 | // honors expireTime if it is nonzero. |
michael@0 | 672 | if ((aExpireType == nsIPermissionManager::EXPIRE_TIME || |
michael@0 | 673 | (aExpireType == nsIPermissionManager::EXPIRE_SESSION && aExpireTime != 0)) && |
michael@0 | 674 | aExpireTime <= (PR_Now() / 1000)) { |
michael@0 | 675 | return NS_OK; |
michael@0 | 676 | } |
michael@0 | 677 | |
michael@0 | 678 | // We don't add the system principal because it actually has no URI and we |
michael@0 | 679 | // always allow action for them. |
michael@0 | 680 | if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
michael@0 | 681 | return NS_OK; |
michael@0 | 682 | } |
michael@0 | 683 | |
michael@0 | 684 | // Permissions may not be added to expanded principals. |
michael@0 | 685 | if (IsExpandedPrincipal(aPrincipal)) { |
michael@0 | 686 | return NS_ERROR_INVALID_ARG; |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | return AddInternal(aPrincipal, nsDependentCString(aType), aPermission, 0, |
michael@0 | 690 | aExpireType, aExpireTime, eNotify, eWriteToDB); |
michael@0 | 691 | } |
michael@0 | 692 | |
michael@0 | 693 | nsresult |
michael@0 | 694 | nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, |
michael@0 | 695 | const nsAFlatCString &aType, |
michael@0 | 696 | uint32_t aPermission, |
michael@0 | 697 | int64_t aID, |
michael@0 | 698 | uint32_t aExpireType, |
michael@0 | 699 | int64_t aExpireTime, |
michael@0 | 700 | NotifyOperationType aNotifyOperation, |
michael@0 | 701 | DBOperationType aDBOperation) |
michael@0 | 702 | { |
michael@0 | 703 | nsAutoCString host; |
michael@0 | 704 | nsresult rv = GetHostForPrincipal(aPrincipal, host); |
michael@0 | 705 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 706 | |
michael@0 | 707 | if (!IsChildProcess()) { |
michael@0 | 708 | uint32_t appId; |
michael@0 | 709 | rv = aPrincipal->GetAppId(&appId); |
michael@0 | 710 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 711 | |
michael@0 | 712 | bool isInBrowserElement; |
michael@0 | 713 | rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); |
michael@0 | 714 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 715 | |
michael@0 | 716 | IPC::Permission permission(host, appId, isInBrowserElement, aType, |
michael@0 | 717 | aPermission, aExpireType, aExpireTime); |
michael@0 | 718 | |
michael@0 | 719 | nsTArray<ContentParent*> cplist; |
michael@0 | 720 | ContentParent::GetAll(cplist); |
michael@0 | 721 | for (uint32_t i = 0; i < cplist.Length(); ++i) { |
michael@0 | 722 | ContentParent* cp = cplist[i]; |
michael@0 | 723 | if (cp->NeedsPermissionsUpdate()) |
michael@0 | 724 | unused << cp->SendAddPermission(permission); |
michael@0 | 725 | } |
michael@0 | 726 | } |
michael@0 | 727 | |
michael@0 | 728 | // look up the type index |
michael@0 | 729 | int32_t typeIndex = GetTypeIndex(aType.get(), true); |
michael@0 | 730 | NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 731 | |
michael@0 | 732 | // When an entry already exists, PutEntry will return that, instead |
michael@0 | 733 | // of adding a new one |
michael@0 | 734 | nsRefPtr<PermissionKey> key = new PermissionKey(aPrincipal); |
michael@0 | 735 | PermissionHashKey* entry = mPermissionTable.PutEntry(key); |
michael@0 | 736 | if (!entry) return NS_ERROR_FAILURE; |
michael@0 | 737 | if (!entry->GetKey()) { |
michael@0 | 738 | mPermissionTable.RawRemoveEntry(entry); |
michael@0 | 739 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 740 | } |
michael@0 | 741 | |
michael@0 | 742 | // figure out the transaction type, and get any existing permission value |
michael@0 | 743 | OperationType op; |
michael@0 | 744 | int32_t index = entry->GetPermissionIndex(typeIndex); |
michael@0 | 745 | if (index == -1) { |
michael@0 | 746 | if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) |
michael@0 | 747 | op = eOperationNone; |
michael@0 | 748 | else |
michael@0 | 749 | op = eOperationAdding; |
michael@0 | 750 | |
michael@0 | 751 | } else { |
michael@0 | 752 | PermissionEntry oldPermissionEntry = entry->GetPermissions()[index]; |
michael@0 | 753 | |
michael@0 | 754 | // remove the permission if the permission is UNKNOWN, update the |
michael@0 | 755 | // permission if its value or expire type have changed OR if the time has |
michael@0 | 756 | // changed and the expire type is time, otherwise, don't modify. There's |
michael@0 | 757 | // no need to modify a permission that doesn't expire with time when the |
michael@0 | 758 | // only thing changed is the expire time. |
michael@0 | 759 | if (aPermission == oldPermissionEntry.mPermission && |
michael@0 | 760 | aExpireType == oldPermissionEntry.mExpireType && |
michael@0 | 761 | (aExpireType == nsIPermissionManager::EXPIRE_NEVER || |
michael@0 | 762 | aExpireTime == oldPermissionEntry.mExpireTime)) |
michael@0 | 763 | op = eOperationNone; |
michael@0 | 764 | else if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) |
michael@0 | 765 | op = eOperationRemoving; |
michael@0 | 766 | else |
michael@0 | 767 | op = eOperationChanging; |
michael@0 | 768 | } |
michael@0 | 769 | |
michael@0 | 770 | // do the work for adding, deleting, or changing a permission: |
michael@0 | 771 | // update the in-memory list, write to the db, and notify consumers. |
michael@0 | 772 | int64_t id; |
michael@0 | 773 | switch (op) { |
michael@0 | 774 | case eOperationNone: |
michael@0 | 775 | { |
michael@0 | 776 | // nothing to do |
michael@0 | 777 | return NS_OK; |
michael@0 | 778 | } |
michael@0 | 779 | |
michael@0 | 780 | case eOperationAdding: |
michael@0 | 781 | { |
michael@0 | 782 | if (aDBOperation == eWriteToDB) { |
michael@0 | 783 | // we'll be writing to the database - generate a known unique id |
michael@0 | 784 | id = ++mLargestID; |
michael@0 | 785 | } else { |
michael@0 | 786 | // we're reading from the database - use the id already assigned |
michael@0 | 787 | id = aID; |
michael@0 | 788 | } |
michael@0 | 789 | |
michael@0 | 790 | entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime)); |
michael@0 | 791 | |
michael@0 | 792 | if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) { |
michael@0 | 793 | uint32_t appId; |
michael@0 | 794 | rv = aPrincipal->GetAppId(&appId); |
michael@0 | 795 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 796 | |
michael@0 | 797 | bool isInBrowserElement; |
michael@0 | 798 | rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); |
michael@0 | 799 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 800 | |
michael@0 | 801 | UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, appId, isInBrowserElement); |
michael@0 | 802 | } |
michael@0 | 803 | |
michael@0 | 804 | if (aNotifyOperation == eNotify) { |
michael@0 | 805 | NotifyObserversWithPermission(host, |
michael@0 | 806 | entry->GetKey()->mAppId, |
michael@0 | 807 | entry->GetKey()->mIsInBrowserElement, |
michael@0 | 808 | mTypeArray[typeIndex], |
michael@0 | 809 | aPermission, |
michael@0 | 810 | aExpireType, |
michael@0 | 811 | aExpireTime, |
michael@0 | 812 | MOZ_UTF16("added")); |
michael@0 | 813 | } |
michael@0 | 814 | |
michael@0 | 815 | break; |
michael@0 | 816 | } |
michael@0 | 817 | |
michael@0 | 818 | case eOperationRemoving: |
michael@0 | 819 | { |
michael@0 | 820 | PermissionEntry oldPermissionEntry = entry->GetPermissions()[index]; |
michael@0 | 821 | id = oldPermissionEntry.mID; |
michael@0 | 822 | entry->GetPermissions().RemoveElementAt(index); |
michael@0 | 823 | |
michael@0 | 824 | if (aDBOperation == eWriteToDB) |
michael@0 | 825 | // We care only about the id here so we pass dummy values for all other |
michael@0 | 826 | // parameters. |
michael@0 | 827 | UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0, |
michael@0 | 828 | nsIPermissionManager::EXPIRE_NEVER, 0, 0, false); |
michael@0 | 829 | |
michael@0 | 830 | if (aNotifyOperation == eNotify) { |
michael@0 | 831 | NotifyObserversWithPermission(host, |
michael@0 | 832 | entry->GetKey()->mAppId, |
michael@0 | 833 | entry->GetKey()->mIsInBrowserElement, |
michael@0 | 834 | mTypeArray[typeIndex], |
michael@0 | 835 | oldPermissionEntry.mPermission, |
michael@0 | 836 | oldPermissionEntry.mExpireType, |
michael@0 | 837 | oldPermissionEntry.mExpireTime, |
michael@0 | 838 | MOZ_UTF16("deleted")); |
michael@0 | 839 | } |
michael@0 | 840 | |
michael@0 | 841 | // If there are no more permissions stored for that entry, clear it. |
michael@0 | 842 | if (entry->GetPermissions().IsEmpty()) { |
michael@0 | 843 | mPermissionTable.RawRemoveEntry(entry); |
michael@0 | 844 | } |
michael@0 | 845 | |
michael@0 | 846 | break; |
michael@0 | 847 | } |
michael@0 | 848 | |
michael@0 | 849 | case eOperationChanging: |
michael@0 | 850 | { |
michael@0 | 851 | id = entry->GetPermissions()[index].mID; |
michael@0 | 852 | |
michael@0 | 853 | // If the new expireType is EXPIRE_SESSION, then we have to keep a |
michael@0 | 854 | // copy of the previous permission/expireType values. This cached value will be |
michael@0 | 855 | // used when restoring the permissions of an app. |
michael@0 | 856 | if (entry->GetPermissions()[index].mExpireType != nsIPermissionManager::EXPIRE_SESSION && |
michael@0 | 857 | aExpireType == nsIPermissionManager::EXPIRE_SESSION) { |
michael@0 | 858 | entry->GetPermissions()[index].mNonSessionPermission = entry->GetPermissions()[index].mPermission; |
michael@0 | 859 | entry->GetPermissions()[index].mNonSessionExpireType = entry->GetPermissions()[index].mExpireType; |
michael@0 | 860 | entry->GetPermissions()[index].mNonSessionExpireTime = entry->GetPermissions()[index].mExpireTime; |
michael@0 | 861 | } else if (aExpireType != nsIPermissionManager::EXPIRE_SESSION) { |
michael@0 | 862 | entry->GetPermissions()[index].mNonSessionPermission = aPermission; |
michael@0 | 863 | entry->GetPermissions()[index].mNonSessionExpireType = aExpireType; |
michael@0 | 864 | entry->GetPermissions()[index].mNonSessionExpireTime = aExpireTime; |
michael@0 | 865 | } |
michael@0 | 866 | |
michael@0 | 867 | entry->GetPermissions()[index].mPermission = aPermission; |
michael@0 | 868 | entry->GetPermissions()[index].mExpireType = aExpireType; |
michael@0 | 869 | entry->GetPermissions()[index].mExpireTime = aExpireTime; |
michael@0 | 870 | |
michael@0 | 871 | if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) |
michael@0 | 872 | // We care only about the id, the permission and expireType/expireTime here. |
michael@0 | 873 | // We pass dummy values for all other parameters. |
michael@0 | 874 | UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), |
michael@0 | 875 | aPermission, aExpireType, aExpireTime, 0, false); |
michael@0 | 876 | |
michael@0 | 877 | if (aNotifyOperation == eNotify) { |
michael@0 | 878 | NotifyObserversWithPermission(host, |
michael@0 | 879 | entry->GetKey()->mAppId, |
michael@0 | 880 | entry->GetKey()->mIsInBrowserElement, |
michael@0 | 881 | mTypeArray[typeIndex], |
michael@0 | 882 | aPermission, |
michael@0 | 883 | aExpireType, |
michael@0 | 884 | aExpireTime, |
michael@0 | 885 | MOZ_UTF16("changed")); |
michael@0 | 886 | } |
michael@0 | 887 | |
michael@0 | 888 | break; |
michael@0 | 889 | } |
michael@0 | 890 | } |
michael@0 | 891 | |
michael@0 | 892 | return NS_OK; |
michael@0 | 893 | } |
michael@0 | 894 | |
michael@0 | 895 | NS_IMETHODIMP |
michael@0 | 896 | nsPermissionManager::Remove(const nsACString &aHost, |
michael@0 | 897 | const char *aType) |
michael@0 | 898 | { |
michael@0 | 899 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 900 | nsresult rv = GetPrincipal(aHost, getter_AddRefs(principal)); |
michael@0 | 901 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 902 | |
michael@0 | 903 | return RemoveFromPrincipal(principal, aType); |
michael@0 | 904 | } |
michael@0 | 905 | |
michael@0 | 906 | NS_IMETHODIMP |
michael@0 | 907 | nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal, |
michael@0 | 908 | const char* aType) |
michael@0 | 909 | { |
michael@0 | 910 | ENSURE_NOT_CHILD_PROCESS; |
michael@0 | 911 | NS_ENSURE_ARG_POINTER(aPrincipal); |
michael@0 | 912 | NS_ENSURE_ARG_POINTER(aType); |
michael@0 | 913 | |
michael@0 | 914 | // System principals are never added to the database, no need to remove them. |
michael@0 | 915 | if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
michael@0 | 916 | return NS_OK; |
michael@0 | 917 | } |
michael@0 | 918 | |
michael@0 | 919 | // Permissions may not be added to expanded principals. |
michael@0 | 920 | if (IsExpandedPrincipal(aPrincipal)) { |
michael@0 | 921 | return NS_ERROR_INVALID_ARG; |
michael@0 | 922 | } |
michael@0 | 923 | |
michael@0 | 924 | // AddInternal() handles removal, just let it do the work |
michael@0 | 925 | return AddInternal(aPrincipal, |
michael@0 | 926 | nsDependentCString(aType), |
michael@0 | 927 | nsIPermissionManager::UNKNOWN_ACTION, |
michael@0 | 928 | 0, |
michael@0 | 929 | nsIPermissionManager::EXPIRE_NEVER, |
michael@0 | 930 | 0, |
michael@0 | 931 | eNotify, |
michael@0 | 932 | eWriteToDB); |
michael@0 | 933 | } |
michael@0 | 934 | |
michael@0 | 935 | NS_IMETHODIMP |
michael@0 | 936 | nsPermissionManager::RemoveAll() |
michael@0 | 937 | { |
michael@0 | 938 | ENSURE_NOT_CHILD_PROCESS; |
michael@0 | 939 | return RemoveAllInternal(true); |
michael@0 | 940 | } |
michael@0 | 941 | |
michael@0 | 942 | void |
michael@0 | 943 | nsPermissionManager::CloseDB(bool aRebuildOnSuccess) |
michael@0 | 944 | { |
michael@0 | 945 | // Null the statements, this will finalize them. |
michael@0 | 946 | mStmtInsert = nullptr; |
michael@0 | 947 | mStmtDelete = nullptr; |
michael@0 | 948 | mStmtUpdate = nullptr; |
michael@0 | 949 | if (mDBConn) { |
michael@0 | 950 | mozIStorageCompletionCallback* cb = new CloseDatabaseListener(this, |
michael@0 | 951 | aRebuildOnSuccess); |
michael@0 | 952 | mozilla::DebugOnly<nsresult> rv = mDBConn->AsyncClose(cb); |
michael@0 | 953 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
michael@0 | 954 | mDBConn = nullptr; // Avoid race conditions |
michael@0 | 955 | } |
michael@0 | 956 | } |
michael@0 | 957 | |
michael@0 | 958 | nsresult |
michael@0 | 959 | nsPermissionManager::RemoveAllInternal(bool aNotifyObservers) |
michael@0 | 960 | { |
michael@0 | 961 | // Remove from memory and notify immediately. Since the in-memory |
michael@0 | 962 | // database is authoritative, we do not need confirmation from the |
michael@0 | 963 | // on-disk database to notify observers. |
michael@0 | 964 | RemoveAllFromMemory(); |
michael@0 | 965 | if (aNotifyObservers) { |
michael@0 | 966 | NotifyObservers(nullptr, MOZ_UTF16("cleared")); |
michael@0 | 967 | } |
michael@0 | 968 | |
michael@0 | 969 | // clear the db |
michael@0 | 970 | if (mDBConn) { |
michael@0 | 971 | nsCOMPtr<mozIStorageAsyncStatement> removeStmt; |
michael@0 | 972 | nsresult rv = mDBConn-> |
michael@0 | 973 | CreateAsyncStatement(NS_LITERAL_CSTRING( |
michael@0 | 974 | "DELETE FROM moz_hosts" |
michael@0 | 975 | ), getter_AddRefs(removeStmt)); |
michael@0 | 976 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
michael@0 | 977 | if (!removeStmt) { |
michael@0 | 978 | return NS_ERROR_UNEXPECTED; |
michael@0 | 979 | } |
michael@0 | 980 | nsCOMPtr<mozIStoragePendingStatement> pending; |
michael@0 | 981 | mozIStorageStatementCallback* cb = new DeleteFromMozHostListener(this); |
michael@0 | 982 | rv = removeStmt->ExecuteAsync(cb, getter_AddRefs(pending)); |
michael@0 | 983 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
michael@0 | 984 | |
michael@0 | 985 | return rv; |
michael@0 | 986 | } |
michael@0 | 987 | |
michael@0 | 988 | return NS_OK; |
michael@0 | 989 | } |
michael@0 | 990 | |
michael@0 | 991 | NS_IMETHODIMP |
michael@0 | 992 | nsPermissionManager::TestExactPermission(nsIURI *aURI, |
michael@0 | 993 | const char *aType, |
michael@0 | 994 | uint32_t *aPermission) |
michael@0 | 995 | { |
michael@0 | 996 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 997 | nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal)); |
michael@0 | 998 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 999 | |
michael@0 | 1000 | return TestExactPermissionFromPrincipal(principal, aType, aPermission); |
michael@0 | 1001 | } |
michael@0 | 1002 | |
michael@0 | 1003 | NS_IMETHODIMP |
michael@0 | 1004 | nsPermissionManager::TestExactPermissionFromPrincipal(nsIPrincipal* aPrincipal, |
michael@0 | 1005 | const char* aType, |
michael@0 | 1006 | uint32_t* aPermission) |
michael@0 | 1007 | { |
michael@0 | 1008 | return CommonTestPermission(aPrincipal, aType, aPermission, true, true); |
michael@0 | 1009 | } |
michael@0 | 1010 | |
michael@0 | 1011 | NS_IMETHODIMP |
michael@0 | 1012 | nsPermissionManager::TestExactPermanentPermission(nsIPrincipal* aPrincipal, |
michael@0 | 1013 | const char* aType, |
michael@0 | 1014 | uint32_t* aPermission) |
michael@0 | 1015 | { |
michael@0 | 1016 | return CommonTestPermission(aPrincipal, aType, aPermission, true, false); |
michael@0 | 1017 | } |
michael@0 | 1018 | |
michael@0 | 1019 | NS_IMETHODIMP |
michael@0 | 1020 | nsPermissionManager::TestPermission(nsIURI *aURI, |
michael@0 | 1021 | const char *aType, |
michael@0 | 1022 | uint32_t *aPermission) |
michael@0 | 1023 | { |
michael@0 | 1024 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 1025 | nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal)); |
michael@0 | 1026 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1027 | |
michael@0 | 1028 | return TestPermissionFromPrincipal(principal, aType, aPermission); |
michael@0 | 1029 | } |
michael@0 | 1030 | |
michael@0 | 1031 | NS_IMETHODIMP |
michael@0 | 1032 | nsPermissionManager::TestPermissionFromWindow(nsIDOMWindow* aWindow, |
michael@0 | 1033 | const char* aType, |
michael@0 | 1034 | uint32_t* aPermission) |
michael@0 | 1035 | { |
michael@0 | 1036 | nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow); |
michael@0 | 1037 | NS_ENSURE_TRUE(window, NS_NOINTERFACE); |
michael@0 | 1038 | |
michael@0 | 1039 | nsPIDOMWindow* innerWindow = window->IsInnerWindow() ? |
michael@0 | 1040 | window.get() : |
michael@0 | 1041 | window->GetCurrentInnerWindow(); |
michael@0 | 1042 | |
michael@0 | 1043 | // Get the document for security check |
michael@0 | 1044 | nsCOMPtr<nsIDocument> document = innerWindow->GetExtantDoc(); |
michael@0 | 1045 | NS_ENSURE_TRUE(document, NS_NOINTERFACE); |
michael@0 | 1046 | |
michael@0 | 1047 | nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal(); |
michael@0 | 1048 | return TestPermissionFromPrincipal(principal, aType, aPermission); |
michael@0 | 1049 | } |
michael@0 | 1050 | |
michael@0 | 1051 | NS_IMETHODIMP |
michael@0 | 1052 | nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal, |
michael@0 | 1053 | const char* aType, |
michael@0 | 1054 | uint32_t* aPermission) |
michael@0 | 1055 | { |
michael@0 | 1056 | return CommonTestPermission(aPrincipal, aType, aPermission, false, true); |
michael@0 | 1057 | } |
michael@0 | 1058 | |
michael@0 | 1059 | NS_IMETHODIMP |
michael@0 | 1060 | nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal, |
michael@0 | 1061 | const char* aType, |
michael@0 | 1062 | bool aExactHostMatch, |
michael@0 | 1063 | nsIPermission** aResult) |
michael@0 | 1064 | { |
michael@0 | 1065 | NS_ENSURE_ARG_POINTER(aPrincipal); |
michael@0 | 1066 | NS_ENSURE_ARG_POINTER(aType); |
michael@0 | 1067 | |
michael@0 | 1068 | *aResult = nullptr; |
michael@0 | 1069 | |
michael@0 | 1070 | if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
michael@0 | 1071 | return NS_OK; |
michael@0 | 1072 | } |
michael@0 | 1073 | |
michael@0 | 1074 | // Querying the permission object of an nsEP is non-sensical. |
michael@0 | 1075 | if (IsExpandedPrincipal(aPrincipal)) { |
michael@0 | 1076 | return NS_ERROR_INVALID_ARG; |
michael@0 | 1077 | } |
michael@0 | 1078 | |
michael@0 | 1079 | nsAutoCString host; |
michael@0 | 1080 | nsresult rv = GetHostForPrincipal(aPrincipal, host); |
michael@0 | 1081 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1082 | |
michael@0 | 1083 | int32_t typeIndex = GetTypeIndex(aType, false); |
michael@0 | 1084 | // If type == -1, the type isn't known, |
michael@0 | 1085 | // so just return NS_OK |
michael@0 | 1086 | if (typeIndex == -1) return NS_OK; |
michael@0 | 1087 | |
michael@0 | 1088 | uint32_t appId; |
michael@0 | 1089 | rv = aPrincipal->GetAppId(&appId); |
michael@0 | 1090 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1091 | |
michael@0 | 1092 | bool isInBrowserElement; |
michael@0 | 1093 | rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); |
michael@0 | 1094 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1095 | |
michael@0 | 1096 | PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, |
michael@0 | 1097 | typeIndex, aExactHostMatch); |
michael@0 | 1098 | if (!entry) { |
michael@0 | 1099 | return NS_OK; |
michael@0 | 1100 | } |
michael@0 | 1101 | |
michael@0 | 1102 | // We don't call GetPermission(typeIndex) because that returns a fake |
michael@0 | 1103 | // UNKNOWN_ACTION entry if there is no match. |
michael@0 | 1104 | int32_t idx = entry->GetPermissionIndex(typeIndex); |
michael@0 | 1105 | if (-1 == idx) { |
michael@0 | 1106 | return NS_OK; |
michael@0 | 1107 | } |
michael@0 | 1108 | |
michael@0 | 1109 | PermissionEntry& perm = entry->GetPermissions()[idx]; |
michael@0 | 1110 | nsCOMPtr<nsIPermission> r = new nsPermission(entry->GetKey()->mHost, |
michael@0 | 1111 | entry->GetKey()->mAppId, |
michael@0 | 1112 | entry->GetKey()->mIsInBrowserElement, |
michael@0 | 1113 | mTypeArray.ElementAt(perm.mType), |
michael@0 | 1114 | perm.mPermission, |
michael@0 | 1115 | perm.mExpireType, |
michael@0 | 1116 | perm.mExpireTime); |
michael@0 | 1117 | r.forget(aResult); |
michael@0 | 1118 | return NS_OK; |
michael@0 | 1119 | } |
michael@0 | 1120 | |
michael@0 | 1121 | nsresult |
michael@0 | 1122 | nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal, |
michael@0 | 1123 | const char *aType, |
michael@0 | 1124 | uint32_t *aPermission, |
michael@0 | 1125 | bool aExactHostMatch, |
michael@0 | 1126 | bool aIncludingSession) |
michael@0 | 1127 | { |
michael@0 | 1128 | NS_ENSURE_ARG_POINTER(aPrincipal); |
michael@0 | 1129 | NS_ENSURE_ARG_POINTER(aType); |
michael@0 | 1130 | |
michael@0 | 1131 | if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
michael@0 | 1132 | *aPermission = nsIPermissionManager::ALLOW_ACTION; |
michael@0 | 1133 | return NS_OK; |
michael@0 | 1134 | } |
michael@0 | 1135 | |
michael@0 | 1136 | // Set the default. |
michael@0 | 1137 | *aPermission = nsIPermissionManager::UNKNOWN_ACTION; |
michael@0 | 1138 | |
michael@0 | 1139 | // For expanded principals, we want to iterate over the whitelist and see |
michael@0 | 1140 | // if the permission is granted for any of them. |
michael@0 | 1141 | nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal); |
michael@0 | 1142 | if (ep) { |
michael@0 | 1143 | nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist; |
michael@0 | 1144 | nsresult rv = ep->GetWhiteList(&whitelist); |
michael@0 | 1145 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1146 | |
michael@0 | 1147 | for (size_t i = 0; i < whitelist->Length(); ++i) { |
michael@0 | 1148 | uint32_t perm; |
michael@0 | 1149 | rv = CommonTestPermission(whitelist->ElementAt(i), aType, &perm, aExactHostMatch, |
michael@0 | 1150 | aIncludingSession); |
michael@0 | 1151 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1152 | if (perm == nsIPermissionManager::ALLOW_ACTION) { |
michael@0 | 1153 | *aPermission = perm; |
michael@0 | 1154 | return NS_OK; |
michael@0 | 1155 | } else if (perm == nsIPermissionManager::PROMPT_ACTION) { |
michael@0 | 1156 | // Store it, but keep going to see if we can do better. |
michael@0 | 1157 | *aPermission = perm; |
michael@0 | 1158 | } |
michael@0 | 1159 | } |
michael@0 | 1160 | |
michael@0 | 1161 | return NS_OK; |
michael@0 | 1162 | } |
michael@0 | 1163 | |
michael@0 | 1164 | nsAutoCString host; |
michael@0 | 1165 | nsresult rv = GetHostForPrincipal(aPrincipal, host); |
michael@0 | 1166 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1167 | |
michael@0 | 1168 | int32_t typeIndex = GetTypeIndex(aType, false); |
michael@0 | 1169 | // If type == -1, the type isn't known, |
michael@0 | 1170 | // so just return NS_OK |
michael@0 | 1171 | if (typeIndex == -1) return NS_OK; |
michael@0 | 1172 | |
michael@0 | 1173 | uint32_t appId; |
michael@0 | 1174 | rv = aPrincipal->GetAppId(&appId); |
michael@0 | 1175 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1176 | |
michael@0 | 1177 | bool isInBrowserElement; |
michael@0 | 1178 | rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); |
michael@0 | 1179 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1180 | |
michael@0 | 1181 | PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, |
michael@0 | 1182 | typeIndex, aExactHostMatch); |
michael@0 | 1183 | if (!entry || |
michael@0 | 1184 | (!aIncludingSession && |
michael@0 | 1185 | entry->GetPermission(typeIndex).mNonSessionExpireType == |
michael@0 | 1186 | nsIPermissionManager::EXPIRE_SESSION)) { |
michael@0 | 1187 | return NS_OK; |
michael@0 | 1188 | } |
michael@0 | 1189 | |
michael@0 | 1190 | *aPermission = aIncludingSession |
michael@0 | 1191 | ? entry->GetPermission(typeIndex).mPermission |
michael@0 | 1192 | : entry->GetPermission(typeIndex).mNonSessionPermission; |
michael@0 | 1193 | |
michael@0 | 1194 | return NS_OK; |
michael@0 | 1195 | } |
michael@0 | 1196 | |
michael@0 | 1197 | // Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple. |
michael@0 | 1198 | // This is not simply using PermissionKey because we will walk-up domains in |
michael@0 | 1199 | // case of |host| contains sub-domains. |
michael@0 | 1200 | // Returns null if nothing found. |
michael@0 | 1201 | // Also accepts host on the format "<foo>". This will perform an exact match |
michael@0 | 1202 | // lookup as the string doesn't contain any dots. |
michael@0 | 1203 | nsPermissionManager::PermissionHashKey* |
michael@0 | 1204 | nsPermissionManager::GetPermissionHashKey(const nsACString& aHost, |
michael@0 | 1205 | uint32_t aAppId, |
michael@0 | 1206 | bool aIsInBrowserElement, |
michael@0 | 1207 | uint32_t aType, |
michael@0 | 1208 | bool aExactHostMatch) |
michael@0 | 1209 | { |
michael@0 | 1210 | PermissionHashKey* entry = nullptr; |
michael@0 | 1211 | |
michael@0 | 1212 | nsRefPtr<PermissionKey> key = new PermissionKey(aHost, aAppId, aIsInBrowserElement); |
michael@0 | 1213 | entry = mPermissionTable.GetEntry(key); |
michael@0 | 1214 | |
michael@0 | 1215 | if (entry) { |
michael@0 | 1216 | PermissionEntry permEntry = entry->GetPermission(aType); |
michael@0 | 1217 | |
michael@0 | 1218 | // if the entry is expired, remove and keep looking for others. |
michael@0 | 1219 | // Note that EXPIRE_SESSION only honors expireTime if it is nonzero. |
michael@0 | 1220 | if ((permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME || |
michael@0 | 1221 | (permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION && |
michael@0 | 1222 | permEntry.mExpireTime != 0)) && |
michael@0 | 1223 | permEntry.mExpireTime <= (PR_Now() / 1000)) { |
michael@0 | 1224 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 1225 | if (NS_FAILED(GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) { |
michael@0 | 1226 | return nullptr; |
michael@0 | 1227 | } |
michael@0 | 1228 | |
michael@0 | 1229 | entry = nullptr; |
michael@0 | 1230 | RemoveFromPrincipal(principal, mTypeArray[aType].get()); |
michael@0 | 1231 | } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { |
michael@0 | 1232 | entry = nullptr; |
michael@0 | 1233 | } |
michael@0 | 1234 | } |
michael@0 | 1235 | |
michael@0 | 1236 | if (entry) { |
michael@0 | 1237 | return entry; |
michael@0 | 1238 | } |
michael@0 | 1239 | |
michael@0 | 1240 | // If we haven't found an entry, depending on the host, we could try a bit |
michael@0 | 1241 | // harder. |
michael@0 | 1242 | // If this is a file:// URI, we can check for the presence of the magic entry |
michael@0 | 1243 | // <file> which gives permission to all file://. This hack might disappear, |
michael@0 | 1244 | // see bug 817007. Note that we don't require aExactHostMatch to be true for |
michael@0 | 1245 | // that to keep retro-compatibility. |
michael@0 | 1246 | // If this is not a file:// URI, and that aExactHostMatch wasn't true, we can |
michael@0 | 1247 | // check if the base domain has a permission entry. |
michael@0 | 1248 | |
michael@0 | 1249 | if (StringBeginsWith(aHost, NS_LITERAL_CSTRING("file://"))) { |
michael@0 | 1250 | return GetPermissionHashKey(NS_LITERAL_CSTRING("<file>"), aAppId, aIsInBrowserElement, aType, true); |
michael@0 | 1251 | } |
michael@0 | 1252 | |
michael@0 | 1253 | if (!aExactHostMatch) { |
michael@0 | 1254 | nsCString domain = GetNextSubDomainForHost(aHost); |
michael@0 | 1255 | if (!domain.IsEmpty()) { |
michael@0 | 1256 | return GetPermissionHashKey(domain, aAppId, aIsInBrowserElement, aType, aExactHostMatch); |
michael@0 | 1257 | } |
michael@0 | 1258 | } |
michael@0 | 1259 | |
michael@0 | 1260 | // No entry, really... |
michael@0 | 1261 | return nullptr; |
michael@0 | 1262 | } |
michael@0 | 1263 | |
michael@0 | 1264 | // helper struct for passing arguments into hash enumeration callback. |
michael@0 | 1265 | struct nsGetEnumeratorData |
michael@0 | 1266 | { |
michael@0 | 1267 | nsGetEnumeratorData(nsCOMArray<nsIPermission> *aArray, const nsTArray<nsCString> *aTypes) |
michael@0 | 1268 | : array(aArray) |
michael@0 | 1269 | , types(aTypes) {} |
michael@0 | 1270 | |
michael@0 | 1271 | nsCOMArray<nsIPermission> *array; |
michael@0 | 1272 | const nsTArray<nsCString> *types; |
michael@0 | 1273 | }; |
michael@0 | 1274 | |
michael@0 | 1275 | static PLDHashOperator |
michael@0 | 1276 | AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg) |
michael@0 | 1277 | { |
michael@0 | 1278 | nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg); |
michael@0 | 1279 | |
michael@0 | 1280 | for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { |
michael@0 | 1281 | nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; |
michael@0 | 1282 | |
michael@0 | 1283 | nsPermission *perm = new nsPermission(entry->GetKey()->mHost, |
michael@0 | 1284 | entry->GetKey()->mAppId, |
michael@0 | 1285 | entry->GetKey()->mIsInBrowserElement, |
michael@0 | 1286 | data->types->ElementAt(permEntry.mType), |
michael@0 | 1287 | permEntry.mPermission, |
michael@0 | 1288 | permEntry.mExpireType, |
michael@0 | 1289 | permEntry.mExpireTime); |
michael@0 | 1290 | |
michael@0 | 1291 | data->array->AppendObject(perm); |
michael@0 | 1292 | } |
michael@0 | 1293 | |
michael@0 | 1294 | return PL_DHASH_NEXT; |
michael@0 | 1295 | } |
michael@0 | 1296 | |
michael@0 | 1297 | NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum) |
michael@0 | 1298 | { |
michael@0 | 1299 | // roll an nsCOMArray of all our permissions, then hand out an enumerator |
michael@0 | 1300 | nsCOMArray<nsIPermission> array; |
michael@0 | 1301 | nsGetEnumeratorData data(&array, &mTypeArray); |
michael@0 | 1302 | |
michael@0 | 1303 | mPermissionTable.EnumerateEntries(AddPermissionsToList, &data); |
michael@0 | 1304 | |
michael@0 | 1305 | return NS_NewArrayEnumerator(aEnum, array); |
michael@0 | 1306 | } |
michael@0 | 1307 | |
michael@0 | 1308 | NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData) |
michael@0 | 1309 | { |
michael@0 | 1310 | ENSURE_NOT_CHILD_PROCESS; |
michael@0 | 1311 | |
michael@0 | 1312 | if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { |
michael@0 | 1313 | if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("permissions.memory_only").get())) { |
michael@0 | 1314 | // XXX: Should we remove the file? Probably not.. |
michael@0 | 1315 | InitDB(PR_FALSE); |
michael@0 | 1316 | } |
michael@0 | 1317 | } else if (!nsCRT::strcmp(aTopic, "profile-before-change")) { |
michael@0 | 1318 | // The profile is about to change, |
michael@0 | 1319 | // or is going away because the application is shutting down. |
michael@0 | 1320 | mIsShuttingDown = true; |
michael@0 | 1321 | if (!nsCRT::strcmp(someData, MOZ_UTF16("shutdown-cleanse"))) { |
michael@0 | 1322 | // Clear the permissions file and close the db asynchronously |
michael@0 | 1323 | RemoveAllInternal(false); |
michael@0 | 1324 | } else { |
michael@0 | 1325 | RemoveAllFromMemory(); |
michael@0 | 1326 | CloseDB(false); |
michael@0 | 1327 | } |
michael@0 | 1328 | } |
michael@0 | 1329 | else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { |
michael@0 | 1330 | // the profile has already changed; init the db from the new location |
michael@0 | 1331 | InitDB(false); |
michael@0 | 1332 | } |
michael@0 | 1333 | |
michael@0 | 1334 | return NS_OK; |
michael@0 | 1335 | } |
michael@0 | 1336 | |
michael@0 | 1337 | PLDHashOperator |
michael@0 | 1338 | nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey* entry, void* arg) |
michael@0 | 1339 | { |
michael@0 | 1340 | GetPermissionsForAppStruct* data = static_cast<GetPermissionsForAppStruct*>(arg); |
michael@0 | 1341 | |
michael@0 | 1342 | for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { |
michael@0 | 1343 | nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; |
michael@0 | 1344 | |
michael@0 | 1345 | if (entry->GetKey()->mAppId != data->appId || |
michael@0 | 1346 | (data->browserOnly && !entry->GetKey()->mIsInBrowserElement)) { |
michael@0 | 1347 | continue; |
michael@0 | 1348 | } |
michael@0 | 1349 | |
michael@0 | 1350 | data->permissions.AppendObject(new nsPermission(entry->GetKey()->mHost, |
michael@0 | 1351 | entry->GetKey()->mAppId, |
michael@0 | 1352 | entry->GetKey()->mIsInBrowserElement, |
michael@0 | 1353 | gPermissionManager->mTypeArray.ElementAt(permEntry.mType), |
michael@0 | 1354 | permEntry.mPermission, |
michael@0 | 1355 | permEntry.mExpireType, |
michael@0 | 1356 | permEntry.mExpireTime)); |
michael@0 | 1357 | } |
michael@0 | 1358 | |
michael@0 | 1359 | return PL_DHASH_NEXT; |
michael@0 | 1360 | } |
michael@0 | 1361 | |
michael@0 | 1362 | NS_IMETHODIMP |
michael@0 | 1363 | nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly) |
michael@0 | 1364 | { |
michael@0 | 1365 | ENSURE_NOT_CHILD_PROCESS; |
michael@0 | 1366 | NS_ENSURE_ARG(aAppId != nsIScriptSecurityManager::NO_APP_ID); |
michael@0 | 1367 | |
michael@0 | 1368 | // We begin by removing all the permissions from the DB. |
michael@0 | 1369 | // After clearing the DB, we call AddInternal() to make sure that all |
michael@0 | 1370 | // processes are aware of this change and the representation of the DB in |
michael@0 | 1371 | // memory is updated. |
michael@0 | 1372 | // We have to get all permissions associated with an application and then |
michael@0 | 1373 | // remove those because doing so in EnumerateEntries() would fail because |
michael@0 | 1374 | // we might happen to actually delete entries from the list. |
michael@0 | 1375 | |
michael@0 | 1376 | nsAutoCString sql; |
michael@0 | 1377 | sql.AppendLiteral("DELETE FROM moz_hosts WHERE appId="); |
michael@0 | 1378 | sql.AppendInt(aAppId); |
michael@0 | 1379 | |
michael@0 | 1380 | if (aBrowserOnly) { |
michael@0 | 1381 | sql.AppendLiteral(" AND isInBrowserElement=1"); |
michael@0 | 1382 | } |
michael@0 | 1383 | |
michael@0 | 1384 | nsCOMPtr<mozIStorageAsyncStatement> removeStmt; |
michael@0 | 1385 | nsresult rv = mDBConn->CreateAsyncStatement(sql, getter_AddRefs(removeStmt)); |
michael@0 | 1386 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1387 | |
michael@0 | 1388 | nsCOMPtr<mozIStoragePendingStatement> pending; |
michael@0 | 1389 | rv = removeStmt->ExecuteAsync(nullptr, getter_AddRefs(pending)); |
michael@0 | 1390 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1391 | |
michael@0 | 1392 | GetPermissionsForAppStruct data(aAppId, aBrowserOnly); |
michael@0 | 1393 | mPermissionTable.EnumerateEntries(GetPermissionsForApp, &data); |
michael@0 | 1394 | |
michael@0 | 1395 | for (int32_t i=0; i<data.permissions.Count(); ++i) { |
michael@0 | 1396 | nsAutoCString host; |
michael@0 | 1397 | bool isInBrowserElement; |
michael@0 | 1398 | nsAutoCString type; |
michael@0 | 1399 | |
michael@0 | 1400 | data.permissions[i]->GetHost(host); |
michael@0 | 1401 | data.permissions[i]->GetIsInBrowserElement(&isInBrowserElement); |
michael@0 | 1402 | data.permissions[i]->GetType(type); |
michael@0 | 1403 | |
michael@0 | 1404 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 1405 | if (NS_FAILED(GetPrincipal(host, aAppId, isInBrowserElement, |
michael@0 | 1406 | getter_AddRefs(principal)))) { |
michael@0 | 1407 | NS_ERROR("GetPrincipal() failed!"); |
michael@0 | 1408 | continue; |
michael@0 | 1409 | } |
michael@0 | 1410 | |
michael@0 | 1411 | AddInternal(principal, |
michael@0 | 1412 | type, |
michael@0 | 1413 | nsIPermissionManager::UNKNOWN_ACTION, |
michael@0 | 1414 | 0, |
michael@0 | 1415 | nsIPermissionManager::EXPIRE_NEVER, |
michael@0 | 1416 | 0, |
michael@0 | 1417 | nsPermissionManager::eNotify, |
michael@0 | 1418 | nsPermissionManager::eNoDBOperation); |
michael@0 | 1419 | } |
michael@0 | 1420 | |
michael@0 | 1421 | return NS_OK; |
michael@0 | 1422 | } |
michael@0 | 1423 | |
michael@0 | 1424 | PLDHashOperator |
michael@0 | 1425 | nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator( |
michael@0 | 1426 | nsPermissionManager::PermissionHashKey* entry, void* arg) |
michael@0 | 1427 | { |
michael@0 | 1428 | uint32_t* appId = static_cast<uint32_t*>(arg); |
michael@0 | 1429 | |
michael@0 | 1430 | for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { |
michael@0 | 1431 | if (entry->GetKey()->mAppId != *appId) { |
michael@0 | 1432 | continue; |
michael@0 | 1433 | } |
michael@0 | 1434 | |
michael@0 | 1435 | nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; |
michael@0 | 1436 | if (permEntry.mExpireType != nsIPermissionManager::EXPIRE_SESSION) { |
michael@0 | 1437 | continue; |
michael@0 | 1438 | } |
michael@0 | 1439 | |
michael@0 | 1440 | if (permEntry.mNonSessionExpireType == nsIPermissionManager::EXPIRE_SESSION) { |
michael@0 | 1441 | PermissionEntry oldPermissionEntry = entry->GetPermissions()[i]; |
michael@0 | 1442 | |
michael@0 | 1443 | entry->GetPermissions().RemoveElementAt(i); |
michael@0 | 1444 | |
michael@0 | 1445 | gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost, |
michael@0 | 1446 | entry->GetKey()->mAppId, |
michael@0 | 1447 | entry->GetKey()->mIsInBrowserElement, |
michael@0 | 1448 | gPermissionManager->mTypeArray.ElementAt(oldPermissionEntry.mType), |
michael@0 | 1449 | oldPermissionEntry.mPermission, |
michael@0 | 1450 | oldPermissionEntry.mExpireType, |
michael@0 | 1451 | oldPermissionEntry.mExpireTime, |
michael@0 | 1452 | MOZ_UTF16("deleted")); |
michael@0 | 1453 | --i; |
michael@0 | 1454 | continue; |
michael@0 | 1455 | } |
michael@0 | 1456 | |
michael@0 | 1457 | permEntry.mPermission = permEntry.mNonSessionPermission; |
michael@0 | 1458 | permEntry.mExpireType = permEntry.mNonSessionExpireType; |
michael@0 | 1459 | permEntry.mExpireTime = permEntry.mNonSessionExpireTime; |
michael@0 | 1460 | |
michael@0 | 1461 | gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost, |
michael@0 | 1462 | entry->GetKey()->mAppId, |
michael@0 | 1463 | entry->GetKey()->mIsInBrowserElement, |
michael@0 | 1464 | gPermissionManager->mTypeArray.ElementAt(permEntry.mType), |
michael@0 | 1465 | permEntry.mPermission, |
michael@0 | 1466 | permEntry.mExpireType, |
michael@0 | 1467 | permEntry.mExpireTime, |
michael@0 | 1468 | MOZ_UTF16("changed")); |
michael@0 | 1469 | } |
michael@0 | 1470 | |
michael@0 | 1471 | return PL_DHASH_NEXT; |
michael@0 | 1472 | } |
michael@0 | 1473 | |
michael@0 | 1474 | nsresult |
michael@0 | 1475 | nsPermissionManager::RemoveExpiredPermissionsForApp(uint32_t aAppId) |
michael@0 | 1476 | { |
michael@0 | 1477 | ENSURE_NOT_CHILD_PROCESS; |
michael@0 | 1478 | |
michael@0 | 1479 | if (aAppId != nsIScriptSecurityManager::NO_APP_ID) { |
michael@0 | 1480 | mPermissionTable.EnumerateEntries(RemoveExpiredPermissionsForAppEnumerator, &aAppId); |
michael@0 | 1481 | } |
michael@0 | 1482 | |
michael@0 | 1483 | return NS_OK; |
michael@0 | 1484 | } |
michael@0 | 1485 | |
michael@0 | 1486 | //***************************************************************************** |
michael@0 | 1487 | //*** nsPermissionManager private methods |
michael@0 | 1488 | //***************************************************************************** |
michael@0 | 1489 | |
michael@0 | 1490 | nsresult |
michael@0 | 1491 | nsPermissionManager::RemoveAllFromMemory() |
michael@0 | 1492 | { |
michael@0 | 1493 | mLargestID = 0; |
michael@0 | 1494 | mTypeArray.Clear(); |
michael@0 | 1495 | mPermissionTable.Clear(); |
michael@0 | 1496 | |
michael@0 | 1497 | return NS_OK; |
michael@0 | 1498 | } |
michael@0 | 1499 | |
michael@0 | 1500 | // Returns -1 on failure |
michael@0 | 1501 | int32_t |
michael@0 | 1502 | nsPermissionManager::GetTypeIndex(const char *aType, |
michael@0 | 1503 | bool aAdd) |
michael@0 | 1504 | { |
michael@0 | 1505 | for (uint32_t i = 0; i < mTypeArray.Length(); ++i) |
michael@0 | 1506 | if (mTypeArray[i].Equals(aType)) |
michael@0 | 1507 | return i; |
michael@0 | 1508 | |
michael@0 | 1509 | if (!aAdd) { |
michael@0 | 1510 | // Not found, but that is ok - we were just looking. |
michael@0 | 1511 | return -1; |
michael@0 | 1512 | } |
michael@0 | 1513 | |
michael@0 | 1514 | // This type was not registered before. |
michael@0 | 1515 | // append it to the array, without copy-constructing the string |
michael@0 | 1516 | nsCString *elem = mTypeArray.AppendElement(); |
michael@0 | 1517 | if (!elem) |
michael@0 | 1518 | return -1; |
michael@0 | 1519 | |
michael@0 | 1520 | elem->Assign(aType); |
michael@0 | 1521 | return mTypeArray.Length() - 1; |
michael@0 | 1522 | } |
michael@0 | 1523 | |
michael@0 | 1524 | // wrapper function for mangling (host,type,perm,expireType,expireTime) |
michael@0 | 1525 | // set into an nsIPermission. |
michael@0 | 1526 | void |
michael@0 | 1527 | nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost, |
michael@0 | 1528 | uint32_t aAppId, |
michael@0 | 1529 | bool aIsInBrowserElement, |
michael@0 | 1530 | const nsCString &aType, |
michael@0 | 1531 | uint32_t aPermission, |
michael@0 | 1532 | uint32_t aExpireType, |
michael@0 | 1533 | int64_t aExpireTime, |
michael@0 | 1534 | const char16_t *aData) |
michael@0 | 1535 | { |
michael@0 | 1536 | nsCOMPtr<nsIPermission> permission = |
michael@0 | 1537 | new nsPermission(aHost, aAppId, aIsInBrowserElement, aType, aPermission, |
michael@0 | 1538 | aExpireType, aExpireTime); |
michael@0 | 1539 | if (permission) |
michael@0 | 1540 | NotifyObservers(permission, aData); |
michael@0 | 1541 | } |
michael@0 | 1542 | |
michael@0 | 1543 | // notify observers that the permission list changed. there are four possible |
michael@0 | 1544 | // values for aData: |
michael@0 | 1545 | // "deleted" means a permission was deleted. aPermission is the deleted permission. |
michael@0 | 1546 | // "added" means a permission was added. aPermission is the added permission. |
michael@0 | 1547 | // "changed" means a permission was altered. aPermission is the new permission. |
michael@0 | 1548 | // "cleared" means the entire permission list was cleared. aPermission is null. |
michael@0 | 1549 | void |
michael@0 | 1550 | nsPermissionManager::NotifyObservers(nsIPermission *aPermission, |
michael@0 | 1551 | const char16_t *aData) |
michael@0 | 1552 | { |
michael@0 | 1553 | if (mObserverService) |
michael@0 | 1554 | mObserverService->NotifyObservers(aPermission, |
michael@0 | 1555 | kPermissionChangeNotification, |
michael@0 | 1556 | aData); |
michael@0 | 1557 | } |
michael@0 | 1558 | |
michael@0 | 1559 | nsresult |
michael@0 | 1560 | nsPermissionManager::Read() |
michael@0 | 1561 | { |
michael@0 | 1562 | ENSURE_NOT_CHILD_PROCESS; |
michael@0 | 1563 | |
michael@0 | 1564 | nsresult rv; |
michael@0 | 1565 | |
michael@0 | 1566 | // delete expired permissions before we read in the db |
michael@0 | 1567 | { |
michael@0 | 1568 | // this deletion has its own scope so the write lock is released when done. |
michael@0 | 1569 | nsCOMPtr<mozIStorageStatement> stmtDeleteExpired; |
michael@0 | 1570 | rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( |
michael@0 | 1571 | "DELETE FROM moz_hosts WHERE expireType = ?1 AND expireTime <= ?2"), |
michael@0 | 1572 | getter_AddRefs(stmtDeleteExpired)); |
michael@0 | 1573 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1574 | |
michael@0 | 1575 | rv = stmtDeleteExpired->BindInt32ByIndex(0, nsIPermissionManager::EXPIRE_TIME); |
michael@0 | 1576 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1577 | |
michael@0 | 1578 | rv = stmtDeleteExpired->BindInt64ByIndex(1, PR_Now() / 1000); |
michael@0 | 1579 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1580 | |
michael@0 | 1581 | bool hasResult; |
michael@0 | 1582 | rv = stmtDeleteExpired->ExecuteStep(&hasResult); |
michael@0 | 1583 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1584 | } |
michael@0 | 1585 | |
michael@0 | 1586 | nsCOMPtr<mozIStorageStatement> stmt; |
michael@0 | 1587 | rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( |
michael@0 | 1588 | "SELECT id, host, type, permission, expireType, expireTime, appId, isInBrowserElement " |
michael@0 | 1589 | "FROM moz_hosts"), getter_AddRefs(stmt)); |
michael@0 | 1590 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1591 | |
michael@0 | 1592 | int64_t id; |
michael@0 | 1593 | nsAutoCString host, type; |
michael@0 | 1594 | uint32_t permission; |
michael@0 | 1595 | uint32_t expireType; |
michael@0 | 1596 | int64_t expireTime; |
michael@0 | 1597 | uint32_t appId; |
michael@0 | 1598 | bool isInBrowserElement; |
michael@0 | 1599 | bool hasResult; |
michael@0 | 1600 | bool readError = false; |
michael@0 | 1601 | |
michael@0 | 1602 | while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { |
michael@0 | 1603 | // explicitly set our entry id counter for use in AddInternal(), |
michael@0 | 1604 | // and keep track of the largest id so we know where to pick up. |
michael@0 | 1605 | id = stmt->AsInt64(0); |
michael@0 | 1606 | if (id > mLargestID) |
michael@0 | 1607 | mLargestID = id; |
michael@0 | 1608 | |
michael@0 | 1609 | rv = stmt->GetUTF8String(1, host); |
michael@0 | 1610 | if (NS_FAILED(rv)) { |
michael@0 | 1611 | readError = true; |
michael@0 | 1612 | continue; |
michael@0 | 1613 | } |
michael@0 | 1614 | |
michael@0 | 1615 | rv = stmt->GetUTF8String(2, type); |
michael@0 | 1616 | if (NS_FAILED(rv)) { |
michael@0 | 1617 | readError = true; |
michael@0 | 1618 | continue; |
michael@0 | 1619 | } |
michael@0 | 1620 | |
michael@0 | 1621 | permission = stmt->AsInt32(3); |
michael@0 | 1622 | expireType = stmt->AsInt32(4); |
michael@0 | 1623 | |
michael@0 | 1624 | // convert into int64_t value (milliseconds) |
michael@0 | 1625 | expireTime = stmt->AsInt64(5); |
michael@0 | 1626 | |
michael@0 | 1627 | if (stmt->AsInt64(6) < 0) { |
michael@0 | 1628 | readError = true; |
michael@0 | 1629 | continue; |
michael@0 | 1630 | } |
michael@0 | 1631 | appId = static_cast<uint32_t>(stmt->AsInt64(6)); |
michael@0 | 1632 | isInBrowserElement = static_cast<bool>(stmt->AsInt32(7)); |
michael@0 | 1633 | |
michael@0 | 1634 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 1635 | nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal)); |
michael@0 | 1636 | if (NS_FAILED(rv)) { |
michael@0 | 1637 | readError = true; |
michael@0 | 1638 | continue; |
michael@0 | 1639 | } |
michael@0 | 1640 | |
michael@0 | 1641 | rv = AddInternal(principal, type, permission, id, expireType, expireTime, |
michael@0 | 1642 | eDontNotify, eNoDBOperation); |
michael@0 | 1643 | if (NS_FAILED(rv)) { |
michael@0 | 1644 | readError = true; |
michael@0 | 1645 | continue; |
michael@0 | 1646 | } |
michael@0 | 1647 | } |
michael@0 | 1648 | |
michael@0 | 1649 | if (readError) { |
michael@0 | 1650 | NS_ERROR("Error occured while reading the permissions database!"); |
michael@0 | 1651 | return NS_ERROR_FAILURE; |
michael@0 | 1652 | } |
michael@0 | 1653 | |
michael@0 | 1654 | return NS_OK; |
michael@0 | 1655 | } |
michael@0 | 1656 | |
michael@0 | 1657 | static const char kMatchTypeHost[] = "host"; |
michael@0 | 1658 | |
michael@0 | 1659 | nsresult |
michael@0 | 1660 | nsPermissionManager::Import() |
michael@0 | 1661 | { |
michael@0 | 1662 | ENSURE_NOT_CHILD_PROCESS; |
michael@0 | 1663 | |
michael@0 | 1664 | nsresult rv; |
michael@0 | 1665 | |
michael@0 | 1666 | nsCOMPtr<nsIFile> permissionsFile; |
michael@0 | 1667 | rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(permissionsFile)); |
michael@0 | 1668 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1669 | |
michael@0 | 1670 | rv = permissionsFile->AppendNative(NS_LITERAL_CSTRING(kHostpermFileName)); |
michael@0 | 1671 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1672 | |
michael@0 | 1673 | nsCOMPtr<nsIInputStream> fileInputStream; |
michael@0 | 1674 | rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), |
michael@0 | 1675 | permissionsFile); |
michael@0 | 1676 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1677 | |
michael@0 | 1678 | nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv); |
michael@0 | 1679 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1680 | |
michael@0 | 1681 | // start a transaction on the storage db, to optimize insertions. |
michael@0 | 1682 | // transaction will automically commit on completion |
michael@0 | 1683 | mozStorageTransaction transaction(mDBConn, true); |
michael@0 | 1684 | |
michael@0 | 1685 | /* format is: |
michael@0 | 1686 | * matchtype \t type \t permission \t host |
michael@0 | 1687 | * Only "host" is supported for matchtype |
michael@0 | 1688 | * type is a string that identifies the type of permission (e.g. "cookie") |
michael@0 | 1689 | * permission is an integer between 1 and 15 |
michael@0 | 1690 | */ |
michael@0 | 1691 | |
michael@0 | 1692 | nsAutoCString buffer; |
michael@0 | 1693 | bool isMore = true; |
michael@0 | 1694 | while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) { |
michael@0 | 1695 | if (buffer.IsEmpty() || buffer.First() == '#') { |
michael@0 | 1696 | continue; |
michael@0 | 1697 | } |
michael@0 | 1698 | |
michael@0 | 1699 | nsTArray<nsCString> lineArray; |
michael@0 | 1700 | |
michael@0 | 1701 | // Split the line at tabs |
michael@0 | 1702 | ParseString(buffer, '\t', lineArray); |
michael@0 | 1703 | |
michael@0 | 1704 | if (lineArray[0].EqualsLiteral(kMatchTypeHost) && |
michael@0 | 1705 | lineArray.Length() == 4) { |
michael@0 | 1706 | |
michael@0 | 1707 | nsresult error; |
michael@0 | 1708 | uint32_t permission = lineArray[2].ToInteger(&error); |
michael@0 | 1709 | if (NS_FAILED(error)) |
michael@0 | 1710 | continue; |
michael@0 | 1711 | |
michael@0 | 1712 | // hosts might be encoded in UTF8; switch them to ACE to be consistent |
michael@0 | 1713 | if (!IsASCII(lineArray[3])) { |
michael@0 | 1714 | rv = NormalizeToACE(lineArray[3]); |
michael@0 | 1715 | if (NS_FAILED(rv)) |
michael@0 | 1716 | continue; |
michael@0 | 1717 | } |
michael@0 | 1718 | |
michael@0 | 1719 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 1720 | nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal)); |
michael@0 | 1721 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1722 | |
michael@0 | 1723 | rv = AddInternal(principal, lineArray[1], permission, 0, |
michael@0 | 1724 | nsIPermissionManager::EXPIRE_NEVER, 0, eDontNotify, eWriteToDB); |
michael@0 | 1725 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1726 | } |
michael@0 | 1727 | } |
michael@0 | 1728 | |
michael@0 | 1729 | // we're done importing - delete the old file |
michael@0 | 1730 | permissionsFile->Remove(false); |
michael@0 | 1731 | |
michael@0 | 1732 | return NS_OK; |
michael@0 | 1733 | } |
michael@0 | 1734 | |
michael@0 | 1735 | nsresult |
michael@0 | 1736 | nsPermissionManager::NormalizeToACE(nsCString &aHost) |
michael@0 | 1737 | { |
michael@0 | 1738 | // lazily init the IDN service |
michael@0 | 1739 | if (!mIDNService) { |
michael@0 | 1740 | nsresult rv; |
michael@0 | 1741 | mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv); |
michael@0 | 1742 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1743 | } |
michael@0 | 1744 | |
michael@0 | 1745 | return mIDNService->ConvertUTF8toACE(aHost, aHost); |
michael@0 | 1746 | } |
michael@0 | 1747 | |
michael@0 | 1748 | void |
michael@0 | 1749 | nsPermissionManager::UpdateDB(OperationType aOp, |
michael@0 | 1750 | mozIStorageAsyncStatement* aStmt, |
michael@0 | 1751 | int64_t aID, |
michael@0 | 1752 | const nsACString &aHost, |
michael@0 | 1753 | const nsACString &aType, |
michael@0 | 1754 | uint32_t aPermission, |
michael@0 | 1755 | uint32_t aExpireType, |
michael@0 | 1756 | int64_t aExpireTime, |
michael@0 | 1757 | uint32_t aAppId, |
michael@0 | 1758 | bool aIsInBrowserElement) |
michael@0 | 1759 | { |
michael@0 | 1760 | ENSURE_NOT_CHILD_PROCESS_NORET; |
michael@0 | 1761 | |
michael@0 | 1762 | nsresult rv; |
michael@0 | 1763 | |
michael@0 | 1764 | // no statement is ok - just means we don't have a profile |
michael@0 | 1765 | if (!aStmt) |
michael@0 | 1766 | return; |
michael@0 | 1767 | |
michael@0 | 1768 | switch (aOp) { |
michael@0 | 1769 | case eOperationAdding: |
michael@0 | 1770 | { |
michael@0 | 1771 | rv = aStmt->BindInt64ByIndex(0, aID); |
michael@0 | 1772 | if (NS_FAILED(rv)) break; |
michael@0 | 1773 | |
michael@0 | 1774 | rv = aStmt->BindUTF8StringByIndex(1, aHost); |
michael@0 | 1775 | if (NS_FAILED(rv)) break; |
michael@0 | 1776 | |
michael@0 | 1777 | rv = aStmt->BindUTF8StringByIndex(2, aType); |
michael@0 | 1778 | if (NS_FAILED(rv)) break; |
michael@0 | 1779 | |
michael@0 | 1780 | rv = aStmt->BindInt32ByIndex(3, aPermission); |
michael@0 | 1781 | if (NS_FAILED(rv)) break; |
michael@0 | 1782 | |
michael@0 | 1783 | rv = aStmt->BindInt32ByIndex(4, aExpireType); |
michael@0 | 1784 | if (NS_FAILED(rv)) break; |
michael@0 | 1785 | |
michael@0 | 1786 | rv = aStmt->BindInt64ByIndex(5, aExpireTime); |
michael@0 | 1787 | if (NS_FAILED(rv)) break; |
michael@0 | 1788 | |
michael@0 | 1789 | rv = aStmt->BindInt64ByIndex(6, aAppId); |
michael@0 | 1790 | if (NS_FAILED(rv)) break; |
michael@0 | 1791 | |
michael@0 | 1792 | rv = aStmt->BindInt64ByIndex(7, aIsInBrowserElement); |
michael@0 | 1793 | break; |
michael@0 | 1794 | } |
michael@0 | 1795 | |
michael@0 | 1796 | case eOperationRemoving: |
michael@0 | 1797 | { |
michael@0 | 1798 | rv = aStmt->BindInt64ByIndex(0, aID); |
michael@0 | 1799 | break; |
michael@0 | 1800 | } |
michael@0 | 1801 | |
michael@0 | 1802 | case eOperationChanging: |
michael@0 | 1803 | { |
michael@0 | 1804 | rv = aStmt->BindInt64ByIndex(0, aID); |
michael@0 | 1805 | if (NS_FAILED(rv)) break; |
michael@0 | 1806 | |
michael@0 | 1807 | rv = aStmt->BindInt32ByIndex(1, aPermission); |
michael@0 | 1808 | if (NS_FAILED(rv)) break; |
michael@0 | 1809 | |
michael@0 | 1810 | rv = aStmt->BindInt32ByIndex(2, aExpireType); |
michael@0 | 1811 | if (NS_FAILED(rv)) break; |
michael@0 | 1812 | |
michael@0 | 1813 | rv = aStmt->BindInt64ByIndex(3, aExpireTime); |
michael@0 | 1814 | break; |
michael@0 | 1815 | } |
michael@0 | 1816 | |
michael@0 | 1817 | default: |
michael@0 | 1818 | { |
michael@0 | 1819 | NS_NOTREACHED("need a valid operation in UpdateDB()!"); |
michael@0 | 1820 | rv = NS_ERROR_UNEXPECTED; |
michael@0 | 1821 | break; |
michael@0 | 1822 | } |
michael@0 | 1823 | } |
michael@0 | 1824 | |
michael@0 | 1825 | if (NS_FAILED(rv)) { |
michael@0 | 1826 | NS_WARNING("db change failed!"); |
michael@0 | 1827 | return; |
michael@0 | 1828 | } |
michael@0 | 1829 | |
michael@0 | 1830 | nsCOMPtr<mozIStoragePendingStatement> pending; |
michael@0 | 1831 | rv = aStmt->ExecuteAsync(nullptr, getter_AddRefs(pending)); |
michael@0 | 1832 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
michael@0 | 1833 | } |
michael@0 | 1834 | |
michael@0 | 1835 | NS_IMETHODIMP |
michael@0 | 1836 | nsPermissionManager::AddrefAppId(uint32_t aAppId) |
michael@0 | 1837 | { |
michael@0 | 1838 | if (aAppId == nsIScriptSecurityManager::NO_APP_ID) { |
michael@0 | 1839 | return NS_OK; |
michael@0 | 1840 | } |
michael@0 | 1841 | |
michael@0 | 1842 | bool found = false; |
michael@0 | 1843 | for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) { |
michael@0 | 1844 | if (mAppIdRefcounts[i].mAppId == aAppId) { |
michael@0 | 1845 | ++mAppIdRefcounts[i].mCounter; |
michael@0 | 1846 | found = true; |
michael@0 | 1847 | break; |
michael@0 | 1848 | } |
michael@0 | 1849 | } |
michael@0 | 1850 | |
michael@0 | 1851 | if (!found) { |
michael@0 | 1852 | ApplicationCounter app = { aAppId, 1 }; |
michael@0 | 1853 | mAppIdRefcounts.AppendElement(app); |
michael@0 | 1854 | } |
michael@0 | 1855 | |
michael@0 | 1856 | return NS_OK; |
michael@0 | 1857 | } |
michael@0 | 1858 | |
michael@0 | 1859 | NS_IMETHODIMP |
michael@0 | 1860 | nsPermissionManager::ReleaseAppId(uint32_t aAppId) |
michael@0 | 1861 | { |
michael@0 | 1862 | // An app has been released, maybe we have to reset its session. |
michael@0 | 1863 | |
michael@0 | 1864 | if (aAppId == nsIScriptSecurityManager::NO_APP_ID) { |
michael@0 | 1865 | return NS_OK; |
michael@0 | 1866 | } |
michael@0 | 1867 | |
michael@0 | 1868 | for (uint32_t i = 0; i < mAppIdRefcounts.Length(); ++i) { |
michael@0 | 1869 | if (mAppIdRefcounts[i].mAppId == aAppId) { |
michael@0 | 1870 | --mAppIdRefcounts[i].mCounter; |
michael@0 | 1871 | |
michael@0 | 1872 | if (!mAppIdRefcounts[i].mCounter) { |
michael@0 | 1873 | mAppIdRefcounts.RemoveElementAt(i); |
michael@0 | 1874 | return RemoveExpiredPermissionsForApp(aAppId); |
michael@0 | 1875 | } |
michael@0 | 1876 | |
michael@0 | 1877 | break; |
michael@0 | 1878 | } |
michael@0 | 1879 | } |
michael@0 | 1880 | |
michael@0 | 1881 | return NS_OK; |
michael@0 | 1882 | } |
michael@0 | 1883 | |
michael@0 | 1884 | NS_IMETHODIMP |
michael@0 | 1885 | nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal, |
michael@0 | 1886 | const char* aType, |
michael@0 | 1887 | bool aExactHostMatch, |
michael@0 | 1888 | uint64_t aSessionExpireTime, |
michael@0 | 1889 | uint64_t aPersistentExpireTime) |
michael@0 | 1890 | { |
michael@0 | 1891 | NS_ENSURE_ARG_POINTER(aPrincipal); |
michael@0 | 1892 | NS_ENSURE_ARG_POINTER(aType); |
michael@0 | 1893 | |
michael@0 | 1894 | uint64_t nowms = PR_Now() / 1000; |
michael@0 | 1895 | if (aSessionExpireTime < nowms || aPersistentExpireTime < nowms) { |
michael@0 | 1896 | return NS_ERROR_INVALID_ARG; |
michael@0 | 1897 | } |
michael@0 | 1898 | |
michael@0 | 1899 | if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
michael@0 | 1900 | return NS_OK; |
michael@0 | 1901 | } |
michael@0 | 1902 | |
michael@0 | 1903 | // Setting the expire time of an nsEP is non-sensical. |
michael@0 | 1904 | if (IsExpandedPrincipal(aPrincipal)) { |
michael@0 | 1905 | return NS_ERROR_INVALID_ARG; |
michael@0 | 1906 | } |
michael@0 | 1907 | |
michael@0 | 1908 | nsAutoCString host; |
michael@0 | 1909 | nsresult rv = GetHostForPrincipal(aPrincipal, host); |
michael@0 | 1910 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1911 | |
michael@0 | 1912 | int32_t typeIndex = GetTypeIndex(aType, false); |
michael@0 | 1913 | // If type == -1, the type isn't known, |
michael@0 | 1914 | // so just return NS_OK |
michael@0 | 1915 | if (typeIndex == -1) return NS_OK; |
michael@0 | 1916 | |
michael@0 | 1917 | uint32_t appId; |
michael@0 | 1918 | rv = aPrincipal->GetAppId(&appId); |
michael@0 | 1919 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1920 | |
michael@0 | 1921 | bool isInBrowserElement; |
michael@0 | 1922 | rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); |
michael@0 | 1923 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1924 | |
michael@0 | 1925 | PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, |
michael@0 | 1926 | typeIndex, aExactHostMatch); |
michael@0 | 1927 | if (!entry) { |
michael@0 | 1928 | return NS_OK; |
michael@0 | 1929 | } |
michael@0 | 1930 | |
michael@0 | 1931 | int32_t idx = entry->GetPermissionIndex(typeIndex); |
michael@0 | 1932 | if (-1 == idx) { |
michael@0 | 1933 | return NS_OK; |
michael@0 | 1934 | } |
michael@0 | 1935 | |
michael@0 | 1936 | PermissionEntry& perm = entry->GetPermissions()[idx]; |
michael@0 | 1937 | if (perm.mExpireType == EXPIRE_TIME) { |
michael@0 | 1938 | perm.mExpireTime = aPersistentExpireTime; |
michael@0 | 1939 | } else if (perm.mExpireType == EXPIRE_SESSION && perm.mExpireTime != 0) { |
michael@0 | 1940 | perm.mExpireTime = aSessionExpireTime; |
michael@0 | 1941 | } |
michael@0 | 1942 | return NS_OK; |
michael@0 | 1943 | } |