02339183aba8699ff6d4a6981a4973b965b75cbc
[platform/framework/web/crosswalk.git] / src / chrome / browser / password_manager / native_backend_gnome_x.cc
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.
4
5 #include "chrome/browser/password_manager/native_backend_gnome_x.h"
6
7 #include <dlfcn.h>
8 #include <gnome-keyring.h>
9
10 #include <map>
11 #include <string>
12 #include <vector>
13
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"
28
29 using autofill::PasswordForm;
30 using base::UTF8ToUTF16;
31 using base::UTF16ToUTF8;
32 using content::BrowserThread;
33 using password_manager::PSLMatchingHelper;
34
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
39
40 bool GnomeKeyringLoader::keyring_loaded = false;
41
42 #if defined(DLOPEN_GNOME_KEYRING)
43
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)
48   {NULL, NULL}
49 };
50 #undef GNOME_KEYRING_FUNCTION_INFO
51
52 /* Load the library and initialize the function pointers. */
53 bool GnomeKeyringLoader::LoadGnomeKeyring() {
54   if (keyring_loaded)
55     return true;
56
57   void* handle = dlopen("libgnome-keyring.so.0", RTLD_NOW | RTLD_GLOBAL);
58   if (!handle) {
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();
63     return false;
64   }
65
66   for (size_t i = 0; functions[i].name; ++i) {
67     dlerror();
68     *functions[i].pointer = dlsym(handle, functions[i].name);
69     const char* error = dlerror();
70     if (error) {
71       LOG(ERROR) << "Unable to load symbol "
72                  << functions[i].name << ": " << error;
73       dlclose(handle);
74       return false;
75     }
76   }
77
78   keyring_loaded = true;
79   // We leak the library handle. That's OK: this function is called only once.
80   return true;
81 }
82
83 #else  // defined(DLOPEN_GNOME_KEYRING)
84
85 bool GnomeKeyringLoader::LoadGnomeKeyring() {
86   if (keyring_loaded)
87     return true;
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;
93   return true;
94 }
95
96 #endif  // defined(DLOPEN_GNOME_KEYRING)
97
98 namespace {
99
100 const char kGnomeKeyringAppString[] = "chrome";
101
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;
115   }
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>();
120
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"],
133                                      &date_created);
134   DCHECK(date_ok);
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
141   return form.Pass();
142 }
143
144 // Parse all the results from the given GList into a PasswordFormList, and free
145 // the GList. PasswordForms are allocated on the heap, and should be deleted by
146 // the consumer. If not NULL, |lookup_form| is used to filter out results --
147 // only credentials with signon realms passing the PSL matching (done by
148 // |helper|) against |lookup_form->signon_realm| will be kept. PSL matched
149 // results get their signon_realm, origin, and action rewritten to those of
150 // |lookup_form_|, with the original signon_realm saved into the result's
151 // original_signon_realm data member.
152 void ConvertFormList(GList* found,
153                      const PasswordForm* lookup_form,
154                      const PSLMatchingHelper& helper,
155                      NativeBackendGnome::PasswordFormList* forms) {
156   PSLMatchingHelper::PSLDomainMatchMetric psl_domain_match_metric =
157       PSLMatchingHelper::PSL_DOMAIN_MATCH_NONE;
158   for (GList* element = g_list_first(found); element != NULL;
159        element = g_list_next(element)) {
160     GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
161     GnomeKeyringAttributeList* attrs = data->attributes;
162
163     scoped_ptr<PasswordForm> form(FormFromAttributes(attrs));
164     if (form) {
165       if (lookup_form && form->signon_realm != lookup_form->signon_realm) {
166         // This is not an exact match, we try PSL matching.
167         if (!(PSLMatchingHelper::IsPublicSuffixDomainMatch(
168                 lookup_form->signon_realm, form->signon_realm))) {
169           continue;
170         }
171         psl_domain_match_metric = PSLMatchingHelper::PSL_DOMAIN_MATCH_FOUND;
172         form->original_signon_realm = form->signon_realm;
173         form->signon_realm = lookup_form->signon_realm;
174         form->origin = lookup_form->origin;
175         form->action = lookup_form->action;
176       }
177       if (data->secret) {
178         form->password_value = UTF8ToUTF16(data->secret);
179       } else {
180         LOG(WARNING) << "Unable to access password from list element!";
181       }
182       forms->push_back(form.release());
183     } else {
184       LOG(WARNING) << "Could not initialize PasswordForm from attributes!";
185     }
186   }
187   if (lookup_form) {
188     UMA_HISTOGRAM_ENUMERATION(
189         "PasswordManager.PslDomainMatchTriggering",
190         helper.IsMatchingEnabled()
191             ? psl_domain_match_metric
192             : PSLMatchingHelper::PSL_DOMAIN_MATCH_DISABLED,
193         PSLMatchingHelper::PSL_DOMAIN_MATCH_COUNT);
194   }
195 }
196
197 // Schema is analagous to the fields in PasswordForm.
198 // TODO(gcasto): Adding 'form_data' would be nice, but we would need to
199 // serialize in a way that is guaranteed to not have any embedded NULLs. Pickle
200 // doesn't make this guarantee, so we just don't serialize this field. Since
201 // it's only used to crowd source data collection it doesn't matter that much
202 // if it's not available on this platform.
203 const GnomeKeyringPasswordSchema kGnomeSchema = {
204   GNOME_KEYRING_ITEM_GENERIC_SECRET, {
205     { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
206     { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
207     { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
208     { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
209     { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
210     { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
211     { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
212     { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
213     { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
214     { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
215     { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
216     { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
217     { "type", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
218     { "times_used", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
219     // This field is always "chrome" so that we can search for it.
220     { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
221     { NULL }
222   }
223 };
224
225 // Sadly, PasswordStore goes to great lengths to switch from the originally
226 // calling thread to the DB thread, and to provide an asynchronous API to
227 // callers while using a synchronous (virtual) API provided by subclasses like
228 // PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main
229 // thread, which is the UI thread to us. So we end up having to switch threads
230 // again, possibly back to the very same thread (in case the UI thread is the
231 // caller, e.g. in the password management UI), and *block* the DB thread
232 // waiting for a response from the UI thread to provide the synchronous API
233 // PasswordStore expects of us. (It will then in turn switch back to the
234 // original caller to send the asynchronous reply to the original request.)
235
236 // This class represents a call to a GNOME Keyring method. A RunnableMethod
237 // should be posted to the UI thread to call one of its action methods, and then
238 // a WaitResult() method should be called to wait for the result. Each instance
239 // supports only one outstanding method at a time, though multiple instances may
240 // be used in parallel.
241 class GKRMethod : public GnomeKeyringLoader {
242  public:
243   typedef NativeBackendGnome::PasswordFormList PasswordFormList;
244
245   GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {}
246
247   // Action methods. These call gnome_keyring_* functions. Call from UI thread.
248   // See GetProfileSpecificAppString() for more information on the app string.
249   void AddLogin(const PasswordForm& form, const char* app_string);
250   void AddLoginSearch(const PasswordForm& form, const char* app_string);
251   void UpdateLoginSearch(const PasswordForm& form, const char* app_string);
252   void RemoveLogin(const PasswordForm& form, const char* app_string);
253   void GetLogins(const PasswordForm& form, const char* app_string);
254   void GetLoginsList(uint32_t blacklisted_by_user, const char* app_string);
255   void GetAllLogins(const char* app_string);
256
257   // Use after AddLogin, RemoveLogin.
258   GnomeKeyringResult WaitResult();
259
260   // Use after AddLoginSearch, UpdateLoginSearch, GetLogins, GetLoginsList,
261   // GetAllLogins.
262   GnomeKeyringResult WaitResult(PasswordFormList* forms);
263
264  private:
265   struct GnomeKeyringAttributeListFreeDeleter {
266     inline void operator()(void* list) const {
267       gnome_keyring_attribute_list_free(
268           static_cast<GnomeKeyringAttributeList*>(list));
269     }
270   };
271
272   typedef scoped_ptr<GnomeKeyringAttributeList,
273                      GnomeKeyringAttributeListFreeDeleter> ScopedAttributeList;
274
275   // Helper methods to abbreviate Gnome Keyring long API names.
276   static void AppendString(ScopedAttributeList* list,
277                            const char* name,
278                            const char* value);
279   static void AppendString(ScopedAttributeList* list,
280                            const char* name,
281                            const std::string& value);
282   static void AppendUint32(ScopedAttributeList* list,
283                            const char* name,
284                            guint32 value);
285
286   // All these callbacks are called on UI thread.
287   static void OnOperationDone(GnomeKeyringResult result, gpointer data);
288
289   static void OnOperationGetList(GnomeKeyringResult result, GList* list,
290                                  gpointer data);
291
292   base::WaitableEvent event_;
293   GnomeKeyringResult result_;
294   NativeBackendGnome::PasswordFormList forms_;
295   // If the credential search is specified by a single form and needs to use PSL
296   // matching, then the specifying form is stored in |lookup_form_|. If PSL
297   // matching is used to find a result, then the results signon realm, origin
298   // and action are stored are replaced by those of |lookup_form_|.
299   // Additionally, |lookup_form_->signon_realm| is also used to narrow down the
300   // found logins to those which indeed PSL-match the look-up. And finally,
301   // |lookup_form_| set to NULL means that PSL matching is not required.
302   scoped_ptr<PasswordForm> lookup_form_;
303   const PSLMatchingHelper helper_;
304 };
305
306 void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) {
307   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
308   time_t date_created = form.date_created.ToTimeT();
309   // If we are asked to save a password with 0 date, use the current time.
310   // We don't want to actually save passwords as though on January 1, 1970.
311   if (!date_created)
312     date_created = time(NULL);
313   gnome_keyring_store_password(
314       &kGnomeSchema,
315       NULL,  // Default keyring.
316       form.origin.spec().c_str(),  // Display name.
317       UTF16ToUTF8(form.password_value).c_str(),
318       OnOperationDone,
319       this,  // data
320       NULL,  // destroy_data
321       "origin_url", form.origin.spec().c_str(),
322       "action_url", form.action.spec().c_str(),
323       "username_element", UTF16ToUTF8(form.username_element).c_str(),
324       "username_value", UTF16ToUTF8(form.username_value).c_str(),
325       "password_element", UTF16ToUTF8(form.password_element).c_str(),
326       "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
327       "signon_realm", form.signon_realm.c_str(),
328       "ssl_valid", form.ssl_valid,
329       "preferred", form.preferred,
330       "date_created", base::Int64ToString(date_created).c_str(),
331       "blacklisted_by_user", form.blacklisted_by_user,
332       "type", form.type,
333       "times_used", form.times_used,
334       "scheme", form.scheme,
335       "application", app_string,
336       NULL);
337 }
338
339 void GKRMethod::AddLoginSearch(const PasswordForm& form,
340                                const char* app_string) {
341   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
342   lookup_form_.reset(NULL);
343   // Search GNOME Keyring for matching passwords to update.
344   ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
345   AppendString(&attrs, "origin_url", form.origin.spec());
346   AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
347   AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
348   AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
349   AppendString(&attrs, "submit_element", UTF16ToUTF8(form.submit_element));
350   AppendString(&attrs, "signon_realm", form.signon_realm);
351   AppendString(&attrs, "application", app_string);
352   gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
353                            attrs.get(),
354                            OnOperationGetList,
355                            /*data=*/this,
356                            /*destroy_data=*/NULL);
357 }
358
359 void GKRMethod::UpdateLoginSearch(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, "signon_realm", form.signon_realm);
370   AppendString(&attrs, "application", app_string);
371   gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
372                            attrs.get(),
373                            OnOperationGetList,
374                            /*data=*/this,
375                            /*destroy_data=*/NULL);
376 }
377
378 void GKRMethod::RemoveLogin(const PasswordForm& form, const char* app_string) {
379   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
380   // We find forms using the same fields as LoginDatabase::RemoveLogin().
381   gnome_keyring_delete_password(
382       &kGnomeSchema,
383       OnOperationDone,
384       this,  // data
385       NULL,  // destroy_data
386       "origin_url", form.origin.spec().c_str(),
387       "action_url", form.action.spec().c_str(),
388       "username_element", UTF16ToUTF8(form.username_element).c_str(),
389       "username_value", UTF16ToUTF8(form.username_value).c_str(),
390       "password_element", UTF16ToUTF8(form.password_element).c_str(),
391       "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
392       "signon_realm", form.signon_realm.c_str(),
393       "application", app_string,
394       NULL);
395 }
396
397 void GKRMethod::GetLogins(const PasswordForm& form, const char* app_string) {
398   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
399   lookup_form_.reset(new PasswordForm(form));
400   // Search GNOME Keyring for matching passwords.
401   ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
402   if (!helper_.ShouldPSLDomainMatchingApply(
403            PSLMatchingHelper::GetRegistryControlledDomain(
404                GURL(form.signon_realm)))) {
405     AppendString(&attrs, "signon_realm", form.signon_realm);
406   }
407   AppendString(&attrs, "application", app_string);
408   gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
409                            attrs.get(),
410                            OnOperationGetList,
411                            /*data=*/this,
412                            /*destroy_data=*/NULL);
413 }
414
415 void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user,
416                               const char* app_string) {
417   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
418   lookup_form_.reset(NULL);
419   // Search GNOME Keyring for matching passwords.
420   ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
421   AppendUint32(&attrs, "blacklisted_by_user", blacklisted_by_user);
422   AppendString(&attrs, "application", app_string);
423   gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
424                            attrs.get(),
425                            OnOperationGetList,
426                            /*data=*/this,
427                            /*destroy_data=*/NULL);
428 }
429
430 void GKRMethod::GetAllLogins(const char* app_string) {
431   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
432   lookup_form_.reset(NULL);
433   // We need to search for something, otherwise we get no results - so
434   // we search for the fixed application string.
435   ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
436   AppendString(&attrs, "application", app_string);
437   gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
438                            attrs.get(),
439                            OnOperationGetList,
440                            /*data=*/this,
441                            /*destroy_data=*/NULL);
442 }
443
444 GnomeKeyringResult GKRMethod::WaitResult() {
445   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
446   event_.Wait();
447   return result_;
448 }
449
450 GnomeKeyringResult GKRMethod::WaitResult(PasswordFormList* forms) {
451   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
452   event_.Wait();
453   if (forms->empty()) {
454     // Normal case. Avoid extra allocation by swapping.
455     forms->swap(forms_);
456   } else {
457     // Rare case. Append forms_ to *forms.
458     forms->insert(forms->end(), forms_.begin(), forms_.end());
459     forms_.clear();
460   }
461   return result_;
462 }
463
464 // static
465 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
466                              const char* name,
467                              const char* value) {
468   gnome_keyring_attribute_list_append_string(list->get(), name, value);
469 }
470
471 // static
472 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
473                              const char* name,
474                              const std::string& value) {
475   AppendString(list, name, value.c_str());
476 }
477
478 // static
479 void GKRMethod::AppendUint32(GKRMethod::ScopedAttributeList* list,
480                              const char* name,
481                              guint32 value) {
482   gnome_keyring_attribute_list_append_uint32(list->get(), name, value);
483 }
484
485 // static
486 void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) {
487   GKRMethod* method = static_cast<GKRMethod*>(data);
488   method->result_ = result;
489   method->event_.Signal();
490 }
491
492 // static
493 void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list,
494                                    gpointer data) {
495   GKRMethod* method = static_cast<GKRMethod*>(data);
496   method->result_ = result;
497   method->forms_.clear();
498   // |list| will be freed after this callback returns, so convert it now.
499   ConvertFormList(
500       list, method->lookup_form_.get(), method->helper_, &method->forms_);
501   method->lookup_form_.reset(NULL);
502   method->event_.Signal();
503 }
504
505 }  // namespace
506
507 NativeBackendGnome::NativeBackendGnome(LocalProfileId id)
508     : profile_id_(id) {
509   app_string_ = GetProfileSpecificAppString();
510 }
511
512 NativeBackendGnome::~NativeBackendGnome() {
513 }
514
515 bool NativeBackendGnome::Init() {
516   return LoadGnomeKeyring() && gnome_keyring_is_available();
517 }
518
519 bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) {
520   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
521   GKRMethod method;
522   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
523                           base::Bind(&GKRMethod::AddLogin,
524                                      base::Unretained(&method),
525                                      form, app_string_.c_str()));
526   GnomeKeyringResult result = method.WaitResult();
527   if (result != GNOME_KEYRING_RESULT_OK) {
528     LOG(ERROR) << "Keyring save failed: "
529                << gnome_keyring_result_to_message(result);
530     return false;
531   }
532   return true;
533 }
534
535 bool NativeBackendGnome::AddLogin(const PasswordForm& form) {
536   // Based on LoginDatabase::AddLogin(), we search for an existing match based
537   // on origin_url, username_element, username_value, password_element, submit
538   // element, and signon_realm first, remove that, and then add the new entry.
539   // We'd add the new one first, and then delete the original, but then the
540   // delete might actually delete the newly-added entry!
541   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
542   GKRMethod method;
543   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
544                           base::Bind(&GKRMethod::AddLoginSearch,
545                                      base::Unretained(&method),
546                                      form, app_string_.c_str()));
547   PasswordFormList forms;
548   GnomeKeyringResult result = method.WaitResult(&forms);
549   if (result != GNOME_KEYRING_RESULT_OK &&
550       result != GNOME_KEYRING_RESULT_NO_MATCH) {
551     LOG(ERROR) << "Keyring find failed: "
552                << gnome_keyring_result_to_message(result);
553     return false;
554   }
555   if (forms.size() > 0) {
556     if (forms.size() > 1) {
557       LOG(WARNING) << "Adding login when there are " << forms.size()
558                    << " matching logins already! Will replace only the first.";
559     }
560
561     RemoveLogin(*forms[0]);
562     for (size_t i = 0; i < forms.size(); ++i)
563       delete forms[i];
564   }
565   return RawAddLogin(form);
566 }
567
568 bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) {
569   // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
570   // origin_url, username_element, username_value, password_element, and
571   // signon_realm. We then compare the result to the updated form. If they
572   // differ in any of the mutable fields, then we remove the original, and
573   // then add the new entry. We'd add the new one first, and then delete the
574   // original, but then the delete might actually delete the newly-added entry!
575   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
576   GKRMethod method;
577   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
578                           base::Bind(&GKRMethod::UpdateLoginSearch,
579                                      base::Unretained(&method),
580                                      form, app_string_.c_str()));
581   PasswordFormList forms;
582   GnomeKeyringResult result = method.WaitResult(&forms);
583   if (result != GNOME_KEYRING_RESULT_OK) {
584     LOG(ERROR) << "Keyring find failed: "
585                << gnome_keyring_result_to_message(result);
586     return false;
587   }
588
589   bool ok = true;
590   for (size_t i = 0; i < forms.size(); ++i) {
591     if (forms[i]->action != form.action ||
592         forms[i]->password_value != form.password_value ||
593         forms[i]->ssl_valid != form.ssl_valid ||
594         forms[i]->preferred != form.preferred ||
595         forms[i]->times_used != form.times_used) {
596       RemoveLogin(*forms[i]);
597
598       forms[i]->action = form.action;
599       forms[i]->password_value = form.password_value;
600       forms[i]->ssl_valid = form.ssl_valid;
601       forms[i]->preferred = form.preferred;
602       forms[i]->times_used = form.times_used;
603       if (!RawAddLogin(*forms[i]))
604         ok = false;
605     }
606     delete forms[i];
607   }
608   return ok;
609 }
610
611 bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
612   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
613   GKRMethod method;
614   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
615                           base::Bind(&GKRMethod::RemoveLogin,
616                                      base::Unretained(&method),
617                                      form, app_string_.c_str()));
618   GnomeKeyringResult result = method.WaitResult();
619   if (result != GNOME_KEYRING_RESULT_OK) {
620     // Warning, not error, because this can sometimes happen due to the user
621     // racing with the daemon to delete the password a second time.
622     LOG(WARNING) << "Keyring delete failed: "
623                  << gnome_keyring_result_to_message(result);
624     return false;
625   }
626   return true;
627 }
628
629 bool NativeBackendGnome::RemoveLoginsCreatedBetween(
630     const base::Time& delete_begin,
631     const base::Time& delete_end) {
632   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
633   bool ok = true;
634   // We could walk the list and delete items as we find them, but it is much
635   // easier to build the list and use RemoveLogin() to delete them.
636   PasswordFormList forms;
637   if (!GetAllLogins(&forms))
638     return false;
639
640   for (size_t i = 0; i < forms.size(); ++i) {
641     if (delete_begin <= forms[i]->date_created &&
642         (delete_end.is_null() || forms[i]->date_created < delete_end)) {
643       if (!RemoveLogin(*forms[i]))
644         ok = false;
645     }
646     delete forms[i];
647   }
648   return ok;
649 }
650
651 bool NativeBackendGnome::GetLogins(const PasswordForm& form,
652                                    PasswordFormList* forms) {
653   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
654   GKRMethod method;
655   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
656                           base::Bind(&GKRMethod::GetLogins,
657                                      base::Unretained(&method),
658                                      form, app_string_.c_str()));
659   GnomeKeyringResult result = method.WaitResult(forms);
660   if (result == GNOME_KEYRING_RESULT_NO_MATCH)
661     return true;
662   if (result != GNOME_KEYRING_RESULT_OK) {
663     LOG(ERROR) << "Keyring find failed: "
664                << gnome_keyring_result_to_message(result);
665     return false;
666   }
667   return true;
668 }
669
670 bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin,
671                                                  const base::Time& get_end,
672                                                  PasswordFormList* forms) {
673   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
674   // We could walk the list and add items as we find them, but it is much
675   // easier to build the list and then filter the results.
676   PasswordFormList all_forms;
677   if (!GetAllLogins(&all_forms))
678     return false;
679
680   forms->reserve(forms->size() + all_forms.size());
681   for (size_t i = 0; i < all_forms.size(); ++i) {
682     if (get_begin <= all_forms[i]->date_created &&
683         (get_end.is_null() || all_forms[i]->date_created < get_end)) {
684       forms->push_back(all_forms[i]);
685     } else {
686       delete all_forms[i];
687     }
688   }
689
690   return true;
691 }
692
693 bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
694   return GetLoginsList(forms, true);
695 }
696
697 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
698   return GetLoginsList(forms, false);
699 }
700
701 bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms,
702                                        bool autofillable) {
703   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
704
705   uint32_t blacklisted_by_user = !autofillable;
706
707   GKRMethod method;
708   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
709                           base::Bind(&GKRMethod::GetLoginsList,
710                                      base::Unretained(&method),
711                                      blacklisted_by_user, app_string_.c_str()));
712   GnomeKeyringResult result = method.WaitResult(forms);
713   if (result == GNOME_KEYRING_RESULT_NO_MATCH)
714     return true;
715   if (result != GNOME_KEYRING_RESULT_OK) {
716     LOG(ERROR) << "Keyring find failed: "
717                << gnome_keyring_result_to_message(result);
718     return false;
719   }
720   return true;
721 }
722
723 bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) {
724   GKRMethod method;
725   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
726                           base::Bind(&GKRMethod::GetAllLogins,
727                                      base::Unretained(&method),
728                                      app_string_.c_str()));
729   GnomeKeyringResult result = method.WaitResult(forms);
730   if (result == GNOME_KEYRING_RESULT_NO_MATCH)
731     return true;
732   if (result != GNOME_KEYRING_RESULT_OK) {
733     LOG(ERROR) << "Keyring find failed: "
734                << gnome_keyring_result_to_message(result);
735     return false;
736   }
737   return true;
738 }
739
740 std::string NativeBackendGnome::GetProfileSpecificAppString() const {
741   // Originally, the application string was always just "chrome" and used only
742   // so that we had *something* to search for since GNOME Keyring won't search
743   // for nothing. Now we use it to distinguish passwords for different profiles.
744   return base::StringPrintf("%s-%d", kGnomeKeyringAppString, profile_id_);
745 }