- add sources.
[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/logging.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_piece.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/synchronization/waitable_event.h"
21 #include "base/time/time.h"
22 #include "content/public/browser/browser_thread.h"
23
24 using autofill::PasswordForm;
25 using content::BrowserThread;
26
27 #define GNOME_KEYRING_DEFINE_POINTER(name) \
28   typeof(&::gnome_keyring_##name) GnomeKeyringLoader::gnome_keyring_##name;
29 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_POINTER)
30 #undef GNOME_KEYRING_DEFINE_POINTER
31
32 bool GnomeKeyringLoader::keyring_loaded = false;
33
34 #if defined(DLOPEN_GNOME_KEYRING)
35
36 #define GNOME_KEYRING_FUNCTION_INFO(name) \
37   {"gnome_keyring_"#name, reinterpret_cast<void**>(&gnome_keyring_##name)},
38 const GnomeKeyringLoader::FunctionInfo GnomeKeyringLoader::functions[] = {
39   GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION_INFO)
40   {NULL, NULL}
41 };
42 #undef GNOME_KEYRING_FUNCTION_INFO
43
44 /* Load the library and initialize the function pointers. */
45 bool GnomeKeyringLoader::LoadGnomeKeyring() {
46   if (keyring_loaded)
47     return true;
48
49   void* handle = dlopen("libgnome-keyring.so.0", RTLD_NOW | RTLD_GLOBAL);
50   if (!handle) {
51     // We wanted to use GNOME Keyring, but we couldn't load it. Warn, because
52     // either the user asked for this, or we autodetected it incorrectly. (Or
53     // the system has broken libraries, which is also good to warn about.)
54     LOG(WARNING) << "Could not load libgnome-keyring.so.0: " << dlerror();
55     return false;
56   }
57
58   for (size_t i = 0; functions[i].name; ++i) {
59     dlerror();
60     *functions[i].pointer = dlsym(handle, functions[i].name);
61     const char* error = dlerror();
62     if (error) {
63       LOG(ERROR) << "Unable to load symbol "
64                  << functions[i].name << ": " << error;
65       dlclose(handle);
66       return false;
67     }
68   }
69
70   keyring_loaded = true;
71   // We leak the library handle. That's OK: this function is called only once.
72   return true;
73 }
74
75 #else  // defined(DLOPEN_GNOME_KEYRING)
76
77 bool GnomeKeyringLoader::LoadGnomeKeyring() {
78   if (keyring_loaded)
79     return true;
80 #define GNOME_KEYRING_ASSIGN_POINTER(name) \
81   gnome_keyring_##name = &::gnome_keyring_##name;
82   GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_ASSIGN_POINTER)
83 #undef GNOME_KEYRING_ASSIGN_POINTER
84   keyring_loaded = true;
85   return true;
86 }
87
88 #endif  // defined(DLOPEN_GNOME_KEYRING)
89
90 namespace {
91
92 const char kGnomeKeyringAppString[] = "chrome";
93
94 // Convert the attributes of a given keyring entry into a new PasswordForm.
95 // Note: does *not* get the actual password, as that is not a key attribute!
96 // Returns NULL if the attributes are for the wrong application.
97 PasswordForm* FormFromAttributes(GnomeKeyringAttributeList* attrs) {
98   // Read the string and int attributes into the appropriate map.
99   std::map<std::string, std::string> string_attr_map;
100   std::map<std::string, uint32_t> uint_attr_map;
101   for (guint i = 0; i < attrs->len; ++i) {
102     GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i);
103     if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)
104       string_attr_map[attr.name] = attr.value.string;
105     else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32)
106       uint_attr_map[attr.name] = attr.value.integer;
107   }
108   // Check to make sure this is a password we care about.
109   const std::string& app_value = string_attr_map["application"];
110   if (!base::StringPiece(app_value).starts_with(kGnomeKeyringAppString))
111     return NULL;
112
113   PasswordForm* form = new PasswordForm();
114   form->origin = GURL(string_attr_map["origin_url"]);
115   form->action = GURL(string_attr_map["action_url"]);
116   form->username_element = UTF8ToUTF16(string_attr_map["username_element"]);
117   form->username_value = UTF8ToUTF16(string_attr_map["username_value"]);
118   form->password_element = UTF8ToUTF16(string_attr_map["password_element"]);
119   form->submit_element = UTF8ToUTF16(string_attr_map["submit_element"]);
120   form->signon_realm = string_attr_map["signon_realm"];
121   form->ssl_valid = uint_attr_map["ssl_valid"];
122   form->preferred = uint_attr_map["preferred"];
123   int64 date_created = 0;
124   bool date_ok = base::StringToInt64(string_attr_map["date_created"],
125                                      &date_created);
126   DCHECK(date_ok);
127   form->date_created = base::Time::FromTimeT(date_created);
128   form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"];
129   form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]);
130
131   return form;
132 }
133
134 // Parse all the results from the given GList into a PasswordFormList, and free
135 // the GList. PasswordForms are allocated on the heap, and should be deleted by
136 // the consumer.
137 void ConvertFormList(GList* found,
138                      NativeBackendGnome::PasswordFormList* forms) {
139   GList* element = g_list_first(found);
140   while (element != NULL) {
141     GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
142     GnomeKeyringAttributeList* attrs = data->attributes;
143
144     PasswordForm* form = FormFromAttributes(attrs);
145     if (form) {
146       if (data->secret) {
147         form->password_value = UTF8ToUTF16(data->secret);
148       } else {
149         LOG(WARNING) << "Unable to access password from list element!";
150       }
151       forms->push_back(form);
152     } else {
153       LOG(WARNING) << "Could not initialize PasswordForm from attributes!";
154     }
155
156     element = g_list_next(element);
157   }
158 }
159
160 // Schema is analagous to the fields in PasswordForm.
161 const GnomeKeyringPasswordSchema kGnomeSchema = {
162   GNOME_KEYRING_ITEM_GENERIC_SECRET, {
163     { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
164     { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
165     { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
166     { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
167     { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
168     { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
169     { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
170     { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
171     { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
172     { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
173     { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
174     { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
175     // This field is always "chrome" so that we can search for it.
176     { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
177     { NULL }
178   }
179 };
180
181 // Sadly, PasswordStore goes to great lengths to switch from the originally
182 // calling thread to the DB thread, and to provide an asynchronous API to
183 // callers while using a synchronous (virtual) API provided by subclasses like
184 // PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main
185 // thread, which is the UI thread to us. So we end up having to switch threads
186 // again, possibly back to the very same thread (in case the UI thread is the
187 // caller, e.g. in the password management UI), and *block* the DB thread
188 // waiting for a response from the UI thread to provide the synchronous API
189 // PasswordStore expects of us. (It will then in turn switch back to the
190 // original caller to send the asynchronous reply to the original request.)
191
192 // This class represents a call to a GNOME Keyring method. A RunnableMethod
193 // should be posted to the UI thread to call one of its action methods, and then
194 // a WaitResult() method should be called to wait for the result. Each instance
195 // supports only one outstanding method at a time, though multiple instances may
196 // be used in parallel.
197 class GKRMethod : public GnomeKeyringLoader {
198  public:
199   typedef NativeBackendGnome::PasswordFormList PasswordFormList;
200
201   GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {}
202
203   // Action methods. These call gnome_keyring_* functions. Call from UI thread.
204   // See GetProfileSpecificAppString() for more information on the app string.
205   void AddLogin(const PasswordForm& form, const char* app_string);
206   void AddLoginSearch(const PasswordForm& form, const char* app_string);
207   void UpdateLoginSearch(const PasswordForm& form, const char* app_string);
208   void RemoveLogin(const PasswordForm& form, const char* app_string);
209   void GetLogins(const PasswordForm& form, const char* app_string);
210   void GetLoginsList(uint32_t blacklisted_by_user, const char* app_string);
211   void GetAllLogins(const char* app_string);
212
213   // Use after AddLogin, RemoveLogin.
214   GnomeKeyringResult WaitResult();
215
216   // Use after AddLoginSearch, UpdateLoginSearch, GetLogins, GetLoginsList,
217   // GetAllLogins.
218   GnomeKeyringResult WaitResult(PasswordFormList* forms);
219
220  private:
221   // All these callbacks are called on UI thread.
222   static void OnOperationDone(GnomeKeyringResult result, gpointer data);
223
224   static void OnOperationGetList(GnomeKeyringResult result, GList* list,
225                                  gpointer data);
226
227   base::WaitableEvent event_;
228   GnomeKeyringResult result_;
229   NativeBackendGnome::PasswordFormList forms_;
230 };
231
232 void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) {
233   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
234   time_t date_created = form.date_created.ToTimeT();
235   // If we are asked to save a password with 0 date, use the current time.
236   // We don't want to actually save passwords as though on January 1, 1970.
237   if (!date_created)
238     date_created = time(NULL);
239   gnome_keyring_store_password(
240       &kGnomeSchema,
241       NULL,  // Default keyring.
242       form.origin.spec().c_str(),  // Display name.
243       UTF16ToUTF8(form.password_value).c_str(),
244       OnOperationDone,
245       this,  // data
246       NULL,  // destroy_data
247       "origin_url", form.origin.spec().c_str(),
248       "action_url", form.action.spec().c_str(),
249       "username_element", UTF16ToUTF8(form.username_element).c_str(),
250       "username_value", UTF16ToUTF8(form.username_value).c_str(),
251       "password_element", UTF16ToUTF8(form.password_element).c_str(),
252       "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
253       "signon_realm", form.signon_realm.c_str(),
254       "ssl_valid", form.ssl_valid,
255       "preferred", form.preferred,
256       "date_created", base::Int64ToString(date_created).c_str(),
257       "blacklisted_by_user", form.blacklisted_by_user,
258       "scheme", form.scheme,
259       "application", app_string,
260       NULL);
261 }
262
263 void GKRMethod::AddLoginSearch(const PasswordForm& form,
264                                const char* app_string) {
265   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
266   // Search GNOME Keyring for matching passwords to update.
267   gnome_keyring_find_itemsv(
268       GNOME_KEYRING_ITEM_GENERIC_SECRET,
269       OnOperationGetList,
270       this,  // data
271       NULL,  // destroy_data
272       "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
273       form.origin.spec().c_str(),
274       "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
275       UTF16ToUTF8(form.username_element).c_str(),
276       "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
277       UTF16ToUTF8(form.username_value).c_str(),
278       "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
279       UTF16ToUTF8(form.password_element).c_str(),
280       "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
281       UTF16ToUTF8(form.submit_element).c_str(),
282       "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
283       form.signon_realm.c_str(),
284       "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
285       app_string,
286       NULL);
287 }
288
289 void GKRMethod::UpdateLoginSearch(const PasswordForm& form,
290                                   const char* app_string) {
291   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
292   // Search GNOME Keyring for matching passwords to update.
293   gnome_keyring_find_itemsv(
294       GNOME_KEYRING_ITEM_GENERIC_SECRET,
295       OnOperationGetList,
296       this,  // data
297       NULL,  // destroy_data
298       "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
299       form.origin.spec().c_str(),
300       "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
301       UTF16ToUTF8(form.username_element).c_str(),
302       "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
303       UTF16ToUTF8(form.username_value).c_str(),
304       "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
305       UTF16ToUTF8(form.password_element).c_str(),
306       "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
307       form.signon_realm.c_str(),
308       "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
309       app_string,
310       NULL);
311 }
312
313 void GKRMethod::RemoveLogin(const PasswordForm& form, const char* app_string) {
314   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
315   // We find forms using the same fields as LoginDatabase::RemoveLogin().
316   gnome_keyring_delete_password(
317       &kGnomeSchema,
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       "application", app_string,
329       NULL);
330 }
331
332 void GKRMethod::GetLogins(const PasswordForm& form, const char* app_string) {
333   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
334   // Search GNOME Keyring for matching passwords.
335   gnome_keyring_find_itemsv(
336       GNOME_KEYRING_ITEM_GENERIC_SECRET,
337       OnOperationGetList,
338       this,  // data
339       NULL,  // destroy_data
340       "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
341       form.signon_realm.c_str(),
342       "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
343       app_string,
344       NULL);
345 }
346
347 void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user,
348                               const char* app_string) {
349   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
350   // Search GNOME Keyring for matching passwords.
351   gnome_keyring_find_itemsv(
352       GNOME_KEYRING_ITEM_GENERIC_SECRET,
353       OnOperationGetList,
354       this,  // data
355       NULL,  // destroy_data
356       "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32,
357       blacklisted_by_user,
358       "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
359       app_string,
360       NULL);
361 }
362
363 void GKRMethod::GetAllLogins(const char* app_string) {
364   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
365   // We need to search for something, otherwise we get no results - so
366   // we search for the fixed application string.
367   gnome_keyring_find_itemsv(
368       GNOME_KEYRING_ITEM_GENERIC_SECRET,
369       OnOperationGetList,
370       this,  // data
371       NULL,  // destroy_data
372       "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
373       app_string,
374       NULL);
375 }
376
377 GnomeKeyringResult GKRMethod::WaitResult() {
378   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
379   event_.Wait();
380   return result_;
381 }
382
383 GnomeKeyringResult GKRMethod::WaitResult(PasswordFormList* forms) {
384   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
385   event_.Wait();
386   if (forms->empty()) {
387     // Normal case. Avoid extra allocation by swapping.
388     forms->swap(forms_);
389   } else {
390     // Rare case. Append forms_ to *forms.
391     forms->insert(forms->end(), forms_.begin(), forms_.end());
392     forms_.clear();
393   }
394   return result_;
395 }
396
397 // static
398 void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) {
399   GKRMethod* method = static_cast<GKRMethod*>(data);
400   method->result_ = result;
401   method->event_.Signal();
402 }
403
404 // static
405 void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list,
406                                    gpointer data) {
407   GKRMethod* method = static_cast<GKRMethod*>(data);
408   method->result_ = result;
409   method->forms_.clear();
410   // |list| will be freed after this callback returns, so convert it now.
411   ConvertFormList(list, &method->forms_);
412   method->event_.Signal();
413 }
414
415 }  // namespace
416
417 NativeBackendGnome::NativeBackendGnome(LocalProfileId id, PrefService* prefs)
418     : profile_id_(id), prefs_(prefs) {
419   // TODO(mdm): after a few more releases, remove the code which is now dead due
420   // to the true || here, and simplify this code. We don't do it yet to make it
421   // easier to revert if necessary.
422   if (true || PasswordStoreX::PasswordsUseLocalProfileId(prefs)) {
423     app_string_ = GetProfileSpecificAppString();
424     // We already did the migration previously. Don't try again.
425     migrate_tried_ = true;
426   } else {
427     app_string_ = kGnomeKeyringAppString;
428     migrate_tried_ = false;
429   }
430 }
431
432 NativeBackendGnome::~NativeBackendGnome() {
433 }
434
435 bool NativeBackendGnome::Init() {
436   return LoadGnomeKeyring() && gnome_keyring_is_available();
437 }
438
439 bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) {
440   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
441   GKRMethod method;
442   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
443                           base::Bind(&GKRMethod::AddLogin,
444                                      base::Unretained(&method),
445                                      form, app_string_.c_str()));
446   GnomeKeyringResult result = method.WaitResult();
447   if (result != GNOME_KEYRING_RESULT_OK) {
448     LOG(ERROR) << "Keyring save failed: "
449                << gnome_keyring_result_to_message(result);
450     return false;
451   }
452   // Successful write. Try migration if necessary.
453   if (!migrate_tried_)
454     MigrateToProfileSpecificLogins();
455   return true;
456 }
457
458 bool NativeBackendGnome::AddLogin(const PasswordForm& form) {
459   // Based on LoginDatabase::AddLogin(), we search for an existing match based
460   // on origin_url, username_element, username_value, password_element, submit
461   // element, and signon_realm first, remove that, and then add the new entry.
462   // We'd add the new one first, and then delete the original, but then the
463   // delete might actually delete the newly-added entry!
464   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
465   GKRMethod method;
466   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
467                           base::Bind(&GKRMethod::AddLoginSearch,
468                                      base::Unretained(&method),
469                                      form, app_string_.c_str()));
470   PasswordFormList forms;
471   GnomeKeyringResult result = method.WaitResult(&forms);
472   if (result != GNOME_KEYRING_RESULT_OK &&
473       result != GNOME_KEYRING_RESULT_NO_MATCH) {
474     LOG(ERROR) << "Keyring find failed: "
475                << gnome_keyring_result_to_message(result);
476     return false;
477   }
478   if (forms.size() > 0) {
479     if (forms.size() > 1) {
480       LOG(WARNING) << "Adding login when there are " << forms.size()
481                    << " matching logins already! Will replace only the first.";
482     }
483
484     // We try migration before updating the existing logins, since otherwise
485     // we'd do it after making some but not all of the changes below.
486     if (forms.size() > 0 && !migrate_tried_)
487       MigrateToProfileSpecificLogins();
488
489     RemoveLogin(*forms[0]);
490     for (size_t i = 0; i < forms.size(); ++i)
491       delete forms[i];
492   }
493   return RawAddLogin(form);
494 }
495
496 bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) {
497   // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
498   // origin_url, username_element, username_value, password_element, and
499   // signon_realm. We then compare the result to the updated form. If they
500   // differ in any of the action, password_value, ssl_valid, or preferred
501   // fields, then we remove the original, and then add the new entry. We'd add
502   // the new one first, and then delete the original, but then the delete might
503   // actually delete the newly-added entry!
504   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
505   GKRMethod method;
506   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
507                           base::Bind(&GKRMethod::UpdateLoginSearch,
508                                      base::Unretained(&method),
509                                      form, app_string_.c_str()));
510   PasswordFormList forms;
511   GnomeKeyringResult result = method.WaitResult(&forms);
512   if (result != GNOME_KEYRING_RESULT_OK) {
513     LOG(ERROR) << "Keyring find failed: "
514                << gnome_keyring_result_to_message(result);
515     return false;
516   }
517
518   // We try migration before updating the existing logins, since otherwise
519   // we'd do it after making some but not all of the changes below.
520   if (forms.size() > 0 && !migrate_tried_)
521     MigrateToProfileSpecificLogins();
522
523   bool ok = true;
524   for (size_t i = 0; i < forms.size(); ++i) {
525     if (forms[i]->action != form.action ||
526         forms[i]->password_value != form.password_value ||
527         forms[i]->ssl_valid != form.ssl_valid ||
528         forms[i]->preferred != form.preferred) {
529       RemoveLogin(*forms[i]);
530     }
531   }
532   for (size_t i = 0; i < forms.size(); ++i) {
533     if (forms[i]->action != form.action ||
534         forms[i]->password_value != form.password_value ||
535         forms[i]->ssl_valid != form.ssl_valid ||
536         forms[i]->preferred != form.preferred) {
537       forms[i]->action = form.action;
538       forms[i]->password_value = form.password_value;
539       forms[i]->ssl_valid = form.ssl_valid;
540       forms[i]->preferred = form.preferred;
541       if (!RawAddLogin(*forms[i]))
542         ok = false;
543     }
544     delete forms[i];
545   }
546   return ok;
547 }
548
549 bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
550   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
551   GKRMethod method;
552   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
553                           base::Bind(&GKRMethod::RemoveLogin,
554                                      base::Unretained(&method),
555                                      form, app_string_.c_str()));
556   GnomeKeyringResult result = method.WaitResult();
557   if (result != GNOME_KEYRING_RESULT_OK) {
558     // Warning, not error, because this can sometimes happen due to the user
559     // racing with the daemon to delete the password a second time.
560     LOG(WARNING) << "Keyring delete failed: "
561                  << gnome_keyring_result_to_message(result);
562     return false;
563   }
564   // Successful write. Try migration if necessary. Note that presumably if we've
565   // been asked to delete a login, it's because we returned it previously; thus,
566   // this will probably never happen since we'd have already tried migration.
567   if (!migrate_tried_)
568     MigrateToProfileSpecificLogins();
569   return true;
570 }
571
572 bool NativeBackendGnome::RemoveLoginsCreatedBetween(
573     const base::Time& delete_begin,
574     const base::Time& delete_end) {
575   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
576   bool ok = true;
577   // We could walk the list and delete items as we find them, but it is much
578   // easier to build the list and use RemoveLogin() to delete them.
579   PasswordFormList forms;
580   if (!GetAllLogins(&forms))
581     return false;
582   // No need to try migration here: GetAllLogins() does it.
583
584   for (size_t i = 0; i < forms.size(); ++i) {
585     if (delete_begin <= forms[i]->date_created &&
586         (delete_end.is_null() || forms[i]->date_created < delete_end)) {
587       if (!RemoveLogin(*forms[i]))
588         ok = false;
589     }
590     delete forms[i];
591   }
592   return ok;
593 }
594
595 bool NativeBackendGnome::GetLogins(const PasswordForm& form,
596                                    PasswordFormList* forms) {
597   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
598   GKRMethod method;
599   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
600                           base::Bind(&GKRMethod::GetLogins,
601                                      base::Unretained(&method),
602                                      form, app_string_.c_str()));
603   GnomeKeyringResult result = method.WaitResult(forms);
604   if (result == GNOME_KEYRING_RESULT_NO_MATCH)
605     return true;
606   if (result != GNOME_KEYRING_RESULT_OK) {
607     LOG(ERROR) << "Keyring find failed: "
608                << gnome_keyring_result_to_message(result);
609     return false;
610   }
611   // Successful read of actual data. Try migration if necessary.
612   if (!migrate_tried_)
613     MigrateToProfileSpecificLogins();
614   return true;
615 }
616
617 bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin,
618                                                  const base::Time& get_end,
619                                                  PasswordFormList* forms) {
620   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
621   // We could walk the list and add items as we find them, but it is much
622   // easier to build the list and then filter the results.
623   PasswordFormList all_forms;
624   if (!GetAllLogins(&all_forms))
625     return false;
626   // No need to try migration here: GetAllLogins() does it.
627
628   forms->reserve(forms->size() + all_forms.size());
629   for (size_t i = 0; i < all_forms.size(); ++i) {
630     if (get_begin <= all_forms[i]->date_created &&
631         (get_end.is_null() || all_forms[i]->date_created < get_end)) {
632       forms->push_back(all_forms[i]);
633     } else {
634       delete all_forms[i];
635     }
636   }
637
638   return true;
639 }
640
641 bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
642   return GetLoginsList(forms, true);
643 }
644
645 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
646   return GetLoginsList(forms, false);
647 }
648
649 bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms,
650                                        bool autofillable) {
651   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
652
653   uint32_t blacklisted_by_user = !autofillable;
654
655   GKRMethod method;
656   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
657                           base::Bind(&GKRMethod::GetLoginsList,
658                                      base::Unretained(&method),
659                                      blacklisted_by_user, app_string_.c_str()));
660   GnomeKeyringResult result = method.WaitResult(forms);
661   if (result == GNOME_KEYRING_RESULT_NO_MATCH)
662     return true;
663   if (result != GNOME_KEYRING_RESULT_OK) {
664     LOG(ERROR) << "Keyring find failed: "
665                << gnome_keyring_result_to_message(result);
666     return false;
667   }
668   // Successful read of actual data. Try migration if necessary.
669   if (!migrate_tried_)
670     MigrateToProfileSpecificLogins();
671   return true;
672 }
673
674 bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) {
675   GKRMethod method;
676   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
677                           base::Bind(&GKRMethod::GetAllLogins,
678                                      base::Unretained(&method),
679                                      app_string_.c_str()));
680   GnomeKeyringResult result = method.WaitResult(forms);
681   if (result == GNOME_KEYRING_RESULT_NO_MATCH)
682     return true;
683   if (result != GNOME_KEYRING_RESULT_OK) {
684     LOG(ERROR) << "Keyring find failed: "
685                << gnome_keyring_result_to_message(result);
686     return false;
687   }
688   // Successful read of actual data. Try migration if necessary.
689   if (!migrate_tried_)
690     MigrateToProfileSpecificLogins();
691   return true;
692 }
693
694 std::string NativeBackendGnome::GetProfileSpecificAppString() const {
695   // Originally, the application string was always just "chrome" and used only
696   // so that we had *something* to search for since GNOME Keyring won't search
697   // for nothing. Now we use it to distinguish passwords for different profiles.
698   return base::StringPrintf("%s-%d", kGnomeKeyringAppString, profile_id_);
699 }
700
701 void NativeBackendGnome::MigrateToProfileSpecificLogins() {
702   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
703
704   DCHECK(!migrate_tried_);
705   DCHECK_EQ(app_string_, kGnomeKeyringAppString);
706
707   // Record the fact that we've attempted migration already right away, so that
708   // we don't get recursive calls back to MigrateToProfileSpecificLogins().
709   migrate_tried_ = true;
710
711   // First get all the logins, using the old app string.
712   PasswordFormList forms;
713   if (!GetAllLogins(&forms))
714     return;
715
716   // Now switch to a profile-specific app string.
717   app_string_ = GetProfileSpecificAppString();
718
719   // Try to add all the logins with the new app string.
720   bool ok = true;
721   for (size_t i = 0; i < forms.size(); ++i) {
722     if (!RawAddLogin(*forms[i]))
723       ok = false;
724     delete forms[i];
725   }
726
727   if (ok) {
728     // All good! Keep the new app string and set a persistent pref.
729     // NOTE: We explicitly don't delete the old passwords yet. They are
730     // potentially shared with other profiles and other user data dirs!
731     // Each other profile must be able to migrate the shared data as well,
732     // so we must leave it alone. After a few releases, we'll add code to
733     // delete them, and eventually remove this migration code.
734     // TODO(mdm): follow through with the plan above.
735     PasswordStoreX::SetPasswordsUseLocalProfileId(prefs_);
736   } else {
737     // We failed to migrate for some reason. Use the old app string.
738     app_string_ = kGnomeKeyringAppString;
739   }
740 }