Home | History | Annotate | Download | only in notifier
      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 "sync/notifier/p2p_invalidator.h"
      6 
      7 #include <algorithm>
      8 #include <iterator>
      9 
     10 #include "base/json/json_reader.h"
     11 #include "base/json/json_writer.h"
     12 #include "base/logging.h"
     13 #include "base/values.h"
     14 #include "jingle/notifier/listener/push_client.h"
     15 #include "sync/notifier/invalidation_handler.h"
     16 #include "sync/notifier/invalidation_util.h"
     17 #include "sync/notifier/object_id_invalidation_map.h"
     18 
     19 namespace syncer {
     20 
     21 const char kSyncP2PNotificationChannel[] = "http://www.google.com/chrome/sync";
     22 
     23 namespace {
     24 
     25 const char kNotifySelf[] = "notifySelf";
     26 const char kNotifyOthers[] = "notifyOthers";
     27 const char kNotifyAll[] = "notifyAll";
     28 
     29 const char kSenderIdKey[] = "senderId";
     30 const char kNotificationTypeKey[] = "notificationType";
     31 const char kInvalidationsKey[] = "invalidations";
     32 
     33 }  // namespace
     34 
     35 std::string P2PNotificationTargetToString(P2PNotificationTarget target) {
     36   switch (target) {
     37     case NOTIFY_SELF:
     38       return kNotifySelf;
     39     case NOTIFY_OTHERS:
     40       return kNotifyOthers;
     41     case NOTIFY_ALL:
     42       return kNotifyAll;
     43     default:
     44       NOTREACHED();
     45       return std::string();
     46   }
     47 }
     48 
     49 P2PNotificationTarget P2PNotificationTargetFromString(
     50     const std::string& target_str) {
     51   if (target_str == kNotifySelf) {
     52     return NOTIFY_SELF;
     53   }
     54   if (target_str == kNotifyOthers) {
     55     return NOTIFY_OTHERS;
     56   }
     57   if (target_str == kNotifyAll) {
     58     return NOTIFY_ALL;
     59   }
     60   LOG(WARNING) << "Could not parse " << target_str;
     61   return NOTIFY_SELF;
     62 }
     63 
     64 P2PNotificationData::P2PNotificationData()
     65     : target_(NOTIFY_SELF) {}
     66 
     67 P2PNotificationData::P2PNotificationData(
     68     const std::string& sender_id,
     69     P2PNotificationTarget target,
     70     const ObjectIdInvalidationMap& invalidation_map)
     71     : sender_id_(sender_id),
     72       target_(target),
     73       invalidation_map_(invalidation_map) {}
     74 
     75 P2PNotificationData::~P2PNotificationData() {}
     76 
     77 bool P2PNotificationData::IsTargeted(const std::string& id) const {
     78   switch (target_) {
     79     case NOTIFY_SELF:
     80       return sender_id_ == id;
     81     case NOTIFY_OTHERS:
     82       return sender_id_ != id;
     83     case NOTIFY_ALL:
     84       return true;
     85     default:
     86       NOTREACHED();
     87       return false;
     88   }
     89 }
     90 
     91 const ObjectIdInvalidationMap&
     92 P2PNotificationData::GetIdInvalidationMap() const {
     93   return invalidation_map_;
     94 }
     95 
     96 bool P2PNotificationData::Equals(const P2PNotificationData& other) const {
     97   return
     98       (sender_id_ == other.sender_id_) &&
     99       (target_ == other.target_) &&
    100       (invalidation_map_ == other.invalidation_map_);
    101 }
    102 
    103 std::string P2PNotificationData::ToString() const {
    104   scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
    105   dict->SetString(kSenderIdKey, sender_id_);
    106   dict->SetString(kNotificationTypeKey,
    107                   P2PNotificationTargetToString(target_));
    108   dict->Set(kInvalidationsKey, invalidation_map_.ToValue().release());
    109   std::string json;
    110   base::JSONWriter::Write(dict.get(), &json);
    111   return json;
    112 }
    113 
    114 bool P2PNotificationData::ResetFromString(const std::string& str) {
    115   scoped_ptr<base::Value> data_value(base::JSONReader::Read(str));
    116   const base::DictionaryValue* data_dict = NULL;
    117   if (!data_value.get() || !data_value->GetAsDictionary(&data_dict)) {
    118     LOG(WARNING) << "Could not parse " << str << " as a dictionary";
    119     return false;
    120   }
    121   if (!data_dict->GetString(kSenderIdKey, &sender_id_)) {
    122     LOG(WARNING) << "Could not find string value for " << kSenderIdKey;
    123   }
    124   std::string target_str;
    125   if (!data_dict->GetString(kNotificationTypeKey, &target_str)) {
    126     LOG(WARNING) << "Could not find string value for "
    127                  << kNotificationTypeKey;
    128   }
    129   target_ = P2PNotificationTargetFromString(target_str);
    130   const base::ListValue* invalidation_map_list = NULL;
    131   if (!data_dict->GetList(kInvalidationsKey, &invalidation_map_list) ||
    132       !invalidation_map_.ResetFromValue(*invalidation_map_list)) {
    133     LOG(WARNING) << "Could not parse " << kInvalidationsKey;
    134   }
    135   return true;
    136 }
    137 
    138 P2PInvalidator::P2PInvalidator(scoped_ptr<notifier::PushClient> push_client,
    139                                const std::string& invalidator_client_id,
    140                                P2PNotificationTarget send_notification_target)
    141     : push_client_(push_client.Pass()),
    142       invalidator_client_id_(invalidator_client_id),
    143       logged_in_(false),
    144       notifications_enabled_(false),
    145       send_notification_target_(send_notification_target) {
    146   DCHECK(send_notification_target_ == NOTIFY_OTHERS ||
    147          send_notification_target_ == NOTIFY_ALL);
    148   push_client_->AddObserver(this);
    149 }
    150 
    151 P2PInvalidator::~P2PInvalidator() {
    152   DCHECK(thread_checker_.CalledOnValidThread());
    153   push_client_->RemoveObserver(this);
    154 }
    155 
    156 void P2PInvalidator::RegisterHandler(InvalidationHandler* handler) {
    157   DCHECK(thread_checker_.CalledOnValidThread());
    158   registrar_.RegisterHandler(handler);
    159 }
    160 
    161 void P2PInvalidator::UpdateRegisteredIds(InvalidationHandler* handler,
    162                                          const ObjectIdSet& ids) {
    163   DCHECK(thread_checker_.CalledOnValidThread());
    164   ObjectIdSet new_ids;
    165   const ObjectIdSet& old_ids = registrar_.GetRegisteredIds(handler);
    166   std::set_difference(ids.begin(), ids.end(),
    167                       old_ids.begin(), old_ids.end(),
    168                       std::inserter(new_ids, new_ids.end()),
    169                       ObjectIdLessThan());
    170   registrar_.UpdateRegisteredIds(handler, ids);
    171   const P2PNotificationData notification_data(
    172       invalidator_client_id_,
    173       send_notification_target_,
    174       ObjectIdInvalidationMap::InvalidateAll(ids));
    175   SendNotificationData(notification_data);
    176 }
    177 
    178 void P2PInvalidator::UnregisterHandler(InvalidationHandler* handler) {
    179   DCHECK(thread_checker_.CalledOnValidThread());
    180   registrar_.UnregisterHandler(handler);
    181 }
    182 
    183 InvalidatorState P2PInvalidator::GetInvalidatorState() const {
    184   DCHECK(thread_checker_.CalledOnValidThread());
    185   return registrar_.GetInvalidatorState();
    186 }
    187 
    188 void P2PInvalidator::UpdateCredentials(
    189     const std::string& email, const std::string& token) {
    190   DCHECK(thread_checker_.CalledOnValidThread());
    191   notifier::Subscription subscription;
    192   subscription.channel = kSyncP2PNotificationChannel;
    193   // There may be some subtle issues around case sensitivity of the
    194   // from field, but it doesn't matter too much since this is only
    195   // used in p2p mode (which is only used in testing).
    196   subscription.from = email;
    197   push_client_->UpdateSubscriptions(
    198       notifier::SubscriptionList(1, subscription));
    199   // If already logged in, the new credentials will take effect on the
    200   // next reconnection.
    201   push_client_->UpdateCredentials(email, token);
    202   logged_in_ = true;
    203 }
    204 
    205 void P2PInvalidator::SendInvalidation(const ObjectIdSet& ids) {
    206   DCHECK(thread_checker_.CalledOnValidThread());
    207   ObjectIdInvalidationMap invalidation_map =
    208       ObjectIdInvalidationMap::InvalidateAll(ids);
    209   const P2PNotificationData notification_data(
    210       invalidator_client_id_, send_notification_target_, invalidation_map);
    211   SendNotificationData(notification_data);
    212 }
    213 
    214 void P2PInvalidator::OnNotificationsEnabled() {
    215   DCHECK(thread_checker_.CalledOnValidThread());
    216   bool just_turned_on = (notifications_enabled_ == false);
    217   notifications_enabled_ = true;
    218   registrar_.UpdateInvalidatorState(INVALIDATIONS_ENABLED);
    219   if (just_turned_on) {
    220     const P2PNotificationData notification_data(
    221         invalidator_client_id_,
    222         NOTIFY_SELF,
    223         ObjectIdInvalidationMap::InvalidateAll(
    224             registrar_.GetAllRegisteredIds()));
    225     SendNotificationData(notification_data);
    226   }
    227 }
    228 
    229 void P2PInvalidator::OnNotificationsDisabled(
    230     notifier::NotificationsDisabledReason reason) {
    231   DCHECK(thread_checker_.CalledOnValidThread());
    232   registrar_.UpdateInvalidatorState(FromNotifierReason(reason));
    233 }
    234 
    235 void P2PInvalidator::OnIncomingNotification(
    236     const notifier::Notification& notification) {
    237   DCHECK(thread_checker_.CalledOnValidThread());
    238   DVLOG(1) << "Received notification " << notification.ToString();
    239   if (!logged_in_) {
    240     DVLOG(1) << "Not logged in yet -- not emitting notification";
    241     return;
    242   }
    243   if (!notifications_enabled_) {
    244     DVLOG(1) << "Notifications not on -- not emitting notification";
    245     return;
    246   }
    247   if (notification.channel != kSyncP2PNotificationChannel) {
    248     LOG(WARNING) << "Notification from unexpected source "
    249                  << notification.channel;
    250   }
    251   P2PNotificationData notification_data;
    252   if (!notification_data.ResetFromString(notification.data)) {
    253     LOG(WARNING) << "Could not parse notification data from "
    254                  << notification.data;
    255     notification_data = P2PNotificationData(
    256         invalidator_client_id_,
    257         NOTIFY_ALL,
    258         ObjectIdInvalidationMap::InvalidateAll(
    259             registrar_.GetAllRegisteredIds()));
    260   }
    261   if (!notification_data.IsTargeted(invalidator_client_id_)) {
    262     DVLOG(1) << "Not a target of the notification -- "
    263              << "not emitting notification";
    264     return;
    265   }
    266   registrar_.DispatchInvalidationsToHandlers(
    267       notification_data.GetIdInvalidationMap());
    268 }
    269 
    270 void P2PInvalidator::SendNotificationDataForTest(
    271     const P2PNotificationData& notification_data) {
    272   DCHECK(thread_checker_.CalledOnValidThread());
    273   SendNotificationData(notification_data);
    274 }
    275 
    276 void P2PInvalidator::SendNotificationData(
    277     const P2PNotificationData& notification_data) {
    278   DCHECK(thread_checker_.CalledOnValidThread());
    279   if (notification_data.GetIdInvalidationMap().Empty()) {
    280     DVLOG(1) << "Not sending XMPP notification with empty state map: "
    281              << notification_data.ToString();
    282     return;
    283   }
    284   notifier::Notification notification;
    285   notification.channel = kSyncP2PNotificationChannel;
    286   notification.data = notification_data.ToString();
    287   DVLOG(1) << "Sending XMPP notification: " << notification.ToString();
    288   push_client_->SendNotification(notification);
    289 }
    290 
    291 }  // namespace syncer
    292