1 // Copyright (c) 2012 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.
5 #include "chrome/browser/password_manager/native_backend_gnome_x.h"
8 #include <gnome-keyring.h>
14 #include "base/basictypes.h"
15 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/metrics/histogram.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_piece.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/synchronization/waitable_event.h"
24 #include "base/time/time.h"
25 #include "components/autofill/core/common/password_form.h"
26 #include "components/password_manager/core/browser/psl_matching_helper.h"
27 #include "content/public/browser/browser_thread.h"
29 using autofill::PasswordForm;
30 using base::UTF8ToUTF16;
31 using base::UTF16ToUTF8;
32 using content::BrowserThread;
33 using password_manager::PSLMatchingHelper;
35 #define GNOME_KEYRING_DEFINE_POINTER(name) \
36 typeof(&::gnome_keyring_##name) GnomeKeyringLoader::gnome_keyring_##name;
37 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_POINTER)
38 #undef GNOME_KEYRING_DEFINE_POINTER
40 bool GnomeKeyringLoader::keyring_loaded = false;
42 #if defined(DLOPEN_GNOME_KEYRING)
44 #define GNOME_KEYRING_FUNCTION_INFO(name) \
45 {"gnome_keyring_"#name, reinterpret_cast<void**>(&gnome_keyring_##name)},
46 const GnomeKeyringLoader::FunctionInfo GnomeKeyringLoader::functions[] = {
47 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION_INFO)
50 #undef GNOME_KEYRING_FUNCTION_INFO
52 /* Load the library and initialize the function pointers. */
53 bool GnomeKeyringLoader::LoadGnomeKeyring() {
57 void* handle = dlopen("libgnome-keyring.so.0", RTLD_NOW | RTLD_GLOBAL);
59 // We wanted to use GNOME Keyring, but we couldn't load it. Warn, because
60 // either the user asked for this, or we autodetected it incorrectly. (Or
61 // the system has broken libraries, which is also good to warn about.)
62 LOG(WARNING) << "Could not load libgnome-keyring.so.0: " << dlerror();
66 for (size_t i = 0; functions[i].name; ++i) {
68 *functions[i].pointer = dlsym(handle, functions[i].name);
69 const char* error = dlerror();
71 LOG(ERROR) << "Unable to load symbol "
72 << functions[i].name << ": " << error;
78 keyring_loaded = true;
79 // We leak the library handle. That's OK: this function is called only once.
83 #else // defined(DLOPEN_GNOME_KEYRING)
85 bool GnomeKeyringLoader::LoadGnomeKeyring() {
88 #define GNOME_KEYRING_ASSIGN_POINTER(name) \
89 gnome_keyring_##name = &::gnome_keyring_##name;
90 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_ASSIGN_POINTER)
91 #undef GNOME_KEYRING_ASSIGN_POINTER
92 keyring_loaded = true;
96 #endif // defined(DLOPEN_GNOME_KEYRING)
100 const char kGnomeKeyringAppString[] = "chrome";
102 // Convert the attributes of a given keyring entry into a new PasswordForm.
103 // Note: does *not* get the actual password, as that is not a key attribute!
104 // Returns NULL if the attributes are for the wrong application.
105 scoped_ptr<PasswordForm> FormFromAttributes(GnomeKeyringAttributeList* attrs) {
106 // Read the string and int attributes into the appropriate map.
107 std::map<std::string, std::string> string_attr_map;
108 std::map<std::string, uint32_t> uint_attr_map;
109 for (guint i = 0; i < attrs->len; ++i) {
110 GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i);
111 if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)
112 string_attr_map[attr.name] = attr.value.string;
113 else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32)
114 uint_attr_map[attr.name] = attr.value.integer;
116 // Check to make sure this is a password we care about.
117 const std::string& app_value = string_attr_map["application"];
118 if (!base::StringPiece(app_value).starts_with(kGnomeKeyringAppString))
119 return scoped_ptr<PasswordForm>();
121 scoped_ptr<PasswordForm> form(new PasswordForm());
122 form->origin = GURL(string_attr_map["origin_url"]);
123 form->action = GURL(string_attr_map["action_url"]);
124 form->username_element = UTF8ToUTF16(string_attr_map["username_element"]);
125 form->username_value = UTF8ToUTF16(string_attr_map["username_value"]);
126 form->password_element = UTF8ToUTF16(string_attr_map["password_element"]);
127 form->submit_element = UTF8ToUTF16(string_attr_map["submit_element"]);
128 form->signon_realm = string_attr_map["signon_realm"];
129 form->ssl_valid = uint_attr_map["ssl_valid"];
130 form->preferred = uint_attr_map["preferred"];
131 int64 date_created = 0;
132 bool date_ok = base::StringToInt64(string_attr_map["date_created"],
135 form->date_created = base::Time::FromTimeT(date_created);
136 form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"];
137 form->type = static_cast<PasswordForm::Type>(uint_attr_map["type"]);
138 form->times_used = uint_attr_map["times_used"];
139 form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]);
140 int64 date_synced = 0;
141 base::StringToInt64(string_attr_map["date_synced"], &date_synced);
142 form->date_synced = base::Time::FromInternalValue(date_synced);
147 // Parse all the results from the given GList into a PasswordFormList, and free
148 // the GList. PasswordForms are allocated on the heap, and should be deleted by
149 // the consumer. If not NULL, |lookup_form| is used to filter out results --
150 // only credentials with signon realms passing the PSL matching (done by
151 // |helper|) against |lookup_form->signon_realm| will be kept. PSL matched
152 // results get their signon_realm, origin, and action rewritten to those of
153 // |lookup_form_|, with the original signon_realm saved into the result's
154 // original_signon_realm data member.
155 void ConvertFormList(GList* found,
156 const PasswordForm* lookup_form,
157 const PSLMatchingHelper& helper,
158 NativeBackendGnome::PasswordFormList* forms) {
159 PSLMatchingHelper::PSLDomainMatchMetric psl_domain_match_metric =
160 PSLMatchingHelper::PSL_DOMAIN_MATCH_NONE;
161 for (GList* element = g_list_first(found); element != NULL;
162 element = g_list_next(element)) {
163 GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
164 GnomeKeyringAttributeList* attrs = data->attributes;
166 scoped_ptr<PasswordForm> form(FormFromAttributes(attrs));
168 if (lookup_form && form->signon_realm != lookup_form->signon_realm) {
169 // This is not an exact match, we try PSL matching.
170 if (lookup_form->scheme != PasswordForm::SCHEME_HTML ||
171 form->scheme != PasswordForm::SCHEME_HTML ||
172 !(PSLMatchingHelper::IsPublicSuffixDomainMatch(
173 lookup_form->signon_realm, form->signon_realm))) {
176 psl_domain_match_metric = PSLMatchingHelper::PSL_DOMAIN_MATCH_FOUND;
177 form->original_signon_realm = form->signon_realm;
178 form->signon_realm = lookup_form->signon_realm;
179 form->origin = lookup_form->origin;
180 form->action = lookup_form->action;
183 form->password_value = UTF8ToUTF16(data->secret);
185 LOG(WARNING) << "Unable to access password from list element!";
187 forms->push_back(form.release());
189 LOG(WARNING) << "Could not initialize PasswordForm from attributes!";
193 UMA_HISTOGRAM_ENUMERATION(
194 "PasswordManager.PslDomainMatchTriggering",
195 helper.IsMatchingEnabled()
196 ? psl_domain_match_metric
197 : PSLMatchingHelper::PSL_DOMAIN_MATCH_DISABLED,
198 PSLMatchingHelper::PSL_DOMAIN_MATCH_COUNT);
202 // Schema is analagous to the fields in PasswordForm.
203 // TODO(gcasto): Adding 'form_data' would be nice, but we would need to
204 // serialize in a way that is guaranteed to not have any embedded NULLs. Pickle
205 // doesn't make this guarantee, so we just don't serialize this field. Since
206 // it's only used to crowd source data collection it doesn't matter that much
207 // if it's not available on this platform.
208 const GnomeKeyringPasswordSchema kGnomeSchema = {
209 GNOME_KEYRING_ITEM_GENERIC_SECRET, {
210 { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
211 { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
212 { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
213 { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
214 { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
215 { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
216 { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
217 { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
218 { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
219 { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
220 { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
221 { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
222 { "type", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
223 { "times_used", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
224 { "date_synced", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
225 // This field is always "chrome" so that we can search for it.
226 { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
231 // Sadly, PasswordStore goes to great lengths to switch from the originally
232 // calling thread to the DB thread, and to provide an asynchronous API to
233 // callers while using a synchronous (virtual) API provided by subclasses like
234 // PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main
235 // thread, which is the UI thread to us. So we end up having to switch threads
236 // again, possibly back to the very same thread (in case the UI thread is the
237 // caller, e.g. in the password management UI), and *block* the DB thread
238 // waiting for a response from the UI thread to provide the synchronous API
239 // PasswordStore expects of us. (It will then in turn switch back to the
240 // original caller to send the asynchronous reply to the original request.)
242 // This class represents a call to a GNOME Keyring method. A RunnableMethod
243 // should be posted to the UI thread to call one of its action methods, and then
244 // a WaitResult() method should be called to wait for the result. Each instance
245 // supports only one outstanding method at a time, though multiple instances may
246 // be used in parallel.
247 class GKRMethod : public GnomeKeyringLoader {
249 typedef NativeBackendGnome::PasswordFormList PasswordFormList;
251 GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {}
253 // Action methods. These call gnome_keyring_* functions. Call from UI thread.
254 // See GetProfileSpecificAppString() for more information on the app string.
255 void AddLogin(const PasswordForm& form, const char* app_string);
256 void AddLoginSearch(const PasswordForm& form, const char* app_string);
257 void UpdateLoginSearch(const PasswordForm& form, const char* app_string);
258 void RemoveLogin(const PasswordForm& form, const char* app_string);
259 void GetLogins(const PasswordForm& form, const char* app_string);
260 void GetLoginsList(uint32_t blacklisted_by_user, const char* app_string);
261 void GetAllLogins(const char* app_string);
263 // Use after AddLogin, RemoveLogin.
264 GnomeKeyringResult WaitResult();
266 // Use after AddLoginSearch, UpdateLoginSearch, GetLogins, GetLoginsList,
268 GnomeKeyringResult WaitResult(PasswordFormList* forms);
271 struct GnomeKeyringAttributeListFreeDeleter {
272 inline void operator()(void* list) const {
273 gnome_keyring_attribute_list_free(
274 static_cast<GnomeKeyringAttributeList*>(list));
278 typedef scoped_ptr<GnomeKeyringAttributeList,
279 GnomeKeyringAttributeListFreeDeleter> ScopedAttributeList;
281 // Helper methods to abbreviate Gnome Keyring long API names.
282 static void AppendString(ScopedAttributeList* list,
285 static void AppendString(ScopedAttributeList* list,
287 const std::string& value);
288 static void AppendUint32(ScopedAttributeList* list,
292 // All these callbacks are called on UI thread.
293 static void OnOperationDone(GnomeKeyringResult result, gpointer data);
295 static void OnOperationGetList(GnomeKeyringResult result, GList* list,
298 base::WaitableEvent event_;
299 GnomeKeyringResult result_;
300 NativeBackendGnome::PasswordFormList forms_;
301 // If the credential search is specified by a single form and needs to use PSL
302 // matching, then the specifying form is stored in |lookup_form_|. If PSL
303 // matching is used to find a result, then the results signon realm, origin
304 // and action are stored are replaced by those of |lookup_form_|.
305 // Additionally, |lookup_form_->signon_realm| is also used to narrow down the
306 // found logins to those which indeed PSL-match the look-up. And finally,
307 // |lookup_form_| set to NULL means that PSL matching is not required.
308 scoped_ptr<PasswordForm> lookup_form_;
309 const PSLMatchingHelper helper_;
312 void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) {
313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
314 time_t date_created = form.date_created.ToTimeT();
315 // If we are asked to save a password with 0 date, use the current time.
316 // We don't want to actually save passwords as though on January 1, 1970.
318 date_created = time(NULL);
319 int64 date_synced = form.date_synced.ToInternalValue();
320 gnome_keyring_store_password(
322 NULL, // Default keyring.
323 form.origin.spec().c_str(), // Display name.
324 UTF16ToUTF8(form.password_value).c_str(),
327 NULL, // destroy_data
328 "origin_url", form.origin.spec().c_str(),
329 "action_url", form.action.spec().c_str(),
330 "username_element", UTF16ToUTF8(form.username_element).c_str(),
331 "username_value", UTF16ToUTF8(form.username_value).c_str(),
332 "password_element", UTF16ToUTF8(form.password_element).c_str(),
333 "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
334 "signon_realm", form.signon_realm.c_str(),
335 "ssl_valid", form.ssl_valid,
336 "preferred", form.preferred,
337 "date_created", base::Int64ToString(date_created).c_str(),
338 "blacklisted_by_user", form.blacklisted_by_user,
340 "times_used", form.times_used,
341 "scheme", form.scheme,
342 "date_synced", base::Int64ToString(date_synced).c_str(),
343 "application", app_string,
347 void GKRMethod::AddLoginSearch(const PasswordForm& form,
348 const char* app_string) {
349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
350 lookup_form_.reset(NULL);
351 // Search GNOME Keyring for matching passwords to update.
352 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
353 AppendString(&attrs, "origin_url", form.origin.spec());
354 AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
355 AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
356 AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
357 AppendString(&attrs, "submit_element", UTF16ToUTF8(form.submit_element));
358 AppendString(&attrs, "signon_realm", form.signon_realm);
359 AppendString(&attrs, "application", app_string);
360 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
364 /*destroy_data=*/NULL);
367 void GKRMethod::UpdateLoginSearch(const PasswordForm& form,
368 const char* app_string) {
369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
370 lookup_form_.reset(NULL);
371 // Search GNOME Keyring for matching passwords to update.
372 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
373 AppendString(&attrs, "origin_url", form.origin.spec());
374 AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
375 AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
376 AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
377 AppendString(&attrs, "signon_realm", form.signon_realm);
378 AppendString(&attrs, "application", app_string);
379 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
383 /*destroy_data=*/NULL);
386 void GKRMethod::RemoveLogin(const PasswordForm& form, const char* app_string) {
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
388 // We find forms using the same fields as LoginDatabase::RemoveLogin().
389 gnome_keyring_delete_password(
393 NULL, // destroy_data
394 "origin_url", form.origin.spec().c_str(),
395 "action_url", form.action.spec().c_str(),
396 "username_element", UTF16ToUTF8(form.username_element).c_str(),
397 "username_value", UTF16ToUTF8(form.username_value).c_str(),
398 "password_element", UTF16ToUTF8(form.password_element).c_str(),
399 "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
400 "signon_realm", form.signon_realm.c_str(),
401 "application", app_string,
405 void GKRMethod::GetLogins(const PasswordForm& form, const char* app_string) {
406 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
407 lookup_form_.reset(new PasswordForm(form));
408 // Search GNOME Keyring for matching passwords.
409 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
410 if (!helper_.ShouldPSLDomainMatchingApply(
411 PSLMatchingHelper::GetRegistryControlledDomain(
412 GURL(form.signon_realm)))) {
413 AppendString(&attrs, "signon_realm", form.signon_realm);
415 AppendString(&attrs, "application", app_string);
416 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
420 /*destroy_data=*/NULL);
423 void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user,
424 const char* app_string) {
425 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
426 lookup_form_.reset(NULL);
427 // Search GNOME Keyring for matching passwords.
428 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
429 AppendUint32(&attrs, "blacklisted_by_user", blacklisted_by_user);
430 AppendString(&attrs, "application", app_string);
431 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
435 /*destroy_data=*/NULL);
438 void GKRMethod::GetAllLogins(const char* app_string) {
439 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
440 lookup_form_.reset(NULL);
441 // We need to search for something, otherwise we get no results - so
442 // we search for the fixed application string.
443 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
444 AppendString(&attrs, "application", app_string);
445 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
449 /*destroy_data=*/NULL);
452 GnomeKeyringResult GKRMethod::WaitResult() {
453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
458 GnomeKeyringResult GKRMethod::WaitResult(PasswordFormList* forms) {
459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
461 if (forms->empty()) {
462 // Normal case. Avoid extra allocation by swapping.
465 // Rare case. Append forms_ to *forms.
466 forms->insert(forms->end(), forms_.begin(), forms_.end());
473 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
476 gnome_keyring_attribute_list_append_string(list->get(), name, value);
480 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
482 const std::string& value) {
483 AppendString(list, name, value.c_str());
487 void GKRMethod::AppendUint32(GKRMethod::ScopedAttributeList* list,
490 gnome_keyring_attribute_list_append_uint32(list->get(), name, value);
494 void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) {
495 GKRMethod* method = static_cast<GKRMethod*>(data);
496 method->result_ = result;
497 method->event_.Signal();
501 void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list,
503 GKRMethod* method = static_cast<GKRMethod*>(data);
504 method->result_ = result;
505 method->forms_.clear();
506 // |list| will be freed after this callback returns, so convert it now.
508 list, method->lookup_form_.get(), method->helper_, &method->forms_);
509 method->lookup_form_.reset(NULL);
510 method->event_.Signal();
515 NativeBackendGnome::NativeBackendGnome(LocalProfileId id)
517 app_string_ = GetProfileSpecificAppString();
520 NativeBackendGnome::~NativeBackendGnome() {
523 bool NativeBackendGnome::Init() {
524 return LoadGnomeKeyring() && gnome_keyring_is_available();
527 bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) {
528 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
530 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
531 base::Bind(&GKRMethod::AddLogin,
532 base::Unretained(&method),
533 form, app_string_.c_str()));
534 GnomeKeyringResult result = method.WaitResult();
535 if (result != GNOME_KEYRING_RESULT_OK) {
536 LOG(ERROR) << "Keyring save failed: "
537 << gnome_keyring_result_to_message(result);
543 password_manager::PasswordStoreChangeList NativeBackendGnome::AddLogin(
544 const PasswordForm& form) {
545 // Based on LoginDatabase::AddLogin(), we search for an existing match based
546 // on origin_url, username_element, username_value, password_element, submit
547 // element, and signon_realm first, remove that, and then add the new entry.
548 // We'd add the new one first, and then delete the original, but then the
549 // delete might actually delete the newly-added entry!
550 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
552 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
553 base::Bind(&GKRMethod::AddLoginSearch,
554 base::Unretained(&method),
555 form, app_string_.c_str()));
556 ScopedVector<autofill::PasswordForm> forms;
557 GnomeKeyringResult result = method.WaitResult(&forms.get());
558 if (result != GNOME_KEYRING_RESULT_OK &&
559 result != GNOME_KEYRING_RESULT_NO_MATCH) {
560 LOG(ERROR) << "Keyring find failed: "
561 << gnome_keyring_result_to_message(result);
562 return password_manager::PasswordStoreChangeList();
564 password_manager::PasswordStoreChangeList changes;
565 if (forms.size() > 0) {
566 if (forms.size() > 1) {
567 LOG(WARNING) << "Adding login when there are " << forms.size()
568 << " matching logins already! Will replace only the first.";
571 if (RemoveLogin(*forms[0])) {
572 changes.push_back(password_manager::PasswordStoreChange(
573 password_manager::PasswordStoreChange::REMOVE, *forms[0]));
576 if (RawAddLogin(form)) {
577 changes.push_back(password_manager::PasswordStoreChange(
578 password_manager::PasswordStoreChange::ADD, form));
583 bool NativeBackendGnome::UpdateLogin(
584 const PasswordForm& form,
585 password_manager::PasswordStoreChangeList* changes) {
586 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
587 // origin_url, username_element, username_value, password_element, and
588 // signon_realm. We then compare the result to the updated form. If they
589 // differ in any of the mutable fields, then we remove the original, and
590 // then add the new entry. We'd add the new one first, and then delete the
591 // original, but then the delete might actually delete the newly-added entry!
592 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
596 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
597 base::Bind(&GKRMethod::UpdateLoginSearch,
598 base::Unretained(&method),
599 form, app_string_.c_str()));
600 ScopedVector<autofill::PasswordForm> forms;
601 GnomeKeyringResult result = method.WaitResult(&forms.get());
602 if (result != GNOME_KEYRING_RESULT_OK) {
603 LOG(ERROR) << "Keyring find failed: "
604 << gnome_keyring_result_to_message(result);
608 bool removed = false;
609 for (size_t i = 0; i < forms.size(); ++i) {
610 if (*forms[i] != form) {
611 RemoveLogin(*forms[i]);
618 if (RawAddLogin(form)) {
619 password_manager::PasswordStoreChange change(
620 password_manager::PasswordStoreChange::UPDATE, form);
621 changes->push_back(change);
627 bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
628 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
630 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
631 base::Bind(&GKRMethod::RemoveLogin,
632 base::Unretained(&method),
633 form, app_string_.c_str()));
634 GnomeKeyringResult result = method.WaitResult();
635 if (result != GNOME_KEYRING_RESULT_OK) {
636 // Warning, not error, because this can sometimes happen due to the user
637 // racing with the daemon to delete the password a second time.
638 LOG(WARNING) << "Keyring delete failed: "
639 << gnome_keyring_result_to_message(result);
645 bool NativeBackendGnome::RemoveLoginsCreatedBetween(
646 base::Time delete_begin,
647 base::Time delete_end,
648 password_manager::PasswordStoreChangeList* changes) {
649 return RemoveLoginsBetween(
650 delete_begin, delete_end, CREATION_TIMESTAMP, changes);
653 bool NativeBackendGnome::RemoveLoginsSyncedBetween(
654 base::Time delete_begin,
655 base::Time delete_end,
656 password_manager::PasswordStoreChangeList* changes) {
657 return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes);
660 bool NativeBackendGnome::GetLogins(const PasswordForm& form,
661 PasswordFormList* forms) {
662 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
664 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
665 base::Bind(&GKRMethod::GetLogins,
666 base::Unretained(&method),
667 form, app_string_.c_str()));
668 GnomeKeyringResult result = method.WaitResult(forms);
669 if (result == GNOME_KEYRING_RESULT_NO_MATCH)
671 if (result != GNOME_KEYRING_RESULT_OK) {
672 LOG(ERROR) << "Keyring find failed: "
673 << gnome_keyring_result_to_message(result);
679 bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
680 return GetLoginsList(forms, true);
683 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
684 return GetLoginsList(forms, false);
687 bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms,
689 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
691 uint32_t blacklisted_by_user = !autofillable;
694 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
695 base::Bind(&GKRMethod::GetLoginsList,
696 base::Unretained(&method),
697 blacklisted_by_user, app_string_.c_str()));
698 GnomeKeyringResult result = method.WaitResult(forms);
699 if (result == GNOME_KEYRING_RESULT_NO_MATCH)
701 if (result != GNOME_KEYRING_RESULT_OK) {
702 LOG(ERROR) << "Keyring find failed: "
703 << gnome_keyring_result_to_message(result);
709 bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) {
711 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
712 base::Bind(&GKRMethod::GetAllLogins,
713 base::Unretained(&method),
714 app_string_.c_str()));
715 GnomeKeyringResult result = method.WaitResult(forms);
716 if (result == GNOME_KEYRING_RESULT_NO_MATCH)
718 if (result != GNOME_KEYRING_RESULT_OK) {
719 LOG(ERROR) << "Keyring find failed: "
720 << gnome_keyring_result_to_message(result);
726 bool NativeBackendGnome::GetLoginsBetween(base::Time get_begin,
728 TimestampToCompare date_to_compare,
729 PasswordFormList* forms) {
730 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
731 // We could walk the list and add items as we find them, but it is much
732 // easier to build the list and then filter the results.
733 PasswordFormList all_forms;
734 if (!GetAllLogins(&all_forms))
737 base::Time autofill::PasswordForm::*date_member =
738 date_to_compare == CREATION_TIMESTAMP
739 ? &autofill::PasswordForm::date_created
740 : &autofill::PasswordForm::date_synced;
741 for (size_t i = 0; i < all_forms.size(); ++i) {
742 if (get_begin <= all_forms[i]->*date_member &&
743 (get_end.is_null() || all_forms[i]->*date_member < get_end)) {
744 forms->push_back(all_forms[i]);
753 bool NativeBackendGnome::RemoveLoginsBetween(
754 base::Time get_begin,
756 TimestampToCompare date_to_compare,
757 password_manager::PasswordStoreChangeList* changes) {
758 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
761 // We could walk the list and delete items as we find them, but it is much
762 // easier to build the list and use RemoveLogin() to delete them.
763 ScopedVector<autofill::PasswordForm> forms;
764 if (!GetLoginsBetween(get_begin, get_end, date_to_compare, &forms.get()))
768 for (size_t i = 0; i < forms.size(); ++i) {
769 if (RemoveLogin(*forms[i])) {
770 changes->push_back(password_manager::PasswordStoreChange(
771 password_manager::PasswordStoreChange::REMOVE, *forms[i]));
779 std::string NativeBackendGnome::GetProfileSpecificAppString() const {
780 // Originally, the application string was always just "chrome" and used only
781 // so that we had *something* to search for since GNOME Keyring won't search
782 // for nothing. Now we use it to distinguish passwords for different profiles.
783 return base::StringPrintf("%s-%d", kGnomeKeyringAppString, profile_id_);