Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / password_manager / native_backend_gnome_x.cc
blob1035e951af10ade381290ccfce503b879da0180b
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"
7 #include <dlfcn.h>
8 #include <gnome-keyring.h>
10 #include <map>
11 #include <string>
12 #include <vector>
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;
34 #define GNOME_KEYRING_DEFINE_POINTER(name) \
35 typeof(&::gnome_keyring_##name) GnomeKeyringLoader::gnome_keyring_##name;
36 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_POINTER)
37 #undef GNOME_KEYRING_DEFINE_POINTER
39 bool GnomeKeyringLoader::keyring_loaded = false;
41 #if defined(DLOPEN_GNOME_KEYRING)
43 #define GNOME_KEYRING_FUNCTION_INFO(name) \
44 {"gnome_keyring_"#name, reinterpret_cast<void**>(&gnome_keyring_##name)},
45 const GnomeKeyringLoader::FunctionInfo GnomeKeyringLoader::functions[] = {
46 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION_INFO)
47 {NULL, NULL}
49 #undef GNOME_KEYRING_FUNCTION_INFO
51 /* Load the library and initialize the function pointers. */
52 bool GnomeKeyringLoader::LoadGnomeKeyring() {
53 if (keyring_loaded)
54 return true;
56 void* handle = dlopen("libgnome-keyring.so.0", RTLD_NOW | RTLD_GLOBAL);
57 if (!handle) {
58 // We wanted to use GNOME Keyring, but we couldn't load it. Warn, because
59 // either the user asked for this, or we autodetected it incorrectly. (Or
60 // the system has broken libraries, which is also good to warn about.)
61 LOG(WARNING) << "Could not load libgnome-keyring.so.0: " << dlerror();
62 return false;
65 for (size_t i = 0; functions[i].name; ++i) {
66 dlerror();
67 *functions[i].pointer = dlsym(handle, functions[i].name);
68 const char* error = dlerror();
69 if (error) {
70 LOG(ERROR) << "Unable to load symbol "
71 << functions[i].name << ": " << error;
72 dlclose(handle);
73 return false;
77 keyring_loaded = true;
78 // We leak the library handle. That's OK: this function is called only once.
79 return true;
82 #else // defined(DLOPEN_GNOME_KEYRING)
84 bool GnomeKeyringLoader::LoadGnomeKeyring() {
85 if (keyring_loaded)
86 return true;
87 #define GNOME_KEYRING_ASSIGN_POINTER(name) \
88 gnome_keyring_##name = &::gnome_keyring_##name;
89 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_ASSIGN_POINTER)
90 #undef GNOME_KEYRING_ASSIGN_POINTER
91 keyring_loaded = true;
92 return true;
95 #endif // defined(DLOPEN_GNOME_KEYRING)
97 namespace {
99 const char kGnomeKeyringAppString[] = "chrome";
101 // Convert the attributes of a given keyring entry into a new PasswordForm.
102 // Note: does *not* get the actual password, as that is not a key attribute!
103 // Returns NULL if the attributes are for the wrong application.
104 scoped_ptr<PasswordForm> FormFromAttributes(GnomeKeyringAttributeList* attrs) {
105 // Read the string and int attributes into the appropriate map.
106 std::map<std::string, std::string> string_attr_map;
107 std::map<std::string, uint32_t> uint_attr_map;
108 for (guint i = 0; i < attrs->len; ++i) {
109 GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i);
110 if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)
111 string_attr_map[attr.name] = attr.value.string;
112 else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32)
113 uint_attr_map[attr.name] = attr.value.integer;
115 // Check to make sure this is a password we care about.
116 const std::string& app_value = string_attr_map["application"];
117 if (!base::StringPiece(app_value).starts_with(kGnomeKeyringAppString))
118 return scoped_ptr<PasswordForm>();
120 scoped_ptr<PasswordForm> form(new PasswordForm());
121 form->origin = GURL(string_attr_map["origin_url"]);
122 form->action = GURL(string_attr_map["action_url"]);
123 form->username_element = UTF8ToUTF16(string_attr_map["username_element"]);
124 form->username_value = UTF8ToUTF16(string_attr_map["username_value"]);
125 form->password_element = UTF8ToUTF16(string_attr_map["password_element"]);
126 form->submit_element = UTF8ToUTF16(string_attr_map["submit_element"]);
127 form->signon_realm = string_attr_map["signon_realm"];
128 form->ssl_valid = uint_attr_map["ssl_valid"];
129 form->preferred = uint_attr_map["preferred"];
130 int64 date_created = 0;
131 bool date_ok = base::StringToInt64(string_attr_map["date_created"],
132 &date_created);
133 DCHECK(date_ok);
134 form->date_created = base::Time::FromTimeT(date_created);
135 form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"];
136 form->type = static_cast<PasswordForm::Type>(uint_attr_map["type"]);
137 form->times_used = uint_attr_map["times_used"];
138 form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]);
139 int64 date_synced = 0;
140 base::StringToInt64(string_attr_map["date_synced"], &date_synced);
141 form->date_synced = base::Time::FromInternalValue(date_synced);
142 form->display_name = UTF8ToUTF16(string_attr_map["display_name"]);
143 form->avatar_url = GURL(string_attr_map["avatar_url"]);
144 form->federation_url = GURL(string_attr_map["federation_url"]);
145 form->is_zero_click = uint_attr_map["is_zero_click"];
147 return form.Pass();
150 // Parse all the results from the given GList into a PasswordFormList, and free
151 // the GList. PasswordForms are allocated on the heap, and should be deleted by
152 // the consumer. If not NULL, |lookup_form| is used to filter out results --
153 // only credentials with signon realms passing the PSL matching against
154 // |lookup_form->signon_realm| will be kept. PSL matched results get their
155 // signon_realm, origin, and action rewritten to those of |lookup_form_|, with
156 // the original signon_realm saved into the result's original_signon_realm data
157 // member.
158 void ConvertFormList(GList* found,
159 const PasswordForm* lookup_form,
160 NativeBackendGnome::PasswordFormList* forms) {
161 password_manager::PSLDomainMatchMetric psl_domain_match_metric =
162 password_manager::PSL_DOMAIN_MATCH_NONE;
163 for (GList* element = g_list_first(found); element != NULL;
164 element = g_list_next(element)) {
165 GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
166 GnomeKeyringAttributeList* attrs = data->attributes;
168 scoped_ptr<PasswordForm> form(FormFromAttributes(attrs));
169 if (form) {
170 if (lookup_form && form->signon_realm != lookup_form->signon_realm) {
171 // This is not an exact match, we try PSL matching.
172 if (lookup_form->scheme != PasswordForm::SCHEME_HTML ||
173 form->scheme != PasswordForm::SCHEME_HTML ||
174 !(password_manager::IsPublicSuffixDomainMatch(
175 lookup_form->signon_realm, form->signon_realm))) {
176 continue;
178 psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND;
179 form->original_signon_realm = form->signon_realm;
180 form->signon_realm = lookup_form->signon_realm;
181 form->origin = lookup_form->origin;
182 form->action = lookup_form->action;
184 if (data->secret) {
185 form->password_value = UTF8ToUTF16(data->secret);
186 } else {
187 LOG(WARNING) << "Unable to access password from list element!";
189 forms->push_back(form.release());
190 } else {
191 LOG(WARNING) << "Could not initialize PasswordForm from attributes!";
194 if (lookup_form) {
195 const GURL signon_realm(lookup_form->signon_realm);
196 std::string registered_domain =
197 password_manager::GetRegistryControlledDomain(signon_realm);
198 UMA_HISTOGRAM_ENUMERATION(
199 "PasswordManager.PslDomainMatchTriggering",
200 password_manager::ShouldPSLDomainMatchingApply(registered_domain)
201 ? psl_domain_match_metric
202 : password_manager::PSL_DOMAIN_MATCH_NOT_USED,
203 password_manager::PSL_DOMAIN_MATCH_COUNT);
207 // Schema is analagous to the fields in PasswordForm.
208 // TODO(gcasto): Adding 'form_data' would be nice, but we would need to
209 // serialize in a way that is guaranteed to not have any embedded NULLs. Pickle
210 // doesn't make this guarantee, so we just don't serialize this field. Since
211 // it's only used to crowd source data collection it doesn't matter that much
212 // if it's not available on this platform.
213 const GnomeKeyringPasswordSchema kGnomeSchema = {
214 GNOME_KEYRING_ITEM_GENERIC_SECRET, {
215 { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
216 { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
217 { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
218 { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
219 { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
220 { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
221 { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
222 { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
223 { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
224 { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
225 { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
226 { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
227 { "type", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
228 { "times_used", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
229 { "date_synced", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
230 { "display_name", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
231 { "avatar_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
232 { "federation_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
233 { "is_zero_click", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
234 // This field is always "chrome" so that we can search for it.
235 { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
236 { NULL }
240 // Sadly, PasswordStore goes to great lengths to switch from the originally
241 // calling thread to the DB thread, and to provide an asynchronous API to
242 // callers while using a synchronous (virtual) API provided by subclasses like
243 // PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main
244 // thread, which is the UI thread to us. So we end up having to switch threads
245 // again, possibly back to the very same thread (in case the UI thread is the
246 // caller, e.g. in the password management UI), and *block* the DB thread
247 // waiting for a response from the UI thread to provide the synchronous API
248 // PasswordStore expects of us. (It will then in turn switch back to the
249 // original caller to send the asynchronous reply to the original request.)
251 // This class represents a call to a GNOME Keyring method. A RunnableMethod
252 // should be posted to the UI thread to call one of its action methods, and then
253 // a WaitResult() method should be called to wait for the result. Each instance
254 // supports only one outstanding method at a time, though multiple instances may
255 // be used in parallel.
256 class GKRMethod : public GnomeKeyringLoader {
257 public:
258 typedef NativeBackendGnome::PasswordFormList PasswordFormList;
260 GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {}
262 // Action methods. These call gnome_keyring_* functions. Call from UI thread.
263 // See GetProfileSpecificAppString() for more information on the app string.
264 void AddLogin(const PasswordForm& form, const char* app_string);
265 void AddLoginSearch(const PasswordForm& form, const char* app_string);
266 void UpdateLoginSearch(const PasswordForm& form, const char* app_string);
267 void RemoveLogin(const PasswordForm& form, const char* app_string);
268 void GetLogins(const PasswordForm& form, const char* app_string);
269 void GetLoginsList(uint32_t blacklisted_by_user, const char* app_string);
270 void GetAllLogins(const char* app_string);
272 // Use after AddLogin, RemoveLogin.
273 GnomeKeyringResult WaitResult();
275 // Use after AddLoginSearch, UpdateLoginSearch, GetLogins, GetLoginsList,
276 // GetAllLogins.
277 GnomeKeyringResult WaitResult(PasswordFormList* forms);
279 private:
280 struct GnomeKeyringAttributeListFreeDeleter {
281 inline void operator()(void* list) const {
282 gnome_keyring_attribute_list_free(
283 static_cast<GnomeKeyringAttributeList*>(list));
287 typedef scoped_ptr<GnomeKeyringAttributeList,
288 GnomeKeyringAttributeListFreeDeleter> ScopedAttributeList;
290 // Helper methods to abbreviate Gnome Keyring long API names.
291 static void AppendString(ScopedAttributeList* list,
292 const char* name,
293 const char* value);
294 static void AppendString(ScopedAttributeList* list,
295 const char* name,
296 const std::string& value);
297 static void AppendUint32(ScopedAttributeList* list,
298 const char* name,
299 guint32 value);
301 // All these callbacks are called on UI thread.
302 static void OnOperationDone(GnomeKeyringResult result, gpointer data);
304 static void OnOperationGetList(GnomeKeyringResult result, GList* list,
305 gpointer data);
307 base::WaitableEvent event_;
308 GnomeKeyringResult result_;
309 NativeBackendGnome::PasswordFormList forms_;
310 // If the credential search is specified by a single form and needs to use PSL
311 // matching, then the specifying form is stored in |lookup_form_|. If PSL
312 // matching is used to find a result, then the results signon realm, origin
313 // and action are stored are replaced by those of |lookup_form_|.
314 // Additionally, |lookup_form_->signon_realm| is also used to narrow down the
315 // found logins to those which indeed PSL-match the look-up. And finally,
316 // |lookup_form_| set to NULL means that PSL matching is not required.
317 scoped_ptr<PasswordForm> lookup_form_;
320 void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) {
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
322 time_t date_created = form.date_created.ToTimeT();
323 // If we are asked to save a password with 0 date, use the current time.
324 // We don't want to actually save passwords as though on January 1, 1970.
325 if (!date_created)
326 date_created = time(NULL);
327 int64 date_synced = form.date_synced.ToInternalValue();
328 gnome_keyring_store_password(
329 &kGnomeSchema,
330 NULL, // Default keyring.
331 form.origin.spec().c_str(), // Display name.
332 UTF16ToUTF8(form.password_value).c_str(),
333 OnOperationDone,
334 this, // data
335 NULL, // destroy_data
336 "origin_url", form.origin.spec().c_str(),
337 "action_url", form.action.spec().c_str(),
338 "username_element", UTF16ToUTF8(form.username_element).c_str(),
339 "username_value", UTF16ToUTF8(form.username_value).c_str(),
340 "password_element", UTF16ToUTF8(form.password_element).c_str(),
341 "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
342 "signon_realm", form.signon_realm.c_str(),
343 "ssl_valid", form.ssl_valid,
344 "preferred", form.preferred,
345 "date_created", base::Int64ToString(date_created).c_str(),
346 "blacklisted_by_user", form.blacklisted_by_user,
347 "type", form.type,
348 "times_used", form.times_used,
349 "scheme", form.scheme,
350 "date_synced", base::Int64ToString(date_synced).c_str(),
351 "display_name", UTF16ToUTF8(form.display_name).c_str(),
352 "avatar_url", form.avatar_url.spec().c_str(),
353 "federation_url", form.federation_url.spec().c_str(),
354 "is_zero_click", form.is_zero_click,
355 "application", app_string,
356 NULL);
359 void GKRMethod::AddLoginSearch(const PasswordForm& form,
360 const char* app_string) {
361 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
362 lookup_form_.reset(NULL);
363 // Search GNOME Keyring for matching passwords to update.
364 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
365 AppendString(&attrs, "origin_url", form.origin.spec());
366 AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
367 AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
368 AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
369 AppendString(&attrs, "submit_element", UTF16ToUTF8(form.submit_element));
370 AppendString(&attrs, "signon_realm", form.signon_realm);
371 AppendString(&attrs, "application", app_string);
372 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
373 attrs.get(),
374 OnOperationGetList,
375 /*data=*/this,
376 /*destroy_data=*/NULL);
379 void GKRMethod::UpdateLoginSearch(const PasswordForm& form,
380 const char* app_string) {
381 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
382 lookup_form_.reset(NULL);
383 // Search GNOME Keyring for matching passwords to update.
384 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
385 AppendString(&attrs, "origin_url", form.origin.spec());
386 AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
387 AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
388 AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
389 AppendString(&attrs, "signon_realm", form.signon_realm);
390 AppendString(&attrs, "application", app_string);
391 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
392 attrs.get(),
393 OnOperationGetList,
394 /*data=*/this,
395 /*destroy_data=*/NULL);
398 void GKRMethod::RemoveLogin(const PasswordForm& form, const char* app_string) {
399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
400 // We find forms using the same fields as LoginDatabase::RemoveLogin().
401 gnome_keyring_delete_password(
402 &kGnomeSchema,
403 OnOperationDone,
404 this, // data
405 NULL, // destroy_data
406 "origin_url", form.origin.spec().c_str(),
407 "username_element", UTF16ToUTF8(form.username_element).c_str(),
408 "username_value", UTF16ToUTF8(form.username_value).c_str(),
409 "password_element", UTF16ToUTF8(form.password_element).c_str(),
410 "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
411 "signon_realm", form.signon_realm.c_str(),
412 "application", app_string,
413 NULL);
416 void GKRMethod::GetLogins(const PasswordForm& form, const char* app_string) {
417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
418 lookup_form_.reset(new PasswordForm(form));
419 // Search GNOME Keyring for matching passwords.
420 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
421 if (!password_manager::ShouldPSLDomainMatchingApply(
422 password_manager::GetRegistryControlledDomain(
423 GURL(form.signon_realm)))) {
424 AppendString(&attrs, "signon_realm", form.signon_realm);
426 AppendString(&attrs, "application", app_string);
427 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
428 attrs.get(),
429 OnOperationGetList,
430 /*data=*/this,
431 /*destroy_data=*/NULL);
434 void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user,
435 const char* app_string) {
436 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
437 lookup_form_.reset(NULL);
438 // Search GNOME Keyring for matching passwords.
439 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
440 AppendUint32(&attrs, "blacklisted_by_user", blacklisted_by_user);
441 AppendString(&attrs, "application", app_string);
442 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
443 attrs.get(),
444 OnOperationGetList,
445 /*data=*/this,
446 /*destroy_data=*/NULL);
449 void GKRMethod::GetAllLogins(const char* app_string) {
450 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
451 lookup_form_.reset(NULL);
452 // We need to search for something, otherwise we get no results - so
453 // we search for the fixed application string.
454 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
455 AppendString(&attrs, "application", app_string);
456 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
457 attrs.get(),
458 OnOperationGetList,
459 /*data=*/this,
460 /*destroy_data=*/NULL);
463 GnomeKeyringResult GKRMethod::WaitResult() {
464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
465 event_.Wait();
466 return result_;
469 GnomeKeyringResult GKRMethod::WaitResult(PasswordFormList* forms) {
470 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
471 event_.Wait();
472 if (forms->empty()) {
473 // Normal case. Avoid extra allocation by swapping.
474 forms->swap(forms_);
475 } else {
476 // Rare case. Append forms_ to *forms.
477 forms->insert(forms->end(), forms_.begin(), forms_.end());
478 forms_.clear();
480 return result_;
483 // static
484 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
485 const char* name,
486 const char* value) {
487 gnome_keyring_attribute_list_append_string(list->get(), name, value);
490 // static
491 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
492 const char* name,
493 const std::string& value) {
494 AppendString(list, name, value.c_str());
497 // static
498 void GKRMethod::AppendUint32(GKRMethod::ScopedAttributeList* list,
499 const char* name,
500 guint32 value) {
501 gnome_keyring_attribute_list_append_uint32(list->get(), name, value);
504 // static
505 void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) {
506 GKRMethod* method = static_cast<GKRMethod*>(data);
507 method->result_ = result;
508 method->event_.Signal();
511 // static
512 void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list,
513 gpointer data) {
514 GKRMethod* method = static_cast<GKRMethod*>(data);
515 method->result_ = result;
516 method->forms_.clear();
517 // |list| will be freed after this callback returns, so convert it now.
518 ConvertFormList(list, method->lookup_form_.get(), &method->forms_);
519 method->lookup_form_.reset(NULL);
520 method->event_.Signal();
523 } // namespace
525 NativeBackendGnome::NativeBackendGnome(LocalProfileId id)
526 : profile_id_(id) {
527 app_string_ = GetProfileSpecificAppString();
530 NativeBackendGnome::~NativeBackendGnome() {
533 bool NativeBackendGnome::Init() {
534 return LoadGnomeKeyring() && gnome_keyring_is_available();
537 bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) {
538 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
539 GKRMethod method;
540 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
541 base::Bind(&GKRMethod::AddLogin,
542 base::Unretained(&method),
543 form, app_string_.c_str()));
544 GnomeKeyringResult result = method.WaitResult();
545 if (result != GNOME_KEYRING_RESULT_OK) {
546 LOG(ERROR) << "Keyring save failed: "
547 << gnome_keyring_result_to_message(result);
548 return false;
550 return true;
553 password_manager::PasswordStoreChangeList NativeBackendGnome::AddLogin(
554 const PasswordForm& form) {
555 // Based on LoginDatabase::AddLogin(), we search for an existing match based
556 // on origin_url, username_element, username_value, password_element, submit
557 // element, and signon_realm first, remove that, and then add the new entry.
558 // We'd add the new one first, and then delete the original, but then the
559 // delete might actually delete the newly-added entry!
560 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
561 GKRMethod method;
562 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
563 base::Bind(&GKRMethod::AddLoginSearch,
564 base::Unretained(&method),
565 form, app_string_.c_str()));
566 ScopedVector<autofill::PasswordForm> forms;
567 GnomeKeyringResult result = method.WaitResult(&forms.get());
568 if (result != GNOME_KEYRING_RESULT_OK &&
569 result != GNOME_KEYRING_RESULT_NO_MATCH) {
570 LOG(ERROR) << "Keyring find failed: "
571 << gnome_keyring_result_to_message(result);
572 return password_manager::PasswordStoreChangeList();
574 password_manager::PasswordStoreChangeList changes;
575 if (forms.size() > 0) {
576 if (forms.size() > 1) {
577 LOG(WARNING) << "Adding login when there are " << forms.size()
578 << " matching logins already! Will replace only the first.";
581 if (RemoveLogin(*forms[0])) {
582 changes.push_back(password_manager::PasswordStoreChange(
583 password_manager::PasswordStoreChange::REMOVE, *forms[0]));
586 if (RawAddLogin(form)) {
587 changes.push_back(password_manager::PasswordStoreChange(
588 password_manager::PasswordStoreChange::ADD, form));
590 return changes;
593 bool NativeBackendGnome::UpdateLogin(
594 const PasswordForm& form,
595 password_manager::PasswordStoreChangeList* changes) {
596 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
597 // origin_url, username_element, username_value, password_element, and
598 // signon_realm. We then compare the result to the updated form. If they
599 // differ in any of the mutable fields, then we remove the original, and
600 // then add the new entry. We'd add the new one first, and then delete the
601 // original, but then the delete might actually delete the newly-added entry!
602 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
603 DCHECK(changes);
604 changes->clear();
605 GKRMethod method;
606 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
607 base::Bind(&GKRMethod::UpdateLoginSearch,
608 base::Unretained(&method),
609 form, app_string_.c_str()));
610 ScopedVector<autofill::PasswordForm> forms;
611 GnomeKeyringResult result = method.WaitResult(&forms.get());
612 if (result != GNOME_KEYRING_RESULT_OK) {
613 LOG(ERROR) << "Keyring find failed: "
614 << gnome_keyring_result_to_message(result);
615 return false;
618 bool removed = false;
619 for (size_t i = 0; i < forms.size(); ++i) {
620 if (*forms[i] != form) {
621 RemoveLogin(*forms[i]);
622 removed = true;
625 if (!removed)
626 return true;
628 if (RawAddLogin(form)) {
629 password_manager::PasswordStoreChange change(
630 password_manager::PasswordStoreChange::UPDATE, form);
631 changes->push_back(change);
632 return true;
634 return false;
637 bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
638 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
639 GKRMethod method;
640 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
641 base::Bind(&GKRMethod::RemoveLogin,
642 base::Unretained(&method),
643 form, app_string_.c_str()));
644 GnomeKeyringResult result = method.WaitResult();
645 if (result != GNOME_KEYRING_RESULT_OK) {
646 // Warning, not error, because this can sometimes happen due to the user
647 // racing with the daemon to delete the password a second time.
648 LOG(WARNING) << "Keyring delete failed: "
649 << gnome_keyring_result_to_message(result);
650 return false;
652 return true;
655 bool NativeBackendGnome::RemoveLoginsCreatedBetween(
656 base::Time delete_begin,
657 base::Time delete_end,
658 password_manager::PasswordStoreChangeList* changes) {
659 return RemoveLoginsBetween(
660 delete_begin, delete_end, CREATION_TIMESTAMP, changes);
663 bool NativeBackendGnome::RemoveLoginsSyncedBetween(
664 base::Time delete_begin,
665 base::Time delete_end,
666 password_manager::PasswordStoreChangeList* changes) {
667 return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes);
670 bool NativeBackendGnome::GetLogins(const PasswordForm& form,
671 PasswordFormList* forms) {
672 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
673 GKRMethod method;
674 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
675 base::Bind(&GKRMethod::GetLogins,
676 base::Unretained(&method),
677 form, app_string_.c_str()));
678 GnomeKeyringResult result = method.WaitResult(forms);
679 if (result == GNOME_KEYRING_RESULT_NO_MATCH)
680 return true;
681 if (result != GNOME_KEYRING_RESULT_OK) {
682 LOG(ERROR) << "Keyring find failed: "
683 << gnome_keyring_result_to_message(result);
684 return false;
686 return true;
689 bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
690 return GetLoginsList(forms, true);
693 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
694 return GetLoginsList(forms, false);
697 bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms,
698 bool autofillable) {
699 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
701 uint32_t blacklisted_by_user = !autofillable;
703 GKRMethod method;
704 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
705 base::Bind(&GKRMethod::GetLoginsList,
706 base::Unretained(&method),
707 blacklisted_by_user, app_string_.c_str()));
708 GnomeKeyringResult result = method.WaitResult(forms);
709 if (result == GNOME_KEYRING_RESULT_NO_MATCH)
710 return true;
711 if (result != GNOME_KEYRING_RESULT_OK) {
712 LOG(ERROR) << "Keyring find failed: "
713 << gnome_keyring_result_to_message(result);
714 return false;
716 return true;
719 bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) {
720 GKRMethod method;
721 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
722 base::Bind(&GKRMethod::GetAllLogins,
723 base::Unretained(&method),
724 app_string_.c_str()));
725 GnomeKeyringResult result = method.WaitResult(forms);
726 if (result == GNOME_KEYRING_RESULT_NO_MATCH)
727 return true;
728 if (result != GNOME_KEYRING_RESULT_OK) {
729 LOG(ERROR) << "Keyring find failed: "
730 << gnome_keyring_result_to_message(result);
731 return false;
733 return true;
736 bool NativeBackendGnome::GetLoginsBetween(base::Time get_begin,
737 base::Time get_end,
738 TimestampToCompare date_to_compare,
739 PasswordFormList* forms) {
740 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
741 // We could walk the list and add items as we find them, but it is much
742 // easier to build the list and then filter the results.
743 PasswordFormList all_forms;
744 if (!GetAllLogins(&all_forms))
745 return false;
747 base::Time autofill::PasswordForm::*date_member =
748 date_to_compare == CREATION_TIMESTAMP
749 ? &autofill::PasswordForm::date_created
750 : &autofill::PasswordForm::date_synced;
751 for (size_t i = 0; i < all_forms.size(); ++i) {
752 if (get_begin <= all_forms[i]->*date_member &&
753 (get_end.is_null() || all_forms[i]->*date_member < get_end)) {
754 forms->push_back(all_forms[i]);
755 } else {
756 delete all_forms[i];
760 return true;
763 bool NativeBackendGnome::RemoveLoginsBetween(
764 base::Time get_begin,
765 base::Time get_end,
766 TimestampToCompare date_to_compare,
767 password_manager::PasswordStoreChangeList* changes) {
768 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
769 DCHECK(changes);
770 changes->clear();
771 // We could walk the list and delete items as we find them, but it is much
772 // easier to build the list and use RemoveLogin() to delete them.
773 ScopedVector<autofill::PasswordForm> forms;
774 if (!GetLoginsBetween(get_begin, get_end, date_to_compare, &forms.get()))
775 return false;
777 bool ok = true;
778 for (size_t i = 0; i < forms.size(); ++i) {
779 if (RemoveLogin(*forms[i])) {
780 changes->push_back(password_manager::PasswordStoreChange(
781 password_manager::PasswordStoreChange::REMOVE, *forms[i]));
782 } else {
783 ok = false;
786 return ok;
789 std::string NativeBackendGnome::GetProfileSpecificAppString() const {
790 // Originally, the application string was always just "chrome" and used only
791 // so that we had *something* to search for since GNOME Keyring won't search
792 // for nothing. Now we use it to distinguish passwords for different profiles.
793 return base::StringPrintf("%s-%d", kGnomeKeyringAppString, profile_id_);