Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / password_manager / native_backend_kwallet_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_kwallet_x.h"
6
7 #include <vector>
8
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/pickle.h"
12 #include "base/stl_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "components/autofill/core/common/password_form.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "dbus/bus.h"
19 #include "dbus/message.h"
20 #include "dbus/object_path.h"
21 #include "dbus/object_proxy.h"
22 #include "grit/chromium_strings.h"
23 #include "ui/base/l10n/l10n_util.h"
24
25 using autofill::PasswordForm;
26 using content::BrowserThread;
27
28 namespace {
29
30 // We could localize this string, but then changing your locale would cause
31 // you to lose access to all your stored passwords. Maybe best not to do that.
32 // Name of the folder to store passwords in.
33 const char kKWalletFolder[] = "Chrome Form Data";
34
35 // DBus service, path, and interface names for klauncher and kwalletd.
36 const char kKWalletServiceName[] = "org.kde.kwalletd";
37 const char kKWalletPath[] = "/modules/kwalletd";
38 const char kKWalletInterface[] = "org.kde.KWallet";
39 const char kKLauncherServiceName[] = "org.kde.klauncher";
40 const char kKLauncherPath[] = "/KLauncher";
41 const char kKLauncherInterface[] = "org.kde.KLauncher";
42
43 // Compares two PasswordForms and returns true if they are the same.
44 // If |update_check| is false, we only check the fields that are checked by
45 // LoginDatabase::UpdateLogin() when updating logins; otherwise, we check the
46 // fields that are checked by LoginDatabase::RemoveLogin() for removing them.
47 bool CompareForms(const autofill::PasswordForm& a,
48                   const autofill::PasswordForm& b,
49                   bool update_check) {
50   // An update check doesn't care about the submit element.
51   if (!update_check && a.submit_element != b.submit_element)
52     return false;
53   return a.origin           == b.origin &&
54          a.password_element == b.password_element &&
55          a.signon_realm     == b.signon_realm &&
56          a.username_element == b.username_element &&
57          a.username_value   == b.username_value;
58 }
59
60 // Checks a serialized list of PasswordForms for sanity. Returns true if OK.
61 // Note that |realm| is only used for generating a useful warning message.
62 bool CheckSerializedValue(const uint8_t* byte_array,
63                           size_t length,
64                           const std::string& realm) {
65   const Pickle::Header* header =
66       reinterpret_cast<const Pickle::Header*>(byte_array);
67   if (length < sizeof(*header) ||
68       header->payload_size > length - sizeof(*header)) {
69     LOG(WARNING) << "Invalid KWallet entry detected (realm: " << realm << ")";
70     return false;
71   }
72   return true;
73 }
74
75 // Convenience function to read a GURL from a Pickle. Assumes the URL has
76 // been written as a UTF-8 string. Returns true on success.
77 bool ReadGURL(PickleIterator* iter, bool warn_only, GURL* url) {
78   std::string url_string;
79   if (!iter->ReadString(&url_string)) {
80     if (!warn_only)
81       LOG(ERROR) << "Failed to deserialize URL.";
82     *url = GURL();
83     return false;
84   }
85   *url = GURL(url_string);
86   return true;
87 }
88
89 void LogDeserializationWarning(int version,
90                                std::string signon_realm,
91                                bool warn_only) {
92   if (warn_only) {
93     LOG(WARNING) << "Failed to deserialize version " << version
94                  << " KWallet entry (realm: " << signon_realm
95                  << ") with native architecture size; will try alternate "
96                  << "size.";
97   } else {
98     LOG(ERROR) << "Failed to deserialize version " << version
99                << " KWallet entry (realm: " << signon_realm << ")";
100   }
101 }
102
103 }  // namespace
104
105 NativeBackendKWallet::NativeBackendKWallet(LocalProfileId id)
106     : profile_id_(id),
107       kwallet_proxy_(NULL),
108       app_name_(l10n_util::GetStringUTF8(IDS_PRODUCT_NAME)) {
109   folder_name_ = GetProfileSpecificFolderName();
110 }
111
112 NativeBackendKWallet::~NativeBackendKWallet() {
113   // This destructor is called on the thread that is destroying the Profile
114   // containing the PasswordStore that owns this NativeBackend. Generally that
115   // won't be the DB thread; it will be the UI thread. So we post a message to
116   // shut it down on the DB thread, and it will be destructed afterward when the
117   // scoped_refptr<dbus::Bus> goes out of scope. The NativeBackend will be
118   // destroyed before that occurs, but that's OK.
119   if (session_bus_.get()) {
120     BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
121                             base::Bind(&dbus::Bus::ShutdownAndBlock,
122                                        session_bus_.get()));
123   }
124 }
125
126 bool NativeBackendKWallet::Init() {
127   // Without the |optional_bus| parameter, a real bus will be instantiated.
128   return InitWithBus(scoped_refptr<dbus::Bus>());
129 }
130
131 bool NativeBackendKWallet::InitWithBus(scoped_refptr<dbus::Bus> optional_bus) {
132   // We must synchronously do a few DBus calls to figure out if initialization
133   // succeeds, but later, we'll want to do most work on the DB thread. So we
134   // have to do the initialization on the DB thread here too, and wait for it.
135   bool success = false;
136   base::WaitableEvent event(false, false);
137   // NativeBackendKWallet isn't reference counted, but we wait for InitWithBus
138   // to finish, so we can safely use base::Unretained here.
139   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
140                           base::Bind(&NativeBackendKWallet::InitOnDBThread,
141                                      base::Unretained(this),
142                                      optional_bus, &event, &success));
143
144   // This ScopedAllowWait should not be here. http://crbug.com/125331
145   base::ThreadRestrictions::ScopedAllowWait allow_wait;
146   event.Wait();
147   return success;
148 }
149
150 void NativeBackendKWallet::InitOnDBThread(scoped_refptr<dbus::Bus> optional_bus,
151                                           base::WaitableEvent* event,
152                                           bool* success) {
153   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
154   DCHECK(!session_bus_.get());
155   if (optional_bus.get()) {
156     // The optional_bus parameter is given when this method is called in tests.
157     session_bus_ = optional_bus;
158   } else {
159     // Get a (real) connection to the session bus.
160     dbus::Bus::Options options;
161     options.bus_type = dbus::Bus::SESSION;
162     options.connection_type = dbus::Bus::PRIVATE;
163     session_bus_ = new dbus::Bus(options);
164   }
165   kwallet_proxy_ =
166       session_bus_->GetObjectProxy(kKWalletServiceName,
167                                    dbus::ObjectPath(kKWalletPath));
168   // kwalletd may not be running. If we get a temporary failure initializing it,
169   // try to start it and then try again. (Note the short-circuit evaluation.)
170   const InitResult result = InitWallet();
171   *success = (result == INIT_SUCCESS ||
172               (result == TEMPORARY_FAIL &&
173                StartKWalletd() && InitWallet() == INIT_SUCCESS));
174   event->Signal();
175 }
176
177 bool NativeBackendKWallet::StartKWalletd() {
178   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
179   // Sadly kwalletd doesn't use DBus activation, so we have to make a call to
180   // klauncher to start it.
181   dbus::ObjectProxy* klauncher =
182       session_bus_->GetObjectProxy(kKLauncherServiceName,
183                                    dbus::ObjectPath(kKLauncherPath));
184
185   dbus::MethodCall method_call(kKLauncherInterface,
186                                "start_service_by_desktop_name");
187   dbus::MessageWriter builder(&method_call);
188   std::vector<std::string> empty;
189   builder.AppendString("kwalletd");     // serviceName
190   builder.AppendArrayOfStrings(empty);  // urls
191   builder.AppendArrayOfStrings(empty);  // envs
192   builder.AppendString(std::string());  // startup_id
193   builder.AppendBool(false);            // blind
194   scoped_ptr<dbus::Response> response(
195       klauncher->CallMethodAndBlock(
196           &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
197   if (!response.get()) {
198     LOG(ERROR) << "Error contacting klauncher to start kwalletd";
199     return false;
200   }
201   dbus::MessageReader reader(response.get());
202   int32_t ret = -1;
203   std::string dbus_name;
204   std::string error;
205   int32_t pid = -1;
206   if (!reader.PopInt32(&ret) || !reader.PopString(&dbus_name) ||
207       !reader.PopString(&error) || !reader.PopInt32(&pid)) {
208     LOG(ERROR) << "Error reading response from klauncher to start kwalletd: "
209                << response->ToString();
210     return false;
211   }
212   if (!error.empty() || ret) {
213     LOG(ERROR) << "Error launching kwalletd: error '" << error << "' "
214                << " (code " << ret << ")";
215     return false;
216   }
217
218   return true;
219 }
220
221 NativeBackendKWallet::InitResult NativeBackendKWallet::InitWallet() {
222   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
223   {
224     // Check that KWallet is enabled.
225     dbus::MethodCall method_call(kKWalletInterface, "isEnabled");
226     scoped_ptr<dbus::Response> response(
227         kwallet_proxy_->CallMethodAndBlock(
228             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
229     if (!response.get()) {
230       LOG(ERROR) << "Error contacting kwalletd (isEnabled)";
231       return TEMPORARY_FAIL;
232     }
233     dbus::MessageReader reader(response.get());
234     bool enabled = false;
235     if (!reader.PopBool(&enabled)) {
236       LOG(ERROR) << "Error reading response from kwalletd (isEnabled): "
237                  << response->ToString();
238       return PERMANENT_FAIL;
239     }
240     // Not enabled? Don't use KWallet. But also don't warn here.
241     if (!enabled) {
242       VLOG(1) << "kwalletd reports that KWallet is not enabled.";
243       return PERMANENT_FAIL;
244     }
245   }
246
247   {
248     // Get the wallet name.
249     dbus::MethodCall method_call(kKWalletInterface, "networkWallet");
250     scoped_ptr<dbus::Response> response(
251         kwallet_proxy_->CallMethodAndBlock(
252             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
253     if (!response.get()) {
254       LOG(ERROR) << "Error contacting kwalletd (networkWallet)";
255       return TEMPORARY_FAIL;
256     }
257     dbus::MessageReader reader(response.get());
258     if (!reader.PopString(&wallet_name_)) {
259       LOG(ERROR) << "Error reading response from kwalletd (networkWallet): "
260                  << response->ToString();
261       return PERMANENT_FAIL;
262     }
263   }
264
265   return INIT_SUCCESS;
266 }
267
268 password_manager::PasswordStoreChangeList NativeBackendKWallet::AddLogin(
269     const PasswordForm& form) {
270   int wallet_handle = WalletHandle();
271   if (wallet_handle == kInvalidKWalletHandle)
272     return password_manager::PasswordStoreChangeList();
273
274   ScopedVector<autofill::PasswordForm> forms;
275   GetLoginsList(&forms.get(), form.signon_realm, wallet_handle);
276
277   // We search for a login to update, rather than unconditionally appending the
278   // login, because in some cases (especially involving sync) we can be asked to
279   // add a login that already exists. In these cases we want to just update.
280   bool updated = false;
281   password_manager::PasswordStoreChangeList changes;
282   for (size_t i = 0; i < forms.size(); ++i) {
283     // Use the more restrictive removal comparison, so that we never have
284     // duplicate logins that would all be removed together by RemoveLogin().
285     if (CompareForms(form, *forms[i], false)) {
286       changes.push_back(password_manager::PasswordStoreChange(
287           password_manager::PasswordStoreChange::REMOVE, *forms[i]));
288       *forms[i] = form;
289       updated = true;
290     }
291   }
292   if (!updated)
293     forms.push_back(new PasswordForm(form));
294   changes.push_back(password_manager::PasswordStoreChange(
295       password_manager::PasswordStoreChange::ADD, form));
296
297   bool ok = SetLoginsList(forms.get(), form.signon_realm, wallet_handle);
298   if (!ok)
299     changes.clear();
300
301   return changes;
302 }
303
304 bool NativeBackendKWallet::UpdateLogin(
305     const PasswordForm& form,
306     password_manager::PasswordStoreChangeList* changes) {
307   DCHECK(changes);
308   changes->clear();
309   int wallet_handle = WalletHandle();
310   if (wallet_handle == kInvalidKWalletHandle)
311     return false;
312
313   ScopedVector<autofill::PasswordForm> forms;
314   GetLoginsList(&forms.get(), form.signon_realm, wallet_handle);
315
316   bool updated = false;
317   for (size_t i = 0; i < forms.size(); ++i) {
318     if (CompareForms(form, *forms[i], true)) {
319       *forms[i] = form;
320       updated = true;
321     }
322   }
323   if (!updated)
324     return true;
325
326   if (SetLoginsList(forms.get(), form.signon_realm, wallet_handle)) {
327     changes->push_back(password_manager::PasswordStoreChange(
328         password_manager::PasswordStoreChange::UPDATE, form));
329     return true;
330   }
331
332   return false;
333 }
334
335 bool NativeBackendKWallet::RemoveLogin(const PasswordForm& form) {
336   int wallet_handle = WalletHandle();
337   if (wallet_handle == kInvalidKWalletHandle)
338     return false;
339
340   PasswordFormList all_forms;
341   GetLoginsList(&all_forms, form.signon_realm, wallet_handle);
342
343   PasswordFormList kept_forms;
344   kept_forms.reserve(all_forms.size());
345   for (size_t i = 0; i < all_forms.size(); ++i) {
346     if (CompareForms(form, *all_forms[i], false))
347       delete all_forms[i];
348     else
349       kept_forms.push_back(all_forms[i]);
350   }
351
352   // Update the entry in the wallet, possibly deleting it.
353   bool ok = SetLoginsList(kept_forms, form.signon_realm, wallet_handle);
354
355   STLDeleteElements(&kept_forms);
356   return ok;
357 }
358
359 bool NativeBackendKWallet::RemoveLoginsCreatedBetween(
360     base::Time delete_begin,
361     base::Time delete_end,
362     password_manager::PasswordStoreChangeList* changes) {
363   return RemoveLoginsBetween(
364       delete_begin, delete_end, CREATION_TIMESTAMP, changes);
365 }
366
367 bool NativeBackendKWallet::RemoveLoginsSyncedBetween(
368     base::Time delete_begin,
369     base::Time delete_end,
370     password_manager::PasswordStoreChangeList* changes) {
371   return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes);
372 }
373
374 bool NativeBackendKWallet::GetLogins(const PasswordForm& form,
375                                      PasswordFormList* forms) {
376   int wallet_handle = WalletHandle();
377   if (wallet_handle == kInvalidKWalletHandle)
378     return false;
379   return GetLoginsList(forms, form.signon_realm, wallet_handle);
380 }
381
382 bool NativeBackendKWallet::GetAutofillableLogins(PasswordFormList* forms) {
383   int wallet_handle = WalletHandle();
384   if (wallet_handle == kInvalidKWalletHandle)
385     return false;
386   return GetLoginsList(forms, true, wallet_handle);
387 }
388
389 bool NativeBackendKWallet::GetBlacklistLogins(PasswordFormList* forms) {
390   int wallet_handle = WalletHandle();
391   if (wallet_handle == kInvalidKWalletHandle)
392     return false;
393   return GetLoginsList(forms, false, wallet_handle);
394 }
395
396 bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms,
397                                          const std::string& signon_realm,
398                                          int wallet_handle) {
399   // Is there an entry in the wallet?
400   {
401     dbus::MethodCall method_call(kKWalletInterface, "hasEntry");
402     dbus::MessageWriter builder(&method_call);
403     builder.AppendInt32(wallet_handle);  // handle
404     builder.AppendString(folder_name_);  // folder
405     builder.AppendString(signon_realm);  // key
406     builder.AppendString(app_name_);     // appid
407     scoped_ptr<dbus::Response> response(
408         kwallet_proxy_->CallMethodAndBlock(
409             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
410     if (!response.get()) {
411       LOG(ERROR) << "Error contacting kwalletd (hasEntry)";
412       return false;
413     }
414     dbus::MessageReader reader(response.get());
415     bool has_entry = false;
416     if (!reader.PopBool(&has_entry)) {
417       LOG(ERROR) << "Error reading response from kwalletd (hasEntry): "
418                  << response->ToString();
419       return false;
420     }
421     if (!has_entry) {
422       // This is not an error. There just isn't a matching entry.
423       return true;
424     }
425   }
426
427   {
428     dbus::MethodCall method_call(kKWalletInterface, "readEntry");
429     dbus::MessageWriter builder(&method_call);
430     builder.AppendInt32(wallet_handle);  // handle
431     builder.AppendString(folder_name_);  // folder
432     builder.AppendString(signon_realm);  // key
433     builder.AppendString(app_name_);     // appid
434     scoped_ptr<dbus::Response> response(
435         kwallet_proxy_->CallMethodAndBlock(
436             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
437     if (!response.get()) {
438       LOG(ERROR) << "Error contacting kwalletd (readEntry)";
439       return false;
440     }
441     dbus::MessageReader reader(response.get());
442     const uint8_t* bytes = NULL;
443     size_t length = 0;
444     if (!reader.PopArrayOfBytes(&bytes, &length)) {
445       LOG(ERROR) << "Error reading response from kwalletd (readEntry): "
446                  << response->ToString();
447       return false;
448     }
449     if (!bytes)
450       return false;
451     if (!CheckSerializedValue(bytes, length, signon_realm)) {
452       // This is weird, but we choose not to call it an error. There is an
453       // invalid entry somehow, but by just ignoring it, we make it easier to
454       // repair without having to delete it using kwalletmanager (that is, by
455       // just saving a new password within this realm to overwrite it).
456       return true;
457     }
458
459     // Can't we all just agree on whether bytes are signed or not? Please?
460     Pickle pickle(reinterpret_cast<const char*>(bytes), length);
461     PasswordFormList all_forms;
462     DeserializeValue(signon_realm, pickle, forms);
463   }
464
465   return true;
466 }
467
468 bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms,
469                                          bool autofillable,
470                                          int wallet_handle) {
471   PasswordFormList all_forms;
472   if (!GetAllLogins(&all_forms, wallet_handle))
473     return false;
474
475   // We have to read all the entries, and then filter them here.
476   forms->reserve(forms->size() + all_forms.size());
477   for (size_t i = 0; i < all_forms.size(); ++i) {
478     if (all_forms[i]->blacklisted_by_user == !autofillable)
479       forms->push_back(all_forms[i]);
480     else
481       delete all_forms[i];
482   }
483
484   return true;
485 }
486
487 bool NativeBackendKWallet::GetAllLogins(PasswordFormList* forms,
488                                         int wallet_handle) {
489   // We could probably also use readEntryList here.
490   std::vector<std::string> realm_list;
491   {
492     dbus::MethodCall method_call(kKWalletInterface, "entryList");
493     dbus::MessageWriter builder(&method_call);
494     builder.AppendInt32(wallet_handle);  // handle
495     builder.AppendString(folder_name_);  // folder
496     builder.AppendString(app_name_);     // appid
497     scoped_ptr<dbus::Response> response(
498         kwallet_proxy_->CallMethodAndBlock(
499             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
500     if (!response.get()) {
501       LOG(ERROR) << "Error contacting kwalletd (entryList)";
502       return false;
503     }
504     dbus::MessageReader reader(response.get());
505     if (!reader.PopArrayOfStrings(&realm_list)) {
506       LOG(ERROR) << "Error reading response from kwalletd (entryList): "
507                  << response->ToString();
508       return false;
509     }
510   }
511
512   for (size_t i = 0; i < realm_list.size(); ++i) {
513     const std::string& signon_realm = realm_list[i];
514     dbus::MethodCall method_call(kKWalletInterface, "readEntry");
515     dbus::MessageWriter builder(&method_call);
516     builder.AppendInt32(wallet_handle);  // handle
517     builder.AppendString(folder_name_);  // folder
518     builder.AppendString(signon_realm);  // key
519     builder.AppendString(app_name_);     // appid
520     scoped_ptr<dbus::Response> response(
521         kwallet_proxy_->CallMethodAndBlock(
522             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
523     if (!response.get()) {
524       LOG(ERROR) << "Error contacting kwalletd (readEntry)";
525       continue;
526     }
527     dbus::MessageReader reader(response.get());
528     const uint8_t* bytes = NULL;
529     size_t length = 0;
530     if (!reader.PopArrayOfBytes(&bytes, &length)) {
531       LOG(ERROR) << "Error reading response from kwalletd (readEntry): "
532                  << response->ToString();
533       continue;
534     }
535     if (!bytes || !CheckSerializedValue(bytes, length, signon_realm))
536       continue;
537
538     // Can't we all just agree on whether bytes are signed or not? Please?
539     Pickle pickle(reinterpret_cast<const char*>(bytes), length);
540     PasswordFormList all_forms;
541     DeserializeValue(signon_realm, pickle, forms);
542   }
543   return true;
544 }
545
546 bool NativeBackendKWallet::SetLoginsList(const PasswordFormList& forms,
547                                          const std::string& signon_realm,
548                                          int wallet_handle) {
549   if (forms.empty()) {
550     // No items left? Remove the entry from the wallet.
551     dbus::MethodCall method_call(kKWalletInterface, "removeEntry");
552     dbus::MessageWriter builder(&method_call);
553     builder.AppendInt32(wallet_handle);  // handle
554     builder.AppendString(folder_name_);  // folder
555     builder.AppendString(signon_realm);  // key
556     builder.AppendString(app_name_);     // appid
557     scoped_ptr<dbus::Response> response(
558         kwallet_proxy_->CallMethodAndBlock(
559             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
560     if (!response.get()) {
561       LOG(ERROR) << "Error contacting kwalletd (removeEntry)";
562       return kInvalidKWalletHandle;
563     }
564     dbus::MessageReader reader(response.get());
565     int ret = 0;
566     if (!reader.PopInt32(&ret)) {
567       LOG(ERROR) << "Error reading response from kwalletd (removeEntry): "
568                  << response->ToString();
569       return false;
570     }
571     if (ret != 0)
572       LOG(ERROR) << "Bad return code " << ret << " from KWallet removeEntry";
573     return ret == 0;
574   }
575
576   Pickle value;
577   SerializeValue(forms, &value);
578
579   dbus::MethodCall method_call(kKWalletInterface, "writeEntry");
580   dbus::MessageWriter builder(&method_call);
581   builder.AppendInt32(wallet_handle);  // handle
582   builder.AppendString(folder_name_);  // folder
583   builder.AppendString(signon_realm);  // key
584   builder.AppendArrayOfBytes(static_cast<const uint8_t*>(value.data()),
585                              value.size());  // value
586   builder.AppendString(app_name_);     // appid
587   scoped_ptr<dbus::Response> response(
588       kwallet_proxy_->CallMethodAndBlock(
589           &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
590   if (!response.get()) {
591     LOG(ERROR) << "Error contacting kwalletd (writeEntry)";
592     return kInvalidKWalletHandle;
593   }
594   dbus::MessageReader reader(response.get());
595   int ret = 0;
596   if (!reader.PopInt32(&ret)) {
597     LOG(ERROR) << "Error reading response from kwalletd (writeEntry): "
598                << response->ToString();
599     return false;
600   }
601   if (ret != 0)
602     LOG(ERROR) << "Bad return code " << ret << " from KWallet writeEntry";
603   return ret == 0;
604 }
605
606 bool NativeBackendKWallet::RemoveLoginsBetween(
607     base::Time delete_begin,
608     base::Time delete_end,
609     TimestampToCompare date_to_compare,
610     password_manager::PasswordStoreChangeList* changes) {
611   DCHECK(changes);
612   changes->clear();
613   int wallet_handle = WalletHandle();
614   if (wallet_handle == kInvalidKWalletHandle)
615     return false;
616
617   // We could probably also use readEntryList here.
618   std::vector<std::string> realm_list;
619   {
620     dbus::MethodCall method_call(kKWalletInterface, "entryList");
621     dbus::MessageWriter builder(&method_call);
622     builder.AppendInt32(wallet_handle);  // handle
623     builder.AppendString(folder_name_);  // folder
624     builder.AppendString(app_name_);     // appid
625     scoped_ptr<dbus::Response> response(kwallet_proxy_->CallMethodAndBlock(
626         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
627     if (!response.get()) {
628       LOG(ERROR) << "Error contacting kwalletd (entryList)";
629       return false;
630     }
631     dbus::MessageReader reader(response.get());
632     dbus::MessageReader array(response.get());
633     if (!reader.PopArray(&array)) {
634       LOG(ERROR) << "Error reading response from kwalletd (entryList): "
635                  << response->ToString();
636       return false;
637     }
638     while (array.HasMoreData()) {
639       std::string realm;
640       if (!array.PopString(&realm)) {
641         LOG(ERROR) << "Error reading response from kwalletd (entryList): "
642                    << response->ToString();
643         return false;
644       }
645       realm_list.push_back(realm);
646     }
647   }
648
649   bool ok = true;
650   for (size_t i = 0; i < realm_list.size(); ++i) {
651     const std::string& signon_realm = realm_list[i];
652     dbus::MethodCall method_call(kKWalletInterface, "readEntry");
653     dbus::MessageWriter builder(&method_call);
654     builder.AppendInt32(wallet_handle);  // handle
655     builder.AppendString(folder_name_);  // folder
656     builder.AppendString(signon_realm);  // key
657     builder.AppendString(app_name_);     // appid
658     scoped_ptr<dbus::Response> response(kwallet_proxy_->CallMethodAndBlock(
659         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
660     if (!response.get()) {
661       LOG(ERROR) << "Error contacting kwalletd (readEntry)";
662       continue;
663     }
664     dbus::MessageReader reader(response.get());
665     const uint8_t* bytes = NULL;
666     size_t length = 0;
667     if (!reader.PopArrayOfBytes(&bytes, &length)) {
668       LOG(ERROR) << "Error reading response from kwalletd (readEntry): "
669                  << response->ToString();
670       continue;
671     }
672     if (!bytes || !CheckSerializedValue(bytes, length, signon_realm))
673       continue;
674
675     // Can't we all just agree on whether bytes are signed or not? Please?
676     Pickle pickle(reinterpret_cast<const char*>(bytes), length);
677     PasswordFormList all_forms;
678     DeserializeValue(signon_realm, pickle, &all_forms);
679
680     PasswordFormList kept_forms;
681     kept_forms.reserve(all_forms.size());
682     base::Time autofill::PasswordForm::*date_member =
683         date_to_compare == CREATION_TIMESTAMP
684             ? &autofill::PasswordForm::date_created
685             : &autofill::PasswordForm::date_synced;
686     for (size_t i = 0; i < all_forms.size(); ++i) {
687       if (delete_begin <= all_forms[i]->*date_member &&
688           (delete_end.is_null() || all_forms[i]->*date_member < delete_end)) {
689         changes->push_back(password_manager::PasswordStoreChange(
690             password_manager::PasswordStoreChange::REMOVE, *all_forms[i]));
691         delete all_forms[i];
692       } else {
693         kept_forms.push_back(all_forms[i]);
694       }
695     }
696
697     if (!SetLoginsList(kept_forms, signon_realm, wallet_handle)) {
698       ok = false;
699       changes->clear();
700     }
701     STLDeleteElements(&kept_forms);
702   }
703   return ok;
704 }
705
706 // static
707 void NativeBackendKWallet::SerializeValue(const PasswordFormList& forms,
708                                           Pickle* pickle) {
709   pickle->WriteInt(kPickleVersion);
710   pickle->WriteUInt64(forms.size());
711   for (PasswordFormList::const_iterator it = forms.begin();
712        it != forms.end(); ++it) {
713     const PasswordForm* form = *it;
714     pickle->WriteInt(form->scheme);
715     pickle->WriteString(form->origin.spec());
716     pickle->WriteString(form->action.spec());
717     pickle->WriteString16(form->username_element);
718     pickle->WriteString16(form->username_value);
719     pickle->WriteString16(form->password_element);
720     pickle->WriteString16(form->password_value);
721     pickle->WriteString16(form->submit_element);
722     pickle->WriteBool(form->ssl_valid);
723     pickle->WriteBool(form->preferred);
724     pickle->WriteBool(form->blacklisted_by_user);
725     pickle->WriteInt64(form->date_created.ToTimeT());
726     pickle->WriteInt(form->type);
727     pickle->WriteInt(form->times_used);
728     autofill::SerializeFormData(form->form_data, pickle);
729     pickle->WriteInt64(form->date_synced.ToInternalValue());
730   }
731 }
732
733 // static
734 bool NativeBackendKWallet::DeserializeValueSize(const std::string& signon_realm,
735                                                 const PickleIterator& init_iter,
736                                                 int version,
737                                                 bool size_32,
738                                                 bool warn_only,
739                                                 PasswordFormList* forms) {
740   PickleIterator iter = init_iter;
741
742   uint64_t count = 0;
743   if (size_32) {
744     uint32_t count_32 = 0;
745     if (!iter.ReadUInt32(&count_32)) {
746       LOG(ERROR) << "Failed to deserialize KWallet entry "
747                  << "(realm: " << signon_realm << ")";
748       return false;
749     }
750     count = count_32;
751   } else {
752     if (!iter.ReadUInt64(&count)) {
753       LOG(ERROR) << "Failed to deserialize KWallet entry "
754                  << "(realm: " << signon_realm << ")";
755       return false;
756     }
757   }
758
759   if (count > 0xFFFF) {
760     // Trying to pin down the cause of http://crbug.com/80728 (or fix it).
761     // This is a very large number of passwords to be saved for a single realm.
762     // It is almost certainly a corrupt pickle and not real data. Ignore it.
763     // This very well might actually be http://crbug.com/107701, so if we're
764     // reading an old pickle, we don't even log this the first time we try to
765     // read it. (That is, when we're reading the native architecture size.)
766     if (!warn_only) {
767       LOG(ERROR) << "Suspiciously large number of entries in KWallet entry "
768                  << "(" << count << "; realm: " << signon_realm << ")";
769     }
770     return false;
771   }
772
773   forms->reserve(forms->size() + count);
774   for (uint64_t i = 0; i < count; ++i) {
775     scoped_ptr<PasswordForm> form(new PasswordForm());
776     form->signon_realm.assign(signon_realm);
777
778     int scheme = 0;
779     int64 date_created = 0;
780     int type = 0;
781     // Note that these will be read back in the order listed due to
782     // short-circuit evaluation. This is important.
783     if (!iter.ReadInt(&scheme) ||
784         !ReadGURL(&iter, warn_only, &form->origin) ||
785         !ReadGURL(&iter, warn_only, &form->action) ||
786         !iter.ReadString16(&form->username_element) ||
787         !iter.ReadString16(&form->username_value) ||
788         !iter.ReadString16(&form->password_element) ||
789         !iter.ReadString16(&form->password_value) ||
790         !iter.ReadString16(&form->submit_element) ||
791         !iter.ReadBool(&form->ssl_valid) ||
792         !iter.ReadBool(&form->preferred) ||
793         !iter.ReadBool(&form->blacklisted_by_user) ||
794         !iter.ReadInt64(&date_created)) {
795       LogDeserializationWarning(version, signon_realm, warn_only);
796       return false;
797     }
798     form->scheme = static_cast<PasswordForm::Scheme>(scheme);
799     form->date_created = base::Time::FromTimeT(date_created);
800
801     if (version > 1) {
802       if (!iter.ReadInt(&type) ||
803           !iter.ReadInt(&form->times_used) ||
804           !autofill::DeserializeFormData(&iter, &form->form_data)) {
805         LogDeserializationWarning(version, signon_realm, false);
806         return false;
807       }
808       form->type = static_cast<PasswordForm::Type>(type);
809     }
810
811     if (version > 2) {
812       int64 date_synced = 0;
813       if (!iter.ReadInt64(&date_synced)) {
814         LogDeserializationWarning(version, signon_realm, false);
815         return false;
816       }
817       form->date_synced = base::Time::FromInternalValue(date_synced);
818     }
819
820     forms->push_back(form.release());
821   }
822
823   return true;
824 }
825
826 // static
827 void NativeBackendKWallet::DeserializeValue(const std::string& signon_realm,
828                                             const Pickle& pickle,
829                                             PasswordFormList* forms) {
830   PickleIterator iter(pickle);
831
832   int version = -1;
833   if (!iter.ReadInt(&version) ||
834       version < 0 || version > kPickleVersion) {
835     LOG(ERROR) << "Failed to deserialize KWallet entry "
836                << "(realm: " << signon_realm << ")";
837     return;
838   }
839
840   if (version > 0) {
841     // In current pickles, we expect 64-bit sizes. Failure is an error.
842     DeserializeValueSize(signon_realm, iter, version, false, false, forms);
843     return;
844   }
845
846   const size_t saved_forms_size = forms->size();
847   const bool size_32 = sizeof(size_t) == sizeof(uint32_t);
848   if (!DeserializeValueSize(
849           signon_realm, iter, version, size_32, true, forms)) {
850     // We failed to read the pickle using the native architecture of the system.
851     // Try again with the opposite architecture. Note that we do this even on
852     // 32-bit machines, in case we're reading a 64-bit pickle. (Probably rare,
853     // since mostly we expect upgrades, not downgrades, but both are possible.)
854     forms->resize(saved_forms_size);
855     DeserializeValueSize(signon_realm, iter, version, !size_32, false, forms);
856   }
857 }
858
859 int NativeBackendKWallet::WalletHandle() {
860   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
861
862   // Open the wallet.
863   // TODO(mdm): Are we leaking these handles? Find out.
864   int32_t handle = kInvalidKWalletHandle;
865   {
866     dbus::MethodCall method_call(kKWalletInterface, "open");
867     dbus::MessageWriter builder(&method_call);
868     builder.AppendString(wallet_name_);  // wallet
869     builder.AppendInt64(0);              // wid
870     builder.AppendString(app_name_);     // appid
871     scoped_ptr<dbus::Response> response(
872         kwallet_proxy_->CallMethodAndBlock(
873             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
874     if (!response.get()) {
875       LOG(ERROR) << "Error contacting kwalletd (open)";
876       return kInvalidKWalletHandle;
877     }
878     dbus::MessageReader reader(response.get());
879     if (!reader.PopInt32(&handle)) {
880       LOG(ERROR) << "Error reading response from kwalletd (open): "
881                  << response->ToString();
882       return kInvalidKWalletHandle;
883     }
884     if (handle == kInvalidKWalletHandle) {
885       LOG(ERROR) << "Error obtaining KWallet handle";
886       return kInvalidKWalletHandle;
887     }
888   }
889
890   // Check if our folder exists.
891   bool has_folder = false;
892   {
893     dbus::MethodCall method_call(kKWalletInterface, "hasFolder");
894     dbus::MessageWriter builder(&method_call);
895     builder.AppendInt32(handle);         // handle
896     builder.AppendString(folder_name_);  // folder
897     builder.AppendString(app_name_);     // appid
898     scoped_ptr<dbus::Response> response(
899         kwallet_proxy_->CallMethodAndBlock(
900             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
901     if (!response.get()) {
902       LOG(ERROR) << "Error contacting kwalletd (hasFolder)";
903       return kInvalidKWalletHandle;
904     }
905     dbus::MessageReader reader(response.get());
906     if (!reader.PopBool(&has_folder)) {
907       LOG(ERROR) << "Error reading response from kwalletd (hasFolder): "
908                  << response->ToString();
909       return kInvalidKWalletHandle;
910     }
911   }
912
913   // Create it if it didn't.
914   if (!has_folder) {
915     dbus::MethodCall method_call(kKWalletInterface, "createFolder");
916     dbus::MessageWriter builder(&method_call);
917     builder.AppendInt32(handle);         // handle
918     builder.AppendString(folder_name_);  // folder
919     builder.AppendString(app_name_);     // appid
920     scoped_ptr<dbus::Response> response(
921         kwallet_proxy_->CallMethodAndBlock(
922             &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
923     if (!response.get()) {
924       LOG(ERROR) << "Error contacting kwalletd (createFolder)";
925       return kInvalidKWalletHandle;
926     }
927     dbus::MessageReader reader(response.get());
928     bool success = false;
929     if (!reader.PopBool(&success)) {
930       LOG(ERROR) << "Error reading response from kwalletd (createFolder): "
931                  << response->ToString();
932       return kInvalidKWalletHandle;
933     }
934     if (!success) {
935       LOG(ERROR) << "Error creating KWallet folder";
936       return kInvalidKWalletHandle;
937     }
938   }
939
940   return handle;
941 }
942
943 std::string NativeBackendKWallet::GetProfileSpecificFolderName() const {
944   // Originally, the folder name was always just "Chrome Form Data".
945   // Now we use it to distinguish passwords for different profiles.
946   return base::StringPrintf("%s (%d)", kKWalletFolder, profile_id_);
947 }