Commit cb58e5e7 authored by jrw's avatar jrw Committed by Commit bot

Added class to subscribe to GCD notifications over XMPP.

Instances of this class are created in an upcoming patch.

BUG=471928

Review URL: https://codereview.chromium.org/1123153002

Cr-Commit-Position: refs/heads/master@{#330628}
parent 8caa7631
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "remoting/protocol/channel_authenticator.h" #include "remoting/protocol/channel_authenticator.h"
#include "remoting/protocol/negotiating_host_authenticator.h" #include "remoting/protocol/negotiating_host_authenticator.h"
#include "remoting/protocol/token_validator.h" #include "remoting/protocol/token_validator.h"
#include "remoting/signaling/jid_util.h"
#include "third_party/webrtc/libjingle/xmllite/xmlelement.h" #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
namespace remoting { namespace remoting {
...@@ -115,12 +116,10 @@ scoped_ptr<Authenticator> Me2MeHostAuthenticatorFactory::CreateAuthenticator( ...@@ -115,12 +116,10 @@ scoped_ptr<Authenticator> Me2MeHostAuthenticatorFactory::CreateAuthenticator(
// where the host owner account does not have an email associated with it. // where the host owner account does not have an email associated with it.
// In those cases, the only guarantee we have is that JIDs for the same // In those cases, the only guarantee we have is that JIDs for the same
// account will have the same prefix. // account will have the same prefix.
size_t slash_pos = local_jid.find('/'); if (!SplitJidResource(local_jid, &remote_jid_prefix, nullptr)) {
if (slash_pos == std::string::npos) {
LOG(DFATAL) << "Invalid local JID:" << local_jid; LOG(DFATAL) << "Invalid local JID:" << local_jid;
return make_scoped_ptr(new RejectingAuthenticator()); return make_scoped_ptr(new RejectingAuthenticator());
} }
remote_jid_prefix = local_jid.substr(0, slash_pos);
} else { } else {
// TODO(rmsousa): This only works for cases where the JID prefix matches // TODO(rmsousa): This only works for cases where the JID prefix matches
// the host owner email. Figure out a way to verify the JID in other cases. // the host owner email. Figure out a way to verify the JID in other cases.
......
...@@ -201,6 +201,8 @@ ...@@ -201,6 +201,8 @@
'signaling/jingle_info_request.h', 'signaling/jingle_info_request.h',
'signaling/log_to_server.cc', 'signaling/log_to_server.cc',
'signaling/log_to_server.h', 'signaling/log_to_server.h',
'signaling/push_notification_subscriber.cc',
'signaling/push_notification_subscriber.h',
'signaling/server_log_entry.cc', 'signaling/server_log_entry.cc',
'signaling/server_log_entry.h', 'signaling/server_log_entry.h',
'signaling/signal_strategy.h', 'signaling/signal_strategy.h',
......
...@@ -254,6 +254,7 @@ ...@@ -254,6 +254,7 @@
'signaling/iq_sender_unittest.cc', 'signaling/iq_sender_unittest.cc',
'signaling/jid_util_unittest.cc', 'signaling/jid_util_unittest.cc',
'signaling/log_to_server_unittest.cc', 'signaling/log_to_server_unittest.cc',
'signaling/push_notification_subscriber_unittest.cc',
'signaling/server_log_entry_unittest.cc', 'signaling/server_log_entry_unittest.cc',
'signaling/server_log_entry_unittest.h', 'signaling/server_log_entry_unittest.h',
'signaling/xmpp_login_handler_unittest.cc', 'signaling/xmpp_login_handler_unittest.cc',
......
...@@ -9,15 +9,35 @@ ...@@ -9,15 +9,35 @@
namespace remoting { namespace remoting {
std::string NormalizeJid(const std::string& jid) { std::string NormalizeJid(const std::string& jid) {
size_t slash_pos = jid.find('/'); std::string bare_jid;
std::string resource;
if (SplitJidResource(jid, &bare_jid, &resource)) {
return base::StringToLowerASCII(bare_jid) + "/" + resource;
}
return base::StringToLowerASCII(bare_jid);
}
// In case there the jid doesn't have resource id covert the whole value to bool SplitJidResource(const std::string& full_jid,
// lower-case. std::string* bare_jid,
if (slash_pos == std::string::npos) std::string* resource) {
return base::StringToLowerASCII(jid); size_t slash_index = full_jid.find('/');
if (slash_index == std::string::npos) {
if (bare_jid) {
*bare_jid = full_jid;
}
if (resource) {
resource->clear();
}
return false;
}
return base::StringToLowerASCII(jid.substr(0, slash_pos)) + if (bare_jid) {
jid.substr(slash_pos); *bare_jid = full_jid.substr(0, slash_index);
}
if (resource) {
*resource = full_jid.substr(slash_index + 1);
}
return true;
} }
} // namespace remoting } // namespace remoting
...@@ -13,6 +13,17 @@ namespace remoting { ...@@ -13,6 +13,17 @@ namespace remoting {
// to lower-case. // to lower-case.
std::string NormalizeJid(const std::string& jid); std::string NormalizeJid(const std::string& jid);
// Splits a JID into a bare JID and a resource suffix. Either or both
// of |full_jid| and |resource| may be null. If |full_jid| is already
// a bare JID, |resource| is set to the empty string. Returns true of
// |full_jid| has a resource, false if not.
//
// e.g. "user@domain/resource" -> "user@domain", "resource", true
// "user@domain" -> "user@domain", "", false
bool SplitJidResource(const std::string& full_jid,
std::string* bare_jid,
std::string* resource);
} // namespace remoting } // namespace remoting
#endif // REMOTING_SIGNALING_JID_UTIL_H_ #endif // REMOTING_SIGNALING_JID_UTIL_H_
...@@ -16,4 +16,20 @@ TEST(JidUtil, NormalizeJid) { ...@@ -16,4 +16,20 @@ TEST(JidUtil, NormalizeJid) {
EXPECT_EQ(NormalizeJid("USER@DOMAIN.com/"), "user@domain.com/"); EXPECT_EQ(NormalizeJid("USER@DOMAIN.com/"), "user@domain.com/");
} }
TEST(JidUtil, SplitJidResource) {
std::string bare_jid;
std::string resource_suffix;
EXPECT_TRUE(SplitJidResource("user@domain/resource", nullptr, nullptr));
EXPECT_TRUE(
SplitJidResource("user@domain/resource", &bare_jid, &resource_suffix));
EXPECT_EQ(bare_jid, "user@domain");
EXPECT_EQ(resource_suffix, "resource");
EXPECT_FALSE(SplitJidResource("user@domain", nullptr, nullptr));
EXPECT_FALSE(SplitJidResource("user@domain", &bare_jid, &resource_suffix));
EXPECT_EQ(bare_jid, "user@domain");
EXPECT_EQ(resource_suffix, "");
}
} // namespace remoting } // namespace remoting
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/signaling/push_notification_subscriber.h"
#include "base/bind.h"
#include "base/callback.h"
#include "remoting/base/logging.h"
#include "remoting/signaling/iq_sender.h"
#include "remoting/signaling/jid_util.h"
#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
namespace remoting {
namespace {
const char kGooglePushNamespace[] = "google:push";
} // namespace
PushNotificationSubscriber::Subscription::Subscription() {
}
PushNotificationSubscriber::Subscription::~Subscription() {
}
PushNotificationSubscriber::PushNotificationSubscriber(
SignalStrategy* signal_strategy,
const SubscriptionList& subscriptions)
: signal_strategy_(signal_strategy), subscriptions_(subscriptions) {
signal_strategy_->AddListener(this);
}
PushNotificationSubscriber::~PushNotificationSubscriber() {
signal_strategy_->RemoveListener(this);
}
void PushNotificationSubscriber::OnSignalStrategyStateChange(
SignalStrategy::State state) {
if (state == SignalStrategy::CONNECTED) {
for (const Subscription& subscription : subscriptions_) {
Subscribe(subscription);
}
subscriptions_.clear(); // no longer needed
}
}
bool PushNotificationSubscriber::OnSignalStrategyIncomingStanza(
const buzz::XmlElement* stanza) {
// Ignore all XMPP stanzas.
return false;
}
void PushNotificationSubscriber::Subscribe(const Subscription& subscription) {
VLOG(0) << "Subscribing to push notifications on channel: "
<< subscription.channel << ".";
std::string bare_jid;
SplitJidResource(signal_strategy_->GetLocalJid(), &bare_jid, nullptr);
// Build a subscription request.
buzz::XmlElement* subscribe_element =
new buzz::XmlElement(buzz::QName(kGooglePushNamespace, "subscribe"));
buzz::XmlElement* item_element =
new buzz::XmlElement(buzz::QName(kGooglePushNamespace, "item"));
subscribe_element->AddElement(item_element);
item_element->SetAttr(buzz::QName(std::string(), "channel"),
subscription.channel);
item_element->SetAttr(buzz::QName(std::string(), "from"), subscription.from);
// Send the request.
iq_sender_.reset(new IqSender(signal_strategy_));
iq_request_ = iq_sender_->SendIq(
"set", bare_jid, make_scoped_ptr(subscribe_element),
base::Bind(&PushNotificationSubscriber::OnSubscriptionResult,
base::Unretained(this)));
}
void PushNotificationSubscriber::OnSubscriptionResult(
IqRequest* request,
const buzz::XmlElement* response) {
std::string response_type =
response->Attr(buzz::QName(std::string(), "type"));
if (response_type != "result") {
LOG(ERROR) << "Invalid response type for subscription: " << response_type;
}
// The IqSender and IqRequest are no longer needed after receiving a
// reply to the subscription request.
iq_request_.reset();
iq_sender_.reset();
}
} // namespace remoting
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef REMOTING_HOST_GCD_NOTIFICATION_SUBSCRIBER_H_
#define REMOTING_HOST_GCD_NOTIFICATION_SUBSCRIBER_H_
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "remoting/signaling/signal_strategy.h"
namespace remoting {
class IqSender;
class IqRequest;
// An object that subscribes to push notifications using an XMPP
// channel. The notifications themselves are ignored, but creating a
// subscription is necessary, e.g., for GCD to see a device as online.
class PushNotificationSubscriber : public SignalStrategy::Listener {
public:
struct Subscription {
Subscription();
~Subscription();
std::string channel;
std::string from;
};
typedef std::vector<Subscription> SubscriptionList;
PushNotificationSubscriber(SignalStrategy* signal_strategy,
const SubscriptionList& subscriptions);
~PushNotificationSubscriber() override;
private:
// SignalStrategy::Listener interface.
void OnSignalStrategyStateChange(SignalStrategy::State state) override;
bool OnSignalStrategyIncomingStanza(const buzz::XmlElement* stanza) override;
void Subscribe(const Subscription& subscription);
void OnSubscriptionResult(IqRequest* request,
const buzz::XmlElement* response);
SignalStrategy* signal_strategy_;
SubscriptionList subscriptions_;
scoped_ptr<IqSender> iq_sender_;
scoped_ptr<IqRequest> iq_request_;
DISALLOW_COPY_AND_ASSIGN(PushNotificationSubscriber);
};
} // namespace remoting
#endif // REMOTING_HOST_GCD_NOTIFICATION_SUBSCRIBER_H_
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/signaling/push_notification_subscriber.h"
#include "remoting/signaling/mock_signal_strategy.h"
#include "testing/gmock/include/gmock/gmock.h"
using testing::_;
using testing::AtLeast;
using testing::DoAll;
using testing::Return;
using testing::SaveArg;
namespace remoting {
TEST(PushNotificationSubscriberTest, Create) {
MockSignalStrategy signal_strategy;
EXPECT_CALL(signal_strategy, AddListener(_));
EXPECT_CALL(signal_strategy, RemoveListener(_));
PushNotificationSubscriber::SubscriptionList subscriptions;
PushNotificationSubscriber subscriber(&signal_strategy, subscriptions);
}
TEST(PushNotificationSubscriberTest, Subscribe) {
MockSignalStrategy signal_strategy;
EXPECT_CALL(signal_strategy, GetLocalJid())
.WillRepeatedly(Return("user@domain/resource"));
EXPECT_CALL(signal_strategy, GetNextId()).WillOnce(Return("next_id"));
EXPECT_CALL(signal_strategy, AddListener(_)).Times(AtLeast(1));
EXPECT_CALL(signal_strategy, RemoveListener(_)).Times(AtLeast(1));
buzz::XmlElement* sent_stanza;
EXPECT_CALL(signal_strategy, SendStanzaPtr(_))
.WillOnce(DoAll(SaveArg<0>(&sent_stanza), Return(true)));
PushNotificationSubscriber::Subscription subscription;
subscription.channel = "sub_channel";
subscription.from = "sub_from";
PushNotificationSubscriber::SubscriptionList subscriptions;
subscriptions.push_back(subscription);
PushNotificationSubscriber subscriber(&signal_strategy, subscriptions);
SignalStrategy::Listener* listener = &subscriber;
listener->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
EXPECT_EQ(
"<cli:iq type=\"set\" to=\"user@domain\" id=\"next_id\""
" xmlns:cli=\"jabber:client\">"
"<push:subscribe xmlns:push=\"google:push\">"
"<push:item channel=\"sub_channel\" from=\"sub_from\"/>"
"</push:subscribe>"
"</cli:iq>",
sent_stanza->Str());
delete sent_stanza;
}
} // namespace remoting
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment