Add explicit |forceOnlineSignin| to user pod status
[chromium-blink-merge.git] / remoting / host / setup / daemon_controller_delegate_mac.mm
blobbb27e887ed1339a6005467b28bd0325154af71a1
1 // Copyright 2013 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.
5 #include <CoreFoundation/CoreFoundation.h>
7 #include "remoting/host/setup/daemon_controller_delegate_mac.h"
9 #include <launch.h>
10 #include <stdio.h>
11 #include <sys/types.h>
13 #include "base/basictypes.h"
14 #include "base/bind.h"
15 #include "base/compiler_specific.h"
16 #include "base/file_util.h"
17 #include "base/files/file_path.h"
18 #include "base/json/json_writer.h"
19 #include "base/logging.h"
20 #include "base/mac/foundation_util.h"
21 #include "base/mac/launchd.h"
22 #include "base/mac/mac_logging.h"
23 #include "base/mac/mac_util.h"
24 #include "base/mac/scoped_launch_data.h"
25 #include "base/time/time.h"
26 #include "base/values.h"
27 #include "remoting/host/constants_mac.h"
28 #include "remoting/host/json_host_config.h"
29 #include "remoting/host/usage_stats_consent.h"
31 namespace remoting {
33 DaemonControllerDelegateMac::DaemonControllerDelegateMac() {
36 DaemonControllerDelegateMac::~DaemonControllerDelegateMac() {
37   DeregisterForPreferencePaneNotifications();
40 DaemonController::State DaemonControllerDelegateMac::GetState() {
41   pid_t job_pid = base::mac::PIDForJob(kServiceName);
42   if (job_pid < 0) {
43     return DaemonController::STATE_NOT_INSTALLED;
44   } else if (job_pid == 0) {
45     // Service is stopped, or a start attempt failed.
46     return DaemonController::STATE_STOPPED;
47   } else {
48     return DaemonController::STATE_STARTED;
49   }
52 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateMac::GetConfig() {
53   base::FilePath config_path(kHostConfigFilePath);
54   JsonHostConfig host_config(config_path);
55   scoped_ptr<base::DictionaryValue> config;
57   if (host_config.Read()) {
58     config.reset(new base::DictionaryValue());
59     std::string value;
60     if (host_config.GetString(kHostIdConfigPath, &value))
61       config.get()->SetString(kHostIdConfigPath, value);
62     if (host_config.GetString(kXmppLoginConfigPath, &value))
63       config.get()->SetString(kXmppLoginConfigPath, value);
64   }
66   return config.Pass();
69 void DaemonControllerDelegateMac::SetConfigAndStart(
70     scoped_ptr<base::DictionaryValue> config,
71     bool consent,
72     const DaemonController::CompletionCallback& done) {
73   config->SetBoolean(kUsageStatsConsentConfigPath, consent);
74   std::string config_data;
75   base::JSONWriter::Write(config.get(), &config_data);
76   ShowPreferencePane(config_data, done);
79 void DaemonControllerDelegateMac::UpdateConfig(
80     scoped_ptr<base::DictionaryValue> config,
81     const DaemonController::CompletionCallback& done) {
82   base::FilePath config_file_path(kHostConfigFilePath);
83   JsonHostConfig config_file(config_file_path);
84   if (!config_file.Read()) {
85     done.Run(DaemonController::RESULT_FAILED);
86     return;
87   }
88   if (!config_file.CopyFrom(config.get())) {
89     LOG(ERROR) << "Failed to update configuration.";
90     done.Run(DaemonController::RESULT_FAILED);
91     return;
92   }
94   std::string config_data = config_file.GetSerializedData();
95   ShowPreferencePane(config_data, done);
98 void DaemonControllerDelegateMac::Stop(
99     const DaemonController::CompletionCallback& done) {
100   ShowPreferencePane("", done);
103 void DaemonControllerDelegateMac::SetWindow(void* window_handle) {
104   // noop
107 std::string DaemonControllerDelegateMac::GetVersion() {
108   std::string version = "";
109   std::string command_line = remoting::kHostHelperScriptPath;
110   command_line += " --host-version";
111   FILE* script_output = popen(command_line.c_str(), "r");
112   if (script_output) {
113     char buffer[100];
114     char* result = fgets(buffer, sizeof(buffer), script_output);
115     pclose(script_output);
116     if (result) {
117       // The string is guaranteed to be null-terminated, but probably contains
118       // a newline character, which we don't want.
119       for (int i = 0; result[i]; ++i) {
120         if (result[i] < ' ') {
121           result[i] = 0;
122           break;
123         }
124       }
125       version = result;
126     }
127   }
129   return version;
132 DaemonController::UsageStatsConsent
133 DaemonControllerDelegateMac::GetUsageStatsConsent() {
134   DaemonController::UsageStatsConsent consent;
135   consent.supported = true;
136   consent.allowed = false;
137   // set_by_policy is not yet supported.
138   consent.set_by_policy = false;
140   base::FilePath config_file_path(kHostConfigFilePath);
141   JsonHostConfig host_config(config_file_path);
142   if (host_config.Read()) {
143     host_config.GetBoolean(kUsageStatsConsentConfigPath, &consent.allowed);
144   }
146   return consent;
149 void DaemonControllerDelegateMac::ShowPreferencePane(
150     const std::string& config_data,
151     const DaemonController::CompletionCallback& done) {
152   if (DoShowPreferencePane(config_data)) {
153     RegisterForPreferencePaneNotifications(done);
154   } else {
155     done.Run(DaemonController::RESULT_FAILED);
156   }
159 // CFNotificationCenterAddObserver ties the thread on which distributed
160 // notifications are received to the one on which it is first called.
161 // This is safe because HostNPScriptObject::InvokeAsyncResultCallback
162 // bounces the invocation to the correct thread, so it doesn't matter
163 // which thread CompletionCallbacks are called on.
164 void DaemonControllerDelegateMac::RegisterForPreferencePaneNotifications(
165     const DaemonController::CompletionCallback& done) {
166   // We can only have one callback registered at a time. This is enforced by the
167   // UX flow of the web-app.
168   DCHECK(current_callback_.is_null());
169   current_callback_ = done;
171   CFNotificationCenterAddObserver(
172       CFNotificationCenterGetDistributedCenter(),
173       this,
174       &DaemonControllerDelegateMac::PreferencePaneCallback,
175       CFSTR(UPDATE_SUCCEEDED_NOTIFICATION_NAME),
176       NULL,
177       CFNotificationSuspensionBehaviorDeliverImmediately);
178   CFNotificationCenterAddObserver(
179       CFNotificationCenterGetDistributedCenter(),
180       this,
181       &DaemonControllerDelegateMac::PreferencePaneCallback,
182       CFSTR(UPDATE_FAILED_NOTIFICATION_NAME),
183       NULL,
184       CFNotificationSuspensionBehaviorDeliverImmediately);
187 void DaemonControllerDelegateMac::DeregisterForPreferencePaneNotifications() {
188   CFNotificationCenterRemoveObserver(
189       CFNotificationCenterGetDistributedCenter(),
190       this,
191       CFSTR(UPDATE_SUCCEEDED_NOTIFICATION_NAME),
192       NULL);
193   CFNotificationCenterRemoveObserver(
194       CFNotificationCenterGetDistributedCenter(),
195       this,
196       CFSTR(UPDATE_FAILED_NOTIFICATION_NAME),
197       NULL);
200 void DaemonControllerDelegateMac::PreferencePaneCallbackDelegate(
201     CFStringRef name) {
202   DaemonController::AsyncResult result = DaemonController::RESULT_FAILED;
203   if (CFStringCompare(name, CFSTR(UPDATE_SUCCEEDED_NOTIFICATION_NAME), 0) ==
204           kCFCompareEqualTo) {
205     result = DaemonController::RESULT_OK;
206   } else if (CFStringCompare(name, CFSTR(UPDATE_FAILED_NOTIFICATION_NAME), 0) ==
207           kCFCompareEqualTo) {
208     result = DaemonController::RESULT_FAILED;
209   } else {
210     LOG(WARNING) << "Ignoring unexpected notification: " << name;
211     return;
212   }
214   DCHECK(!current_callback_.is_null());
215   DaemonController::CompletionCallback done = current_callback_;
216   current_callback_.Reset();
217   done.Run(result);
219   DeregisterForPreferencePaneNotifications();
222 // static
223 bool DaemonControllerDelegateMac::DoShowPreferencePane(
224     const std::string& config_data) {
225   if (!config_data.empty()) {
226     base::FilePath config_path;
227     if (!base::GetTempDir(&config_path)) {
228       LOG(ERROR) << "Failed to get filename for saving configuration data.";
229       return false;
230     }
231     config_path = config_path.Append(kHostConfigFileName);
233     int written = file_util::WriteFile(config_path, config_data.data(),
234                                        config_data.size());
235     if (written != static_cast<int>(config_data.size())) {
236       LOG(ERROR) << "Failed to save configuration data to: "
237                  << config_path.value();
238       return false;
239     }
240   }
242   base::FilePath pane_path;
243   // TODO(lambroslambrou): Use NSPreferencePanesDirectory once we start
244   // building against SDK 10.6.
245   if (!base::mac::GetLocalDirectory(NSLibraryDirectory, &pane_path)) {
246     LOG(ERROR) << "Failed to get directory for local preference panes.";
247     return false;
248   }
249   pane_path = pane_path.Append("PreferencePanes").Append(kPrefPaneFileName);
251   FSRef pane_path_ref;
252   if (!base::mac::FSRefFromPath(pane_path.value(), &pane_path_ref)) {
253     LOG(ERROR) << "Failed to create FSRef";
254     return false;
255   }
256   OSStatus status = LSOpenFSRef(&pane_path_ref, NULL);
257   if (status != noErr) {
258     OSSTATUS_LOG(ERROR, status) << "LSOpenFSRef failed for path: "
259                                 << pane_path.value();
260     return false;
261   }
263   CFNotificationCenterRef center =
264       CFNotificationCenterGetDistributedCenter();
265   base::ScopedCFTypeRef<CFStringRef> service_name(CFStringCreateWithCString(
266       kCFAllocatorDefault, remoting::kServiceName, kCFStringEncodingUTF8));
267   CFNotificationCenterPostNotification(center, service_name, NULL, NULL,
268                                        TRUE);
269   return true;
272 // static
273 void DaemonControllerDelegateMac::PreferencePaneCallback(
274     CFNotificationCenterRef center,
275     void* observer,
276     CFStringRef name,
277     const void* object,
278     CFDictionaryRef user_info) {
279   DaemonControllerDelegateMac* self =
280       reinterpret_cast<DaemonControllerDelegateMac*>(observer);
281   if (!self) {
282     LOG(WARNING) << "Ignoring notification with NULL observer: " << name;
283     return;
284   }
286   self->PreferencePaneCallbackDelegate(name);
289 scoped_refptr<DaemonController> DaemonController::Create() {
290   scoped_ptr<DaemonController::Delegate> delegate(
291       new DaemonControllerDelegateMac());
292   return new DaemonController(delegate.Pass());
295 }  // namespace remoting