1a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// found in the LICENSE file.
4a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
5a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "cloud_print/gcp20/prototype/cloud_print_xmpp_listener.h"
6a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
73240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch#include "base/bind.h"
8a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "base/logging.h"
93240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch#include "base/message_loop/message_loop.h"
103240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch#include "base/rand_util.h"
11a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "base/single_thread_task_runner.h"
12a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "cloud_print/gcp20/prototype/cloud_print_url_request_context_getter.h"
13a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "jingle/notifier/base/notifier_options.h"
14a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "jingle/notifier/listener/push_client.h"
15a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
16a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)namespace {
17a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
183240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdochconst int kUrgentPingInterval = 60;  // in seconds
193240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdochconst int kPingTimeout = 30;  // in seconds
203240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdochconst double kRandomDelayPercentage = 0.2;
213240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
22a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)const char kCloudPrintPushNotificationsSource[] = "cloudprint.google.com";
23a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
24a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// Splits message into two parts (e.g. '<device_id>/delete' will be splitted
25a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// into '<device_id>' and '/delete').
26a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)void TokenizeXmppMessage(const std::string& message, std::string* device_id,
27a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                         std::string* path) {
28a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  std::string::size_type pos = message.find('/');
29a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (pos != std::string::npos) {
30a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    *device_id = message.substr(0, pos);
31a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    *path = message.substr(pos);
32a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  } else {
33a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    *device_id = message;
34a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    path->clear();
35a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
36a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
37a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
38a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}  // namespace
39a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
40a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)CloudPrintXmppListener::CloudPrintXmppListener(
41a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    const std::string& robot_email,
423240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    int standard_ping_interval,
43a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
44a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    Delegate* delegate)
45a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    : robot_email_(robot_email),
463240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      standard_ping_interval_(standard_ping_interval),
473240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      ping_timeouts_posted_(0),
483240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      ping_responses_pending_(0),
493240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      ping_scheduled_(false),
50a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      context_getter_(new CloudPrintURLRequestContextGetter(task_runner)),
51a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      delegate_(delegate) {
52a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
53a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
54a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)CloudPrintXmppListener::~CloudPrintXmppListener() {
553240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  if (push_client_) {
563240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    push_client_->RemoveObserver(this);
573240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    push_client_.reset();
583240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  }
59a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
60a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
61a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)void CloudPrintXmppListener::Connect(const std::string& access_token) {
62a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  access_token_ = access_token;
633240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  ping_responses_pending_ = 0;
643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  ping_scheduled_ = false;
65a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
66a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  notifier::NotifierOptions options;
67a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  options.request_context_getter = context_getter_;
68a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  options.auth_mechanism = "X-OAUTH2";
69a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  options.try_ssltcp_first = true;
70a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  push_client_ = notifier::PushClient::CreateDefault(options);
71a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
72a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  notifier::Subscription subscription;
73a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  subscription.channel = kCloudPrintPushNotificationsSource;
74a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  subscription.from = kCloudPrintPushNotificationsSource;
75a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
76a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  notifier::SubscriptionList list;
77a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  list.push_back(subscription);
78a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
79a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  push_client_->UpdateSubscriptions(list);
80a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  push_client_->AddObserver(this);
81a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  push_client_->UpdateCredentials(robot_email_, access_token_);
82a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
83a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)void CloudPrintXmppListener::set_ping_interval(int interval) {
853240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  standard_ping_interval_ = interval;
863240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch}
873240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
88a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)void CloudPrintXmppListener::OnNotificationsEnabled() {
89a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  delegate_->OnXmppConnected();
903240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  SchedulePing();
91a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
92a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
93a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)void CloudPrintXmppListener::OnNotificationsDisabled(
94a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    notifier::NotificationsDisabledReason reason) {
95a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  switch (reason) {
96a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    case notifier::NOTIFICATION_CREDENTIALS_REJECTED:
97a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      delegate_->OnXmppAuthError();
98a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      break;
99a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    case notifier::TRANSIENT_NOTIFICATION_ERROR:
1003240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      Disconnect();
101a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      break;
102a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    default:
103a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      NOTREACHED() << "XMPP failed with unexpected reason code: " << reason;
104a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
105a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
106a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
107a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)void CloudPrintXmppListener::OnIncomingNotification(
108a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    const notifier::Notification& notification) {
109a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  std::string device_id;
110a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  std::string path;
111a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  TokenizeXmppMessage(notification.data, &device_id, &path);
112a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
113a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (path.empty() || path == "/") {
114a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    delegate_->OnXmppNewPrintJob(device_id);
115a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  } else if (path == "/update_settings") {
116a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    delegate_->OnXmppNewLocalSettings(device_id);
117a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  } else if (path == "/delete") {
118a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    delegate_->OnXmppDeleteNotification(device_id);
119a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  } else {
120a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    LOG(ERROR) << "Cannot parse XMPP notification: " << notification.data;
121a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
122a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
123a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
124a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)void CloudPrintXmppListener::OnPingResponse() {
1253240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  ping_responses_pending_ = 0;
1263240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  SchedulePing();
1273240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch}
1283240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
1293240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdochvoid CloudPrintXmppListener::Disconnect() {
1303240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  push_client_.reset();
1313240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  delegate_->OnXmppNetworkError();
1323240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch}
1333240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
1343240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdochvoid CloudPrintXmppListener::SchedulePing() {
1353240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  if (ping_scheduled_)
1363240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    return;
1373240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
1383240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  DCHECK_LE(kPingTimeout, kUrgentPingInterval);
1393240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  int delay = (ping_responses_pending_ > 0)
1403240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      ? kUrgentPingInterval - kPingTimeout
1413240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      : standard_ping_interval_;
1423240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
1433240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  delay += base::RandInt(0, delay*kRandomDelayPercentage);
1443240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
1453240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  base::MessageLoop::current()->PostDelayedTask(
1463240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      FROM_HERE,
1473240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      base::Bind(&CloudPrintXmppListener::SendPing, AsWeakPtr()),
1483240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      base::TimeDelta::FromSeconds(delay));
1493240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
1503240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  ping_scheduled_ = true;
1513240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch}
1523240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
1533240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdochvoid CloudPrintXmppListener::SendPing() {
1543240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  ping_scheduled_ = false;
1553240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
1563240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  DCHECK(push_client_);
1573240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  push_client_->SendPing();
1583240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  ++ping_responses_pending_;
1593240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
1603240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  base::MessageLoop::current()->PostDelayedTask(
1613240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        FROM_HERE,
1623240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        base::Bind(&CloudPrintXmppListener::OnPingTimeoutReached, AsWeakPtr()),
1633240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch        base::TimeDelta::FromSeconds(kPingTimeout));
1643240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  ++ping_timeouts_posted_;
1653240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch}
1663240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
1673240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdochvoid CloudPrintXmppListener::OnPingTimeoutReached() {
1683240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  DCHECK_GT(ping_timeouts_posted_, 0);
1693240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  --ping_timeouts_posted_;
1703240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  if (ping_timeouts_posted_ > 0)
1713240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    return;  // Fake (old) timeout.
1723240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch
1733240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  switch (ping_responses_pending_) {
1743240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    case 0:
1753240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      break;
1763240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    case 1:
1773240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      SchedulePing();
1783240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      break;
1793240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    case 2:
1803240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      Disconnect();
1813240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      break;
1823240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch    default:
1833240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch      NOTREACHED();
1843240926e260ce088908e02ac07a6cf7b0c0cbf44Ben Murdoch  }
185a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
186a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
187