OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/password_manager/password_store_x.h" |
| 6 |
| 7 #include <map> |
| 8 #include <vector> |
| 9 |
| 10 #include "base/logging.h" |
| 11 #include "base/stl_util-inl.h" |
| 12 #include "chrome/browser/chrome_thread.h" |
| 13 #include "chrome/browser/password_manager/password_store_change.h" |
| 14 #include "chrome/common/notification_service.h" |
| 15 |
| 16 using std::vector; |
| 17 using webkit_glue::PasswordForm; |
| 18 |
| 19 PasswordStoreX::PasswordStoreX(LoginDatabase* login_db, |
| 20 Profile* profile, |
| 21 WebDataService* web_data_service, |
| 22 NativeBackend* backend) |
| 23 : PasswordStoreDefault(login_db, profile, web_data_service), |
| 24 backend_(backend), migration_checked_(!backend), allow_fallback_(false) { |
| 25 } |
| 26 |
| 27 PasswordStoreX::~PasswordStoreX() { |
| 28 } |
| 29 |
| 30 void PasswordStoreX::AddLoginImpl(const PasswordForm& form) { |
| 31 CheckMigration(); |
| 32 if (use_native_backend() && backend_->AddLogin(form)) { |
| 33 PasswordStoreChangeList changes; |
| 34 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); |
| 35 NotificationService::current()->Notify( |
| 36 NotificationType::LOGINS_CHANGED, |
| 37 NotificationService::AllSources(), |
| 38 Details<PasswordStoreChangeList>(&changes)); |
| 39 allow_fallback_ = false; |
| 40 } else if (allow_default_store()) { |
| 41 PasswordStoreDefault::AddLoginImpl(form); |
| 42 } |
| 43 } |
| 44 |
| 45 void PasswordStoreX::UpdateLoginImpl(const PasswordForm& form) { |
| 46 CheckMigration(); |
| 47 if (use_native_backend() && backend_->UpdateLogin(form)) { |
| 48 PasswordStoreChangeList changes; |
| 49 changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form)); |
| 50 NotificationService::current()->Notify( |
| 51 NotificationType::LOGINS_CHANGED, |
| 52 NotificationService::AllSources(), |
| 53 Details<PasswordStoreChangeList>(&changes)); |
| 54 allow_fallback_ = false; |
| 55 } else if (allow_default_store()) { |
| 56 PasswordStoreDefault::UpdateLoginImpl(form); |
| 57 } |
| 58 } |
| 59 |
| 60 void PasswordStoreX::RemoveLoginImpl(const PasswordForm& form) { |
| 61 CheckMigration(); |
| 62 if (use_native_backend() && backend_->RemoveLogin(form)) { |
| 63 PasswordStoreChangeList changes; |
| 64 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); |
| 65 NotificationService::current()->Notify( |
| 66 NotificationType::LOGINS_CHANGED, |
| 67 NotificationService::AllSources(), |
| 68 Details<PasswordStoreChangeList>(&changes)); |
| 69 allow_fallback_ = false; |
| 70 } else if (allow_default_store()) { |
| 71 PasswordStoreDefault::RemoveLoginImpl(form); |
| 72 } |
| 73 } |
| 74 |
| 75 void PasswordStoreX::RemoveLoginsCreatedBetweenImpl( |
| 76 const base::Time& delete_begin, |
| 77 const base::Time& delete_end) { |
| 78 CheckMigration(); |
| 79 vector<PasswordForm*> forms; |
| 80 if (use_native_backend() && |
| 81 backend_->GetLoginsCreatedBetween(delete_begin, delete_end, &forms) && |
| 82 backend_->RemoveLoginsCreatedBetween(delete_begin, delete_end)) { |
| 83 PasswordStoreChangeList changes; |
| 84 for (vector<PasswordForm*>::const_iterator it = forms.begin(); |
| 85 it != forms.end(); ++it) { |
| 86 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, |
| 87 **it)); |
| 88 } |
| 89 NotificationService::current()->Notify( |
| 90 NotificationType::LOGINS_CHANGED, |
| 91 NotificationService::AllSources(), |
| 92 Details<PasswordStoreChangeList>(&changes)); |
| 93 allow_fallback_ = false; |
| 94 } else if (allow_default_store()) { |
| 95 PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(delete_begin, |
| 96 delete_end); |
| 97 } |
| 98 STLDeleteElements(&forms); |
| 99 } |
| 100 |
| 101 void PasswordStoreX::GetLoginsImpl(GetLoginsRequest* request, |
| 102 const PasswordForm& form) { |
| 103 CheckMigration(); |
| 104 vector<PasswordForm*> forms; |
| 105 if (use_native_backend() && backend_->GetLogins(form, &forms)) { |
| 106 NotifyConsumer(request, forms); |
| 107 allow_fallback_ = false; |
| 108 } else if (allow_default_store()) { |
| 109 PasswordStoreDefault::GetLoginsImpl(request, form); |
| 110 } else { |
| 111 // The consumer will be left hanging unless we reply. |
| 112 NotifyConsumer(request, forms); |
| 113 } |
| 114 } |
| 115 |
| 116 void PasswordStoreX::GetAutofillableLoginsImpl(GetLoginsRequest* request) { |
| 117 CheckMigration(); |
| 118 vector<PasswordForm*> forms; |
| 119 if (use_native_backend() && backend_->GetAutofillableLogins(&forms)) { |
| 120 NotifyConsumer(request, forms); |
| 121 allow_fallback_ = false; |
| 122 } else if (allow_default_store()) { |
| 123 PasswordStoreDefault::GetAutofillableLoginsImpl(request); |
| 124 } else { |
| 125 // The consumer will be left hanging unless we reply. |
| 126 NotifyConsumer(request, forms); |
| 127 } |
| 128 } |
| 129 |
| 130 void PasswordStoreX::GetBlacklistLoginsImpl(GetLoginsRequest* request) { |
| 131 CheckMigration(); |
| 132 vector<PasswordForm*> forms; |
| 133 if (use_native_backend() && backend_->GetBlacklistLogins(&forms)) { |
| 134 NotifyConsumer(request, forms); |
| 135 allow_fallback_ = false; |
| 136 } else if (allow_default_store()) { |
| 137 PasswordStoreDefault::GetBlacklistLoginsImpl(request); |
| 138 } else { |
| 139 // The consumer will be left hanging unless we reply. |
| 140 NotifyConsumer(request, forms); |
| 141 } |
| 142 } |
| 143 |
| 144 bool PasswordStoreX::FillAutofillableLogins(vector<PasswordForm*>* forms) { |
| 145 CheckMigration(); |
| 146 if (use_native_backend() && backend_->GetAutofillableLogins(forms)) { |
| 147 allow_fallback_ = false; |
| 148 return true; |
| 149 } |
| 150 if (allow_default_store()) |
| 151 return PasswordStoreDefault::FillAutofillableLogins(forms); |
| 152 return false; |
| 153 } |
| 154 |
| 155 bool PasswordStoreX::FillBlacklistLogins(vector<PasswordForm*>* forms) { |
| 156 CheckMigration(); |
| 157 if (use_native_backend() && backend_->GetBlacklistLogins(forms)) { |
| 158 allow_fallback_ = false; |
| 159 return true; |
| 160 } |
| 161 if (allow_default_store()) |
| 162 return PasswordStoreDefault::FillBlacklistLogins(forms); |
| 163 return false; |
| 164 } |
| 165 |
| 166 void PasswordStoreX::CheckMigration() { |
| 167 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); |
| 168 if (migration_checked_ || !backend_.get()) |
| 169 return; |
| 170 migration_checked_ = true; |
| 171 ssize_t migrated = MigrateLogins(); |
| 172 if (migrated > 0) { |
| 173 LOG(INFO) << "Migrated " << migrated << " passwords to native store."; |
| 174 } else if (migrated == 0) { |
| 175 // As long as we are able to migrate some passwords, we know the native |
| 176 // store is working. But if there is nothing to migrate, the "migration" |
| 177 // can succeed even when the native store would fail. In this case we |
| 178 // allow a later fallback to the default store. Once any later operation |
| 179 // succeeds on the native store, we will no longer allow it. |
| 180 allow_fallback_ = true; |
| 181 } else { |
| 182 LOG(WARNING) << "Native password store migration failed! " << |
| 183 "Falling back on default (unencrypted) store."; |
| 184 backend_.reset(NULL); |
| 185 } |
| 186 } |
| 187 |
| 188 bool PasswordStoreX::allow_default_store() { |
| 189 if (allow_fallback_) { |
| 190 LOG(WARNING) << "Native password store failed! " << |
| 191 "Falling back on default (unencrypted) store."; |
| 192 backend_.reset(NULL); |
| 193 // Don't warn again. We'll use the default store because backend_ is NULL. |
| 194 allow_fallback_ = false; |
| 195 } |
| 196 return !backend_.get(); |
| 197 } |
| 198 |
| 199 ssize_t PasswordStoreX::MigrateLogins() { |
| 200 DCHECK(backend_.get()); |
| 201 vector<PasswordForm*> forms; |
| 202 bool ok = PasswordStoreDefault::FillAutofillableLogins(&forms) && |
| 203 PasswordStoreDefault::FillBlacklistLogins(&forms); |
| 204 if (ok) { |
| 205 // We add all the passwords (and blacklist entries) to the native backend |
| 206 // before attempting to remove any from the login database, to make sure we |
| 207 // don't somehow end up with some of the passwords in one store and some in |
| 208 // another. We'll always have at least one intact store this way. |
| 209 for (size_t i = 0; i < forms.size(); ++i) { |
| 210 if (!backend_->AddLogin(*forms[i])) { |
| 211 ok = false; |
| 212 break; |
| 213 } |
| 214 } |
| 215 if (ok) { |
| 216 for (size_t i = 0; i < forms.size(); ++i) { |
| 217 // If even one of these calls to RemoveLoginImpl() succeeds, then we |
| 218 // should prefer the native backend to the now-incomplete login |
| 219 // database. Thus we want to return a success status even in the case |
| 220 // where some fail. The only real problem with this is that we might |
| 221 // leave passwords in the login database and never come back to clean |
| 222 // them out if any of these calls do fail. |
| 223 // TODO(mdm): Really we should just delete the login database file. |
| 224 PasswordStoreDefault::RemoveLoginImpl(*forms[i]); |
| 225 } |
| 226 } |
| 227 } |
| 228 ssize_t result = ok ? forms.size() : -1; |
| 229 STLDeleteElements(&forms); |
| 230 return result; |
| 231 } |
OLD | NEW |