Upstream version 8.37.180.0
[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   int64 date_synced = 0;
141   base::StringToInt64(string_attr_map["date_synced"], &date_synced);
142   form->date_synced = base::Time::FromInternalValue(date_synced);
143
144   return form.Pass();
145 }
146
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;
165
166     scoped_ptr<PasswordForm> form(FormFromAttributes(attrs));
167     if (form) {
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))) {
174           continue;
175         }
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;
181       }
182       if (data->secret) {
183         form->password_value = UTF8ToUTF16(data->secret);
184       } else {
185         LOG(WARNING) << "Unable to access password from list element!";
186       }
187       forms->push_back(form.release());
188     } else {
189       LOG(WARNING) << "Could not initialize PasswordForm from attributes!";
190     }
191   }
192   if (lookup_form) {
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);
199   }
200 }
201
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 },
227     { NULL }
228   }
229 };
230
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.)
241
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 {
248  public:
249   typedef NativeBackendGnome::PasswordFormList PasswordFormList;
250
251   GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {}
252
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);
262
263   // Use after AddLogin, RemoveLogin.
264   GnomeKeyringResult WaitResult();
265
266   // Use after AddLoginSearch, UpdateLoginSearch, GetLogins, GetLoginsList,
267   // GetAllLogins.
268   GnomeKeyringResult WaitResult(PasswordFormList* forms);
269
270  private:
271   struct GnomeKeyringAttributeListFreeDeleter {
272     inline void operator()(void* list) const {
273       gnome_keyring_attribute_list_free(
274           static_cast<GnomeKeyringAttributeList*>(list));
275     }
276   };
277
278   typedef scoped_ptr<GnomeKeyringAttributeList,
279                      GnomeKeyringAttributeListFreeDeleter> ScopedAttributeList;
280
281   // Helper methods to abbreviate Gnome Keyring long API names.
282   static void AppendString(ScopedAttributeList* list,
283                            const char* name,
284                            const char* value);
285   static void AppendString(ScopedAttributeList* list,
286                            const char* name,
287                            const std::string& value);
288   static void AppendUint32(ScopedAttributeList* list,
289                            const char* name,
290                            guint32 value);
291
292   // All these callbacks are called on UI thread.
293   static void OnOperationDone(GnomeKeyringResult result, gpointer data);
294
295   static void OnOperationGetList(GnomeKeyringResult result, GList* list,
296                                  gpointer data);
297
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_;
310 };
311
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.
317   if (!date_created)
318     date_created = time(NULL);
319   int64 date_synced = form.date_synced.ToInternalValue();
320   gnome_keyring_store_password(
321       &kGnomeSchema,
322       NULL,  // Default keyring.
323       form.origin.spec().c_str(),  // Display name.
324       UTF16ToUTF8(form.password_value).c_str(),
325       OnOperationDone,
326       this,  // data
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,
339       "type", form.type,
340       "times_used", form.times_used,
341       "scheme", form.scheme,
342       "date_synced", base::Int64ToString(date_synced).c_str(),
343       "application", app_string,
344       NULL);
345 }
346
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,
361                            attrs.get(),
362                            OnOperationGetList,
363                            /*data=*/this,
364                            /*destroy_data=*/NULL);
365 }
366
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,
380                            attrs.get(),
381                            OnOperationGetList,
382                            /*data=*/this,
383                            /*destroy_data=*/NULL);
384 }
385
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(
390       &kGnomeSchema,
391       OnOperationDone,
392       this,  // data
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,
402       NULL);
403 }
404
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);
414   }
415   AppendString(&attrs, "application", app_string);
416   gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
417                            attrs.get(),
418                            OnOperationGetList,
419                            /*data=*/this,
420                            /*destroy_data=*/NULL);
421 }
422
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,
432                            attrs.get(),
433                            OnOperationGetList,
434                            /*data=*/this,
435                            /*destroy_data=*/NULL);
436 }
437
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,
446                            attrs.get(),
447                            OnOperationGetList,
448                            /*data=*/this,
449                            /*destroy_data=*/NULL);
450 }
451
452 GnomeKeyringResult GKRMethod::WaitResult() {
453   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
454   event_.Wait();
455   return result_;
456 }
457
458 GnomeKeyringResult GKRMethod::WaitResult(PasswordFormList* forms) {
459   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
460   event_.Wait();
461   if (forms->empty()) {
462     // Normal case. Avoid extra allocation by swapping.
463     forms->swap(forms_);
464   } else {
465     // Rare case. Append forms_ to *forms.
466     forms->insert(forms->end(), forms_.begin(), forms_.end());
467     forms_.clear();
468   }
469   return result_;
470 }
471
472 // static
473 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
474                              const char* name,
475                              const char* value) {
476   gnome_keyring_attribute_list_append_string(list->get(), name, value);
477 }
478
479 // static
480 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
481                              const char* name,
482                              const std::string& value) {
483   AppendString(list, name, value.c_str());
484 }
485
486 // static
487 void GKRMethod::AppendUint32(GKRMethod::ScopedAttributeList* list,
488                              const char* name,
489                              guint32 value) {
490   gnome_keyring_attribute_list_append_uint32(list->get(), name, value);
491 }
492
493 // static
494 void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) {
495   GKRMethod* method = static_cast<GKRMethod*>(data);
496   method->result_ = result;
497   method->event_.Signal();
498 }
499
500 // static
501 void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list,
502                                    gpointer data) {
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.
507   ConvertFormList(
508       list, method->lookup_form_.get(), method->helper_, &method->forms_);
509   method->lookup_form_.reset(NULL);
510   method->event_.Signal();
511 }
512
513 }  // namespace
514
515 NativeBackendGnome::NativeBackendGnome(LocalProfileId id)
516     : profile_id_(id) {
517   app_string_ = GetProfileSpecificAppString();
518 }
519
520 NativeBackendGnome::~NativeBackendGnome() {
521 }
522
523 bool NativeBackendGnome::Init() {
524   return LoadGnomeKeyring() && gnome_keyring_is_available();
525 }
526
527 bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) {
528   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
529   GKRMethod method;
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);
538     return false;
539   }
540   return true;
541 }
542
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));
551   GKRMethod method;
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();
563   }
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.";
569     }
570
571     if (RemoveLogin(*forms[0])) {
572       changes.push_back(password_manager::PasswordStoreChange(
573           password_manager::PasswordStoreChange::REMOVE, *forms[0]));
574     }
575   }
576   if (RawAddLogin(form)) {
577     changes.push_back(password_manager::PasswordStoreChange(
578         password_manager::PasswordStoreChange::ADD, form));
579   }
580   return changes;
581 }
582
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));
593   DCHECK(changes);
594   changes->clear();
595   GKRMethod method;
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);
605     return false;
606   }
607
608   bool removed = false;
609   for (size_t i = 0; i < forms.size(); ++i) {
610     if (*forms[i] != form) {
611       RemoveLogin(*forms[i]);
612       removed = true;
613     }
614   }
615   if (!removed)
616     return true;
617
618   if (RawAddLogin(form)) {
619     password_manager::PasswordStoreChange change(
620         password_manager::PasswordStoreChange::UPDATE, form);
621     changes->push_back(change);
622     return true;
623   }
624   return false;
625 }
626
627 bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
628   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
629   GKRMethod method;
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);
640     return false;
641   }
642   return true;
643 }
644
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);
651 }
652
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);
658 }
659
660 bool NativeBackendGnome::GetLogins(const PasswordForm& form,
661                                    PasswordFormList* forms) {
662   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
663   GKRMethod method;
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)
670     return true;
671   if (result != GNOME_KEYRING_RESULT_OK) {
672     LOG(ERROR) << "Keyring find failed: "
673                << gnome_keyring_result_to_message(result);
674     return false;
675   }
676   return true;
677 }
678
679 bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
680   return GetLoginsList(forms, true);
681 }
682
683 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
684   return GetLoginsList(forms, false);
685 }
686
687 bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms,
688                                        bool autofillable) {
689   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
690
691   uint32_t blacklisted_by_user = !autofillable;
692
693   GKRMethod method;
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)
700     return true;
701   if (result != GNOME_KEYRING_RESULT_OK) {
702     LOG(ERROR) << "Keyring find failed: "
703                << gnome_keyring_result_to_message(result);
704     return false;
705   }
706   return true;
707 }
708
709 bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) {
710   GKRMethod method;
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)
717     return true;
718   if (result != GNOME_KEYRING_RESULT_OK) {
719     LOG(ERROR) << "Keyring find failed: "
720                << gnome_keyring_result_to_message(result);
721     return false;
722   }
723   return true;
724 }
725
726 bool NativeBackendGnome::GetLoginsBetween(base::Time get_begin,
727                                           base::Time get_end,
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))
735     return false;
736
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]);
745     } else {
746       delete all_forms[i];
747     }
748   }
749
750   return true;
751 }
752
753 bool NativeBackendGnome::RemoveLoginsBetween(
754     base::Time get_begin,
755     base::Time get_end,
756     TimestampToCompare date_to_compare,
757     password_manager::PasswordStoreChangeList* changes) {
758   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
759   DCHECK(changes);
760   changes->clear();
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()))
765     return false;
766
767   bool ok = true;
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]));
772     } else {
773       ok = false;
774     }
775   }
776   return ok;
777 }
778
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_);
784 }