Home | History | Annotate | Download | only in glue
      1 // Copyright (c) 2011 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/sync/glue/password_change_processor.h"
      6 
      7 #include <string>
      8 
      9 #include "base/string_util.h"
     10 #include "base/utf_string_conversions.h"
     11 #include "chrome/browser/password_manager/password_store.h"
     12 #include "chrome/browser/password_manager/password_store_change.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/browser/sync/glue/password_model_associator.h"
     15 #include "chrome/browser/sync/profile_sync_service.h"
     16 #include "chrome/browser/sync/protocol/password_specifics.pb.h"
     17 #include "content/common/notification_details.h"
     18 #include "content/common/notification_source.h"
     19 #include "content/common/notification_type.h"
     20 #include "webkit/glue/password_form.h"
     21 
     22 namespace browser_sync {
     23 
     24 PasswordChangeProcessor::PasswordChangeProcessor(
     25     PasswordModelAssociator* model_associator,
     26     PasswordStore* password_store,
     27     UnrecoverableErrorHandler* error_handler)
     28     : ChangeProcessor(error_handler),
     29       model_associator_(model_associator),
     30       password_store_(password_store),
     31       observing_(false),
     32       expected_loop_(MessageLoop::current()) {
     33   DCHECK(model_associator);
     34   DCHECK(error_handler);
     35 #if defined(OS_MACOSX)
     36   DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
     37 #else
     38   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
     39 #endif
     40   StartObserving();
     41 }
     42 
     43 PasswordChangeProcessor::~PasswordChangeProcessor() {
     44   DCHECK(expected_loop_ == MessageLoop::current());
     45 }
     46 
     47 void PasswordChangeProcessor::Observe(NotificationType type,
     48                                       const NotificationSource& source,
     49                                       const NotificationDetails& details) {
     50   DCHECK(expected_loop_ == MessageLoop::current());
     51   DCHECK(NotificationType::LOGINS_CHANGED == type);
     52   if (!observing_)
     53     return;
     54 
     55   DCHECK(running());
     56 
     57   sync_api::WriteTransaction trans(share_handle());
     58 
     59   sync_api::ReadNode password_root(&trans);
     60   if (!password_root.InitByTagLookup(kPasswordTag)) {
     61     error_handler()->OnUnrecoverableError(FROM_HERE,
     62         "Server did not create the top-level password node. "
     63         "We might be running against an out-of-date server.");
     64     return;
     65   }
     66 
     67   PasswordStoreChangeList* changes =
     68       Details<PasswordStoreChangeList>(details).ptr();
     69   for (PasswordStoreChangeList::iterator change = changes->begin();
     70        change != changes->end(); ++change) {
     71     std::string tag = PasswordModelAssociator::MakeTag(change->form());
     72     switch (change->type()) {
     73       case PasswordStoreChange::ADD: {
     74         sync_api::WriteNode sync_node(&trans);
     75         if (!sync_node.InitUniqueByCreation(syncable::PASSWORDS,
     76                                             password_root, tag)) {
     77           error_handler()->OnUnrecoverableError(FROM_HERE,
     78               "Failed to create password sync node.");
     79           return;
     80         }
     81 
     82         PasswordModelAssociator::WriteToSyncNode(change->form(), &sync_node);
     83         model_associator_->Associate(&tag, sync_node.GetId());
     84         break;
     85       }
     86       case PasswordStoreChange::UPDATE: {
     87         sync_api::WriteNode sync_node(&trans);
     88         int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
     89         if (sync_api::kInvalidId == sync_id) {
     90           error_handler()->OnUnrecoverableError(FROM_HERE,
     91               "Unexpected notification for: ");
     92           return;
     93         } else {
     94           if (!sync_node.InitByIdLookup(sync_id)) {
     95             error_handler()->OnUnrecoverableError(FROM_HERE,
     96                 "Password node lookup failed.");
     97             return;
     98           }
     99         }
    100 
    101         PasswordModelAssociator::WriteToSyncNode(change->form(), &sync_node);
    102         break;
    103       }
    104       case PasswordStoreChange::REMOVE: {
    105         sync_api::WriteNode sync_node(&trans);
    106         int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
    107         if (sync_api::kInvalidId == sync_id) {
    108           error_handler()->OnUnrecoverableError(FROM_HERE,
    109               "Unexpected notification");
    110           return;
    111         } else {
    112           if (!sync_node.InitByIdLookup(sync_id)) {
    113             error_handler()->OnUnrecoverableError(FROM_HERE,
    114                 "Password node lookup failed.");
    115             return;
    116           }
    117           model_associator_->Disassociate(sync_node.GetId());
    118           sync_node.Remove();
    119         }
    120         break;
    121       }
    122     }
    123   }
    124 }
    125 
    126 void PasswordChangeProcessor::ApplyChangesFromSyncModel(
    127     const sync_api::BaseTransaction* trans,
    128     const sync_api::SyncManager::ChangeRecord* changes,
    129     int change_count) {
    130   DCHECK(expected_loop_ == MessageLoop::current());
    131   if (!running())
    132     return;
    133 
    134   sync_api::ReadNode password_root(trans);
    135   if (!password_root.InitByTagLookup(kPasswordTag)) {
    136     error_handler()->OnUnrecoverableError(FROM_HERE,
    137         "Password root node lookup failed.");
    138     return;
    139   }
    140 
    141   DCHECK(deleted_passwords_.empty() && new_passwords_.empty() &&
    142          updated_passwords_.empty());
    143 
    144   for (int i = 0; i < change_count; ++i) {
    145     if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
    146         changes[i].action) {
    147       DCHECK(changes[i].specifics.HasExtension(sync_pb::password))
    148           << "Password specifics data not present on delete!";
    149       DCHECK(changes[i].extra.get());
    150       sync_api::SyncManager::ExtraPasswordChangeRecordData* extra =
    151           changes[i].extra.get();
    152       const sync_pb::PasswordSpecificsData& password = extra->unencrypted();
    153       webkit_glue::PasswordForm form;
    154       PasswordModelAssociator::CopyPassword(password, &form);
    155       deleted_passwords_.push_back(form);
    156       model_associator_->Disassociate(changes[i].id);
    157       continue;
    158     }
    159 
    160     sync_api::ReadNode sync_node(trans);
    161     if (!sync_node.InitByIdLookup(changes[i].id)) {
    162       error_handler()->OnUnrecoverableError(FROM_HERE,
    163           "Password node lookup failed.");
    164       return;
    165     }
    166 
    167     // Check that the changed node is a child of the passwords folder.
    168     DCHECK(password_root.GetId() == sync_node.GetParentId());
    169     DCHECK(syncable::PASSWORDS == sync_node.GetModelType());
    170 
    171     const sync_pb::PasswordSpecificsData& password_data =
    172         sync_node.GetPasswordSpecifics();
    173     webkit_glue::PasswordForm password;
    174     PasswordModelAssociator::CopyPassword(password_data, &password);
    175 
    176     if (sync_api::SyncManager::ChangeRecord::ACTION_ADD == changes[i].action) {
    177       std::string tag(PasswordModelAssociator::MakeTag(password));
    178       model_associator_->Associate(&tag, sync_node.GetId());
    179       new_passwords_.push_back(password);
    180     } else {
    181       DCHECK(sync_api::SyncManager::ChangeRecord::ACTION_UPDATE ==
    182              changes[i].action);
    183       updated_passwords_.push_back(password);
    184     }
    185   }
    186 }
    187 
    188 void PasswordChangeProcessor::CommitChangesFromSyncModel() {
    189   DCHECK(expected_loop_ == MessageLoop::current());
    190   if (!running())
    191     return;
    192   StopObserving();
    193 
    194   if (!model_associator_->WriteToPasswordStore(&new_passwords_,
    195                                                &updated_passwords_,
    196                                                &deleted_passwords_)) {
    197     error_handler()->OnUnrecoverableError(FROM_HERE, "Error writing passwords");
    198     return;
    199   }
    200 
    201   deleted_passwords_.clear();
    202   new_passwords_.clear();
    203   updated_passwords_.clear();
    204 
    205   StartObserving();
    206 }
    207 
    208 void PasswordChangeProcessor::StartImpl(Profile* profile) {
    209   DCHECK(expected_loop_ == MessageLoop::current());
    210   observing_ = true;
    211 }
    212 
    213 void PasswordChangeProcessor::StopImpl() {
    214   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    215   observing_ = false;
    216 }
    217 
    218 
    219 void PasswordChangeProcessor::StartObserving() {
    220   DCHECK(expected_loop_ == MessageLoop::current());
    221   notification_registrar_.Add(this,
    222                               NotificationType::LOGINS_CHANGED,
    223                               Source<PasswordStore>(password_store_));
    224 }
    225 
    226 void PasswordChangeProcessor::StopObserving() {
    227   DCHECK(expected_loop_ == MessageLoop::current());
    228   notification_registrar_.Remove(this,
    229                                  NotificationType::LOGINS_CHANGED,
    230                                  Source<PasswordStore>(password_store_));
    231 }
    232 
    233 }  // namespace browser_sync
    234