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/printing/cloud_print/cloud_print_proxy_service.h"
6
7#include <stack>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/command_line.h"
13#include "base/file_util.h"
14#include "base/json/json_reader.h"
15#include "base/message_loop/message_loop.h"
16#include "base/metrics/histogram.h"
17#include "base/prefs/pref_service.h"
18#include "base/strings/utf_string_conversions.h"
19#include "chrome/browser/browser_process.h"
20#include "chrome/browser/chrome_notification_types.h"
21#include "chrome/browser/lifetime/application_lifetime.h"
22#include "chrome/browser/notifications/desktop_notification_service.h"
23#include "chrome/browser/notifications/notification.h"
24#include "chrome/browser/notifications/notification_ui_manager.h"
25#include "chrome/browser/profiles/profile.h"
26#include "chrome/browser/service_process/service_process_control.h"
27#include "chrome/common/chrome_switches.h"
28#include "chrome/common/cloud_print/cloud_print_proxy_info.h"
29#include "chrome/common/pref_names.h"
30#include "chrome/common/service_messages.h"
31#include "content/public/browser/browser_thread.h"
32#include "grit/generated_resources.h"
33#include "printing/backend/print_backend.h"
34#include "ui/base/l10n/l10n_util.h"
35
36using content::BrowserThread;
37
38CloudPrintProxyService::CloudPrintProxyService(Profile* profile)
39    : profile_(profile),
40      weak_factory_(this),
41      enforcing_connector_policy_(false) {
42}
43
44CloudPrintProxyService::~CloudPrintProxyService() {
45}
46
47void CloudPrintProxyService::Initialize() {
48  DCHECK_CURRENTLY_ON(BrowserThread::UI);
49  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
50                            ServiceProcessControl::SERVICE_EVENT_INITIALIZE,
51                            ServiceProcessControl::SERVICE_EVENT_MAX);
52  if (profile_->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) &&
53      (!profile_->GetPrefs()->GetString(prefs::kCloudPrintEmail).empty() ||
54       !profile_->GetPrefs()->GetBoolean(prefs::kCloudPrintProxyEnabled))) {
55    // If the cloud print proxy is enabled, or the policy preventing it from
56    // being enabled is set, establish a channel with the service process and
57    // update the status. This will check the policy when the status is sent
58    // back.
59    UMA_HISTOGRAM_ENUMERATION(
60        "CloudPrint.ServiceEvents",
61        ServiceProcessControl::SERVICE_EVENT_ENABLED_ON_LAUNCH,
62        ServiceProcessControl::SERVICE_EVENT_MAX);
63    RefreshStatusFromService();
64  }
65
66  pref_change_registrar_.Init(profile_->GetPrefs());
67  pref_change_registrar_.Add(
68      prefs::kCloudPrintProxyEnabled,
69      base::Bind(
70          base::IgnoreResult(
71              &CloudPrintProxyService::ApplyCloudPrintConnectorPolicy),
72          base::Unretained(this)));
73}
74
75void CloudPrintProxyService::RefreshStatusFromService() {
76  DCHECK_CURRENTLY_ON(BrowserThread::UI);
77  InvokeServiceTask(
78      base::Bind(&CloudPrintProxyService::RefreshCloudPrintProxyStatus,
79                 weak_factory_.GetWeakPtr()));
80}
81
82bool CloudPrintProxyService::EnforceCloudPrintConnectorPolicyAndQuit() {
83  DCHECK_CURRENTLY_ON(BrowserThread::UI);
84  enforcing_connector_policy_ = true;
85  if (ApplyCloudPrintConnectorPolicy())
86    return true;
87  return false;
88}
89
90void CloudPrintProxyService::EnableForUserWithRobot(
91    const std::string& robot_auth_code,
92    const std::string& robot_email,
93    const std::string& user_email,
94    const base::DictionaryValue& user_preferences) {
95  DCHECK_CURRENTLY_ON(BrowserThread::UI);
96  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
97                            ServiceProcessControl::SERVICE_EVENT_ENABLE,
98                            ServiceProcessControl::SERVICE_EVENT_MAX);
99  if (profile_->GetPrefs()->GetBoolean(prefs::kCloudPrintProxyEnabled)) {
100    InvokeServiceTask(
101        base::Bind(&CloudPrintProxyService::EnableCloudPrintProxyWithRobot,
102                   weak_factory_.GetWeakPtr(), robot_auth_code, robot_email,
103                   user_email, base::Owned(user_preferences.DeepCopy())));
104  }
105}
106
107void CloudPrintProxyService::DisableForUser() {
108  DCHECK_CURRENTLY_ON(BrowserThread::UI);
109  UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
110                            ServiceProcessControl::SERVICE_EVENT_DISABLE,
111                            ServiceProcessControl::SERVICE_EVENT_MAX);
112  InvokeServiceTask(
113      base::Bind(&CloudPrintProxyService::DisableCloudPrintProxy,
114                 weak_factory_.GetWeakPtr()));
115}
116
117bool CloudPrintProxyService::ApplyCloudPrintConnectorPolicy() {
118  DCHECK_CURRENTLY_ON(BrowserThread::UI);
119  if (!profile_->GetPrefs()->GetBoolean(prefs::kCloudPrintProxyEnabled)) {
120    std::string email =
121        profile_->GetPrefs()->GetString(prefs::kCloudPrintEmail);
122    if (!email.empty()) {
123      UMA_HISTOGRAM_ENUMERATION(
124          "CloudPrint.ServiceEvents",
125          ServiceProcessControl::SERVICE_EVENT_DISABLE_BY_POLICY,
126          ServiceProcessControl::SERVICE_EVENT_MAX);
127      DisableForUser();
128      profile_->GetPrefs()->SetString(prefs::kCloudPrintEmail, std::string());
129      if (enforcing_connector_policy_) {
130        base::MessageLoop::current()->PostTask(
131            FROM_HERE,
132            base::Bind(&CloudPrintProxyService::RefreshCloudPrintProxyStatus,
133                       weak_factory_.GetWeakPtr()));
134      }
135      return false;
136    } else if (enforcing_connector_policy_) {
137      base::MessageLoop::current()->PostTask(FROM_HERE,
138                                             base::MessageLoop::QuitClosure());
139    }
140  }
141  return true;
142}
143
144void CloudPrintProxyService::GetPrinters(const PrintersCallback& callback) {
145  DCHECK_CURRENTLY_ON(BrowserThread::UI);
146  if (!profile_->GetPrefs()->GetBoolean(prefs::kCloudPrintProxyEnabled))
147    return;
148
149  base::FilePath list_path(
150      CommandLine::ForCurrentProcess()->GetSwitchValuePath(
151          switches::kCloudPrintSetupProxy));
152  if (!list_path.empty()) {
153    std::string printers_json;
154    base::ReadFileToString(list_path, &printers_json);
155    scoped_ptr<base::Value> value(base::JSONReader::Read(printers_json));
156    base::ListValue* list = NULL;
157    std::vector<std::string> printers;
158    if (value && value->GetAsList(&list) && list) {
159      for (size_t i = 0; i < list->GetSize(); ++i) {
160        std::string printer;
161        if (list->GetString(i, &printer))
162          printers.push_back(printer);
163      }
164    }
165    UMA_HISTOGRAM_COUNTS_10000("CloudPrint.AvailablePrintersList",
166                               printers.size());
167    base::MessageLoop::current()->PostTask(FROM_HERE,
168                                           base::Bind(callback, printers));
169  } else {
170    InvokeServiceTask(
171        base::Bind(&CloudPrintProxyService::GetCloudPrintProxyPrinters,
172                   weak_factory_.GetWeakPtr(),
173                   callback));
174  }
175}
176
177void CloudPrintProxyService::GetCloudPrintProxyPrinters(
178    const PrintersCallback& callback) {
179  DCHECK_CURRENTLY_ON(BrowserThread::UI);
180  ServiceProcessControl* process_control = GetServiceProcessControl();
181  DCHECK(process_control->IsConnected());
182  process_control->GetPrinters(callback);
183}
184
185void CloudPrintProxyService::RefreshCloudPrintProxyStatus() {
186  DCHECK_CURRENTLY_ON(BrowserThread::UI);
187  ServiceProcessControl* process_control = GetServiceProcessControl();
188  DCHECK(process_control->IsConnected());
189  ServiceProcessControl::CloudPrintProxyInfoCallback callback = base::Bind(
190      &CloudPrintProxyService::ProxyInfoCallback, base::Unretained(this));
191  process_control->GetCloudPrintProxyInfo(callback);
192}
193
194void CloudPrintProxyService::EnableCloudPrintProxyWithRobot(
195    const std::string& robot_auth_code,
196    const std::string& robot_email,
197    const std::string& user_email,
198    const base::DictionaryValue* user_preferences) {
199  ServiceProcessControl* process_control = GetServiceProcessControl();
200  DCHECK(process_control->IsConnected());
201  process_control->Send(
202      new ServiceMsg_EnableCloudPrintProxyWithRobot(
203          robot_auth_code, robot_email, user_email, *user_preferences));
204  // Assume the IPC worked.
205  profile_->GetPrefs()->SetString(prefs::kCloudPrintEmail, user_email);
206}
207
208void CloudPrintProxyService::DisableCloudPrintProxy() {
209  ServiceProcessControl* process_control = GetServiceProcessControl();
210  DCHECK(process_control->IsConnected());
211  process_control->Send(new ServiceMsg_DisableCloudPrintProxy);
212  // Assume the IPC worked.
213  profile_->GetPrefs()->SetString(prefs::kCloudPrintEmail, std::string());
214}
215
216void CloudPrintProxyService::ProxyInfoCallback(
217    const cloud_print::CloudPrintProxyInfo& proxy_info) {
218  proxy_id_ = proxy_info.proxy_id;
219  profile_->GetPrefs()->SetString(
220      prefs::kCloudPrintEmail,
221      proxy_info.enabled ? proxy_info.email : std::string());
222  ApplyCloudPrintConnectorPolicy();
223}
224
225bool CloudPrintProxyService::InvokeServiceTask(const base::Closure& task) {
226  GetServiceProcessControl()->Launch(task, base::Closure());
227  return true;
228}
229
230ServiceProcessControl* CloudPrintProxyService::GetServiceProcessControl() {
231  return ServiceProcessControl::GetInstance();
232}
233