summaryrefslogtreecommitdiffstats
path: root/remoting/host/mac
diff options
context:
space:
mode:
authorlambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 05:03:16 +0000
committerlambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 05:03:16 +0000
commit3efb1b624c74fbdc04a81c4dd186606a097fbfe5 (patch)
treea043085ef675dc87e31ccbae5c8361c713703f6f /remoting/host/mac
parentc626c735a5aa0501f2f83f6f1f984465c0202e9c (diff)
downloadchromium_src-3efb1b624c74fbdc04a81c4dd186606a097fbfe5.zip
chromium_src-3efb1b624c74fbdc04a81c4dd186606a097fbfe5.tar.gz
chromium_src-3efb1b624c74fbdc04a81c4dd186606a097fbfe5.tar.bz2
Move remoting pref-pane files to mac/ folder.
Review URL: https://chromiumcodereview.appspot.com/11184031 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162652 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/host/mac')
-rw-r--r--remoting/host/mac/me2me_preference_pane-Info.plist36
-rw-r--r--remoting/host/mac/me2me_preference_pane.h146
-rw-r--r--remoting/host/mac/me2me_preference_pane.mm755
-rw-r--r--remoting/host/mac/me2me_preference_pane.xib364
-rw-r--r--remoting/host/mac/me2me_preference_pane_confirm_pin.h26
-rw-r--r--remoting/host/mac/me2me_preference_pane_confirm_pin.mm46
-rw-r--r--remoting/host/mac/me2me_preference_pane_confirm_pin.xib472
-rw-r--r--remoting/host/mac/me2me_preference_pane_disable.h20
-rw-r--r--remoting/host/mac/me2me_preference_pane_disable.mm30
-rw-r--r--remoting/host/mac/me2me_preference_pane_disable.xib242
10 files changed, 2137 insertions, 0 deletions
diff --git a/remoting/host/mac/me2me_preference_pane-Info.plist b/remoting/host/mac/me2me_preference_pane-Info.plist
new file mode 100644
index 0000000..1fab228
--- /dev/null
+++ b/remoting/host/mac/me2me_preference_pane-Info.plist
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>BUNDLE_ID</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>BUNDLE_NAME</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>VERSION_SHORT</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>VERSION_FULL</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>COPYRIGHT_INFO</string>
+ <key>NSMainNibFile</key>
+ <string>me2me_preference_pane</string>
+ <key>NSPrefPaneIconFile</key>
+ <string>chromoting128.png</string>
+ <key>NSPrefPaneIconLabel</key>
+ <string>PREF_PANE_ICON_LABEL</string>
+ <key>NSPrincipalClass</key>
+ <string>Me2MePreferencePane</string>
+</dict>
+</plist>
diff --git a/remoting/host/mac/me2me_preference_pane.h b/remoting/host/mac/me2me_preference_pane.h
new file mode 100644
index 0000000..d3cfb06
--- /dev/null
+++ b/remoting/host/mac/me2me_preference_pane.h
@@ -0,0 +1,146 @@
+// Copyright (c) 2012 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.
+
+#import <Cocoa/Cocoa.h>
+#import <PreferencePanes/PreferencePanes.h>
+#import <SecurityInterface/SFAuthorizationView.h>
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "third_party/jsoncpp/source/include/json/value.h"
+
+namespace remoting {
+
+// This is an implementation of JsonHostConfig which does not use code from
+// the "base" target, so it can be built for 64-bit on Mac OS X.
+
+// TODO(lambroslambrou): Once the "base" target has 64-bit support, remove this
+// implementation and use the one in remoting/host/json_host_config.h - see
+// http://crbug.com/128122.
+class JsonHostConfig {
+ public:
+ JsonHostConfig(const std::string& filename);
+ ~JsonHostConfig();
+
+ bool Read();
+ bool GetString(const std::string& path, std::string* out_value) const;
+ std::string GetSerializedData() const;
+
+ private:
+ Json::Value config_;
+ std::string filename_;
+
+ DISALLOW_COPY_AND_ASSIGN(JsonHostConfig);
+};
+
+}
+
+@class Me2MePreferencePaneConfirmPin;
+@class Me2MePreferencePaneDisable;
+
+@interface Me2MePreferencePane : NSPreferencePane {
+ Me2MePreferencePaneConfirmPin* confirm_pin_view_;
+ Me2MePreferencePaneDisable* disable_view_;
+
+ IBOutlet NSTextField* status_message_;
+ IBOutlet NSBox* box_;
+ IBOutlet SFAuthorizationView* authorization_view_;
+
+ // Holds the new proposed configuration if a temporary config file is
+ // present.
+ scoped_ptr<remoting::JsonHostConfig> config_;
+
+ NSTimer* service_status_timer_;
+
+ // These flags determine the UI state. These are computed in the
+ // update...Status methods.
+ BOOL is_service_running_;
+ BOOL is_pane_unlocked_;
+
+ // True if a new proposed config file has been loaded into memory.
+ BOOL have_new_config_;
+
+ // True if launchd has been instructed to stop the service and we are waiting
+ // for the operation to complete.
+ BOOL awaiting_service_stop_;
+
+ // True if a version-mismatch has been detected. If true, this causes all
+ // controls to be greyed out, and also prevents any config file from being
+ // deleted, pending a restart of the preference pane.
+ BOOL restart_pending_or_canceled_;
+}
+
+- (void)mainViewDidLoad;
+- (void)willSelect;
+- (void)didSelect;
+- (void)willUnselect;
+- (void)onDisable:(id)sender;
+- (void)applyConfiguration:(id)sender
+ pin:(NSString*)pin;
+- (void)onNewConfigFile:(NSNotification*)notification;
+- (void)refreshServiceStatus:(NSTimer*)timer;
+- (void)authorizationViewDidAuthorize:(SFAuthorizationView*)view;
+- (void)authorizationViewDidDeauthorize:(SFAuthorizationView*)view;
+- (void)updateServiceStatus;
+- (void)updateAuthorizationStatus;
+
+// Read any new config file if present. If a config file is successfully read,
+// this deletes the file and keeps the config data loaded in memory. If this
+// method is called a second time (when the file has been deleted), the current
+// config is remembered, so this method acts as a latch: it can change
+// |have_new_config_| from NO to YES, but never from YES to NO.
+//
+// This scheme means that this method can delete the file immediately (to avoid
+// leaving a stale file around in case of a crash), but this method can safely
+// be called multiple times without forgetting the loaded config. To explicitly
+// forget the current config, set |have_new_config_| to NO.
+//
+// This method should not be called if |restart_pending_or_canceled_| is YES,
+// since this would delete any config file.
+- (void)readNewConfig;
+
+// Update all UI controls according to any stored flags and loaded config.
+// This should be called after any sequence of operations that might change the
+// UI state.
+- (void)updateUI;
+
+// Alert the user to a generic error condition.
+- (void)showError;
+
+// Alert the user that the typed PIN is incorrect.
+- (void)showIncorrectPinMessage;
+
+// Save the new config to the system, and either start the service or inform
+// the currently-running service of the new config.
+- (void)applyNewServiceConfig;
+
+- (BOOL)runHelperAsRootWithCommand:(const char*)command
+ inputData:(const std::string&)input_data;
+- (BOOL)sendJobControlMessage:(const char*)launch_key;
+
+// Compare the version of the running pref-pane against the installed version.
+// If the versions are mismatched and the pref-pane is visible, disable the
+// pane to prevent interaction, and prompt the user to restart System
+// Preferences.
+//
+// This should be called on notification of a new config, and also in
+// |didSelect| when the pane becomes visible. The pane needs to be visible so
+// that the alert appears as a sheet over the pane (instead of a detached
+// window), which gives the user an appropriate context for the alert.
+//
+// In the case of a version-mismatch, the new config file should be kept until
+// System Preferences is restarted, or thrown away when the user cancels the
+// alert. This method sets the |restart_pending_or_canceled_| flag on
+// detecting version-mismatch.
+- (void)checkInstalledVersion;
+
+- (void)mismatchAlertDidEnd:(NSAlert*)alert
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void*)contextInfo;
+
+// Called when the user chooses OK when prompted to restart System Preferences.
+- (void)restartSystemPreferences;
+
+@end
diff --git a/remoting/host/mac/me2me_preference_pane.mm b/remoting/host/mac/me2me_preference_pane.mm
new file mode 100644
index 0000000..ad4ac3c
--- /dev/null
+++ b/remoting/host/mac/me2me_preference_pane.mm
@@ -0,0 +1,755 @@
+// Copyright (c) 2012 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.
+
+#import "remoting/host/mac/me2me_preference_pane.h"
+
+#import <Cocoa/Cocoa.h>
+#include <CommonCrypto/CommonHMAC.h>
+#include <errno.h>
+#include <launch.h>
+#import <PreferencePanes/PreferencePanes.h>
+#import <SecurityInterface/SFAuthorizationView.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <fstream>
+
+#include "base/eintr_wrapper.h"
+#include "base/mac/scoped_launch_data.h"
+#include "base/memory/scoped_ptr.h"
+#include "remoting/host/constants_mac.h"
+#include "remoting/host/host_config.h"
+#import "remoting/host/mac/me2me_preference_pane_confirm_pin.h"
+#import "remoting/host/mac/me2me_preference_pane_disable.h"
+#include "third_party/jsoncpp/source/include/json/reader.h"
+#include "third_party/jsoncpp/source/include/json/writer.h"
+#include "third_party/modp_b64/modp_b64.h"
+
+namespace {
+
+bool GetTemporaryConfigFilePath(std::string* path) {
+ NSString* filename = NSTemporaryDirectory();
+ if (filename == nil)
+ return false;
+
+ *path = [[NSString stringWithFormat:@"%@/%s",
+ filename, remoting::kHostConfigFileName] UTF8String];
+ return true;
+}
+
+bool IsConfigValid(const remoting::JsonHostConfig* config) {
+ std::string value;
+ return (config->GetString(remoting::kHostIdConfigPath, &value) &&
+ config->GetString(remoting::kHostSecretHashConfigPath, &value) &&
+ config->GetString(remoting::kXmppLoginConfigPath, &value));
+}
+
+bool IsPinValid(const std::string& pin, const std::string& host_id,
+ const std::string& host_secret_hash) {
+ // TODO(lambroslambrou): Once the "base" target supports building for 64-bit
+ // on Mac OS X, remove this code and replace it with |VerifyHostPinHash()|
+ // from host/pin_hash.h.
+ size_t separator = host_secret_hash.find(':');
+ if (separator == std::string::npos)
+ return false;
+
+ std::string method = host_secret_hash.substr(0, separator);
+ if (method != "hmac") {
+ NSLog(@"Authentication method '%s' not supported", method.c_str());
+ return false;
+ }
+
+ std::string hash_base64 = host_secret_hash.substr(separator + 1);
+
+ // Convert |hash_base64| to |hash|, based on code from base/base64.cc.
+ int hash_base64_size = static_cast<int>(hash_base64.size());
+ std::string hash;
+ hash.resize(modp_b64_decode_len(hash_base64_size));
+
+ // modp_b64_decode_len() returns at least 1, so hash[0] is safe here.
+ int hash_size = modp_b64_decode(&(hash[0]), hash_base64.data(),
+ hash_base64_size);
+ if (hash_size < 0) {
+ NSLog(@"Failed to parse host_secret_hash");
+ return false;
+ }
+ hash.resize(hash_size);
+
+ std::string computed_hash;
+ computed_hash.resize(CC_SHA256_DIGEST_LENGTH);
+
+ CCHmac(kCCHmacAlgSHA256,
+ host_id.data(), host_id.size(),
+ pin.data(), pin.size(),
+ &(computed_hash[0]));
+
+ // Normally, a constant-time comparison function would be used, but it is
+ // unnecessary here as the "secret" is already readable by the user
+ // supplying input to this routine.
+ return computed_hash == hash;
+}
+
+} // namespace
+
+// These methods are copied from base/mac, but with the logging changed to use
+// NSLog().
+//
+// TODO(lambroslambrou): Once the "base" target supports building for 64-bit
+// on Mac OS X, remove these implementations and use the ones in base/mac.
+namespace base {
+namespace mac {
+
+// MessageForJob sends a single message to launchd with a simple dictionary
+// mapping |operation| to |job_label|, and returns the result of calling
+// launch_msg to send that message. On failure, returns NULL. The caller
+// assumes ownership of the returned launch_data_t object.
+launch_data_t MessageForJob(const std::string& job_label,
+ const char* operation) {
+ // launch_data_alloc returns something that needs to be freed.
+ ScopedLaunchData message(launch_data_alloc(LAUNCH_DATA_DICTIONARY));
+ if (!message) {
+ NSLog(@"launch_data_alloc");
+ return NULL;
+ }
+
+ // launch_data_new_string returns something that needs to be freed, but
+ // the dictionary will assume ownership when launch_data_dict_insert is
+ // called, so put it in a scoper and .release() it when given to the
+ // dictionary.
+ ScopedLaunchData job_label_launchd(launch_data_new_string(job_label.c_str()));
+ if (!job_label_launchd) {
+ NSLog(@"launch_data_new_string");
+ return NULL;
+ }
+
+ if (!launch_data_dict_insert(message,
+ job_label_launchd.release(),
+ operation)) {
+ return NULL;
+ }
+
+ return launch_msg(message);
+}
+
+pid_t PIDForJob(const std::string& job_label) {
+ ScopedLaunchData response(MessageForJob(job_label, LAUNCH_KEY_GETJOB));
+ if (!response) {
+ return -1;
+ }
+
+ launch_data_type_t response_type = launch_data_get_type(response);
+ if (response_type != LAUNCH_DATA_DICTIONARY) {
+ if (response_type == LAUNCH_DATA_ERRNO) {
+ NSLog(@"PIDForJob: error %d", launch_data_get_errno(response));
+ } else {
+ NSLog(@"PIDForJob: expected dictionary, got %d", response_type);
+ }
+ return -1;
+ }
+
+ launch_data_t pid_data = launch_data_dict_lookup(response,
+ LAUNCH_JOBKEY_PID);
+ if (!pid_data)
+ return 0;
+
+ if (launch_data_get_type(pid_data) != LAUNCH_DATA_INTEGER) {
+ NSLog(@"PIDForJob: expected integer");
+ return -1;
+ }
+
+ return launch_data_get_integer(pid_data);
+}
+
+OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization,
+ const char* tool_path,
+ AuthorizationFlags options,
+ const char** arguments,
+ FILE** pipe,
+ pid_t* pid) {
+ // pipe may be NULL, but this function needs one. In that case, use a local
+ // pipe.
+ FILE* local_pipe;
+ FILE** pipe_pointer;
+ if (pipe) {
+ pipe_pointer = pipe;
+ } else {
+ pipe_pointer = &local_pipe;
+ }
+
+ // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|,
+ // but it doesn't actually modify the arguments, and that type is kind of
+ // silly and callers probably aren't dealing with that. Put the cast here
+ // to make things a little easier on callers.
+ OSStatus status = AuthorizationExecuteWithPrivileges(authorization,
+ tool_path,
+ options,
+ (char* const*)arguments,
+ pipe_pointer);
+ if (status != errAuthorizationSuccess) {
+ return status;
+ }
+
+ long line_pid = -1;
+ size_t line_length = 0;
+ char* line_c = fgetln(*pipe_pointer, &line_length);
+ if (line_c) {
+ if (line_length > 0 && line_c[line_length - 1] == '\n') {
+ // line_c + line_length is the start of the next line if there is one.
+ // Back up one character.
+ --line_length;
+ }
+ std::string line(line_c, line_length);
+
+ // The version in base/mac used base::StringToInt() here.
+ line_pid = strtol(line.c_str(), NULL, 10);
+ if (line_pid == 0) {
+ NSLog(@"ExecuteWithPrivilegesAndGetPid: funny line: %s", line.c_str());
+ line_pid = -1;
+ }
+ } else {
+ NSLog(@"ExecuteWithPrivilegesAndGetPid: no line");
+ }
+
+ if (!pipe) {
+ fclose(*pipe_pointer);
+ }
+
+ if (pid) {
+ *pid = line_pid;
+ }
+
+ return status;
+}
+
+} // namespace mac
+} // namespace base
+
+namespace remoting {
+
+JsonHostConfig::JsonHostConfig(const std::string& filename)
+ : filename_(filename) {
+}
+
+JsonHostConfig::~JsonHostConfig() {
+}
+
+bool JsonHostConfig::Read() {
+ std::ifstream file(filename_.c_str());
+ Json::Reader reader;
+ return reader.parse(file, config_, false /* ignore comments */);
+}
+
+bool JsonHostConfig::GetString(const std::string& path,
+ std::string* out_value) const {
+ if (!config_.isObject())
+ return false;
+
+ if (!config_.isMember(path))
+ return false;
+
+ Json::Value value = config_[path];
+ if (!value.isString())
+ return false;
+
+ *out_value = value.asString();
+ return true;
+}
+
+std::string JsonHostConfig::GetSerializedData() const {
+ Json::FastWriter writer;
+ return writer.write(config_);
+}
+
+} // namespace remoting
+
+@implementation Me2MePreferencePane
+
+- (void)mainViewDidLoad {
+ [authorization_view_ setDelegate:self];
+ [authorization_view_ setString:kAuthorizationRightExecute];
+ [authorization_view_ setAutoupdate:YES
+ interval:60];
+ confirm_pin_view_ = [[Me2MePreferencePaneConfirmPin alloc] init];
+ [confirm_pin_view_ setDelegate:self];
+ disable_view_ = [[Me2MePreferencePaneDisable alloc] init];
+ [disable_view_ setDelegate:self];
+}
+
+- (void)willSelect {
+ have_new_config_ = NO;
+ awaiting_service_stop_ = NO;
+
+ NSDistributedNotificationCenter* center =
+ [NSDistributedNotificationCenter defaultCenter];
+ [center addObserver:self
+ selector:@selector(onNewConfigFile:)
+ name:[NSString stringWithUTF8String:remoting::kServiceName]
+ object:nil];
+
+ service_status_timer_ =
+ [[NSTimer scheduledTimerWithTimeInterval:2.0
+ target:self
+ selector:@selector(refreshServiceStatus:)
+ userInfo:nil
+ repeats:YES] retain];
+ [self updateServiceStatus];
+ [self updateAuthorizationStatus];
+
+ [self checkInstalledVersion];
+ if (!restart_pending_or_canceled_)
+ [self readNewConfig];
+
+ [self updateUI];
+}
+
+- (void)didSelect {
+ [self checkInstalledVersion];
+}
+
+- (void)willUnselect {
+ NSDistributedNotificationCenter* center =
+ [NSDistributedNotificationCenter defaultCenter];
+ [center removeObserver:self];
+
+ [service_status_timer_ invalidate];
+ [service_status_timer_ release];
+ service_status_timer_ = nil;
+
+ [self notifyPlugin:UPDATE_FAILED_NOTIFICATION_NAME];
+}
+
+- (void)applyConfiguration:(id)sender
+ pin:(NSString*)pin {
+ if (!have_new_config_) {
+ // It shouldn't be possible to hit the button if there is no config to
+ // apply, but check anyway just in case it happens somehow.
+ return;
+ }
+
+ // Ensure the authorization token is up-to-date before using it.
+ [self updateAuthorizationStatus];
+ [self updateUI];
+
+ std::string pin_utf8 = [pin UTF8String];
+ std::string host_id, host_secret_hash;
+ bool result = (config_->GetString(remoting::kHostIdConfigPath, &host_id) &&
+ config_->GetString(remoting::kHostSecretHashConfigPath,
+ &host_secret_hash));
+ if (!result) {
+ [self showError];
+ return;
+ }
+ if (!IsPinValid(pin_utf8, host_id, host_secret_hash)) {
+ [self showIncorrectPinMessage];
+ return;
+ }
+
+ [self applyNewServiceConfig];
+ [self updateUI];
+}
+
+- (void)onDisable:(id)sender {
+ // Ensure the authorization token is up-to-date before using it.
+ [self updateAuthorizationStatus];
+ [self updateUI];
+ if (!is_pane_unlocked_)
+ return;
+
+ if (![self runHelperAsRootWithCommand:"--disable"
+ inputData:""]) {
+ NSLog(@"Failed to run the helper tool");
+ [self showError];
+ [self notifyPlugin:UPDATE_FAILED_NOTIFICATION_NAME];
+ return;
+ }
+
+ // Stop the launchd job. This cannot easily be done by the helper tool,
+ // since the launchd job runs in the current user's context.
+ [self sendJobControlMessage:LAUNCH_KEY_STOPJOB];
+ awaiting_service_stop_ = YES;
+}
+
+- (void)onNewConfigFile:(NSNotification*)notification {
+ [self checkInstalledVersion];
+ if (!restart_pending_or_canceled_)
+ [self readNewConfig];
+
+ [self updateUI];
+}
+
+- (void)refreshServiceStatus:(NSTimer*)timer {
+ BOOL was_running = is_service_running_;
+ [self updateServiceStatus];
+ if (awaiting_service_stop_ && !is_service_running_) {
+ awaiting_service_stop_ = NO;
+ [self notifyPlugin:UPDATE_SUCCEEDED_NOTIFICATION_NAME];
+ }
+
+ if (was_running != is_service_running_)
+ [self updateUI];
+}
+
+- (void)authorizationViewDidAuthorize:(SFAuthorizationView*)view {
+ [self updateAuthorizationStatus];
+ [self updateUI];
+}
+
+- (void)authorizationViewDidDeauthorize:(SFAuthorizationView*)view {
+ [self updateAuthorizationStatus];
+ [self updateUI];
+}
+
+- (void)updateServiceStatus {
+ pid_t job_pid = base::mac::PIDForJob(remoting::kServiceName);
+ is_service_running_ = (job_pid > 0);
+}
+
+- (void)updateAuthorizationStatus {
+ is_pane_unlocked_ = [authorization_view_ updateStatus:authorization_view_];
+}
+
+- (void)readNewConfig {
+ std::string file;
+ if (!GetTemporaryConfigFilePath(&file)) {
+ NSLog(@"Failed to get path of configuration data.");
+ [self showError];
+ return;
+ }
+ if (access(file.c_str(), F_OK) != 0)
+ return;
+
+ scoped_ptr<remoting::JsonHostConfig> new_config_(
+ new remoting::JsonHostConfig(file));
+ if (!new_config_->Read()) {
+ // Report the error, because the file exists but couldn't be read. The
+ // case of non-existence is normal and expected.
+ NSLog(@"Error reading configuration data from %s", file.c_str());
+ [self showError];
+ return;
+ }
+ remove(file.c_str());
+ if (!IsConfigValid(new_config_.get())) {
+ NSLog(@"Invalid configuration data read.");
+ [self showError];
+ return;
+ }
+
+ config_.swap(new_config_);
+ have_new_config_ = YES;
+
+ [confirm_pin_view_ resetPin];
+}
+
+- (void)updateUI {
+ if (have_new_config_) {
+ [box_ setContentView:[confirm_pin_view_ view]];
+ } else {
+ [box_ setContentView:[disable_view_ view]];
+ }
+
+ // TODO(lambroslambrou): Show "enabled" and "disabled" in bold font.
+ NSString* message;
+ if (is_service_running_) {
+ if (have_new_config_) {
+ message = @"Please confirm your new PIN.";
+ } else {
+ message = @"Remote connections to this computer are enabled.";
+ }
+ } else {
+ if (have_new_config_) {
+ message = @"Remote connections to this computer are disabled. To enable "
+ "remote connections you must confirm your PIN.";
+ } else {
+ message = @"Remote connections to this computer are disabled.";
+ }
+ }
+ [status_message_ setStringValue:message];
+
+ std::string email;
+ if (config_.get()) {
+ bool result = config_->GetString(remoting::kXmppLoginConfigPath, &email);
+
+ // The config has already been checked by |IsConfigValid|.
+ if (!result) {
+ [self showError];
+ return;
+ }
+ }
+ [disable_view_ setEnabled:(is_pane_unlocked_ && is_service_running_ &&
+ !restart_pending_or_canceled_)];
+ [confirm_pin_view_ setEnabled:(is_pane_unlocked_ &&
+ !restart_pending_or_canceled_)];
+ [confirm_pin_view_ setEmail:[NSString stringWithUTF8String:email.c_str()]];
+ NSString* applyButtonText = is_service_running_ ? @"Confirm" : @"Enable";
+ [confirm_pin_view_ setButtonText:applyButtonText];
+
+ if (restart_pending_or_canceled_)
+ [authorization_view_ setEnabled:NO];
+}
+
+- (void)showError {
+ NSAlert* alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"An unexpected error occurred."];
+ [alert setInformativeText:@"Check the system log for more information."];
+ [alert setAlertStyle:NSWarningAlertStyle];
+ [alert beginSheetModalForWindow:[[self mainView] window]
+ modalDelegate:nil
+ didEndSelector:nil
+ contextInfo:nil];
+ [alert release];
+}
+
+- (void)showIncorrectPinMessage {
+ NSAlert* alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"Incorrect PIN entered."];
+ [alert setAlertStyle:NSWarningAlertStyle];
+ [alert beginSheetModalForWindow:[[self mainView] window]
+ modalDelegate:nil
+ didEndSelector:nil
+ contextInfo:nil];
+ [alert release];
+}
+
+- (void)applyNewServiceConfig {
+ [self updateServiceStatus];
+ std::string serialized_config = config_->GetSerializedData();
+ const char* command = is_service_running_ ? "--save-config" : "--enable";
+ if (![self runHelperAsRootWithCommand:command
+ inputData:serialized_config]) {
+ NSLog(@"Failed to run the helper tool");
+ [self showError];
+ return;
+ }
+
+ have_new_config_ = NO;
+
+ // Ensure the service is started.
+ if (!is_service_running_) {
+ [self sendJobControlMessage:LAUNCH_KEY_STARTJOB];
+ }
+
+ // Broadcast a distributed notification to inform the plugin that the
+ // configuration has been applied.
+ [self notifyPlugin:UPDATE_SUCCEEDED_NOTIFICATION_NAME];
+}
+
+- (BOOL)runHelperAsRootWithCommand:(const char*)command
+ inputData:(const std::string&)input_data {
+ AuthorizationRef authorization =
+ [[authorization_view_ authorization] authorizationRef];
+ if (!authorization) {
+ NSLog(@"Failed to obtain authorizationRef");
+ return NO;
+ }
+
+ // TODO(lambroslambrou): Replace the deprecated ExecuteWithPrivileges
+ // call with a launchd-based helper tool, which is more secure.
+ // http://crbug.com/120903
+ const char* arguments[] = { command, NULL };
+ FILE* pipe = NULL;
+ pid_t pid;
+ OSStatus status = base::mac::ExecuteWithPrivilegesAndGetPID(
+ authorization,
+ remoting::kHostHelperScriptPath,
+ kAuthorizationFlagDefaults,
+ arguments,
+ &pipe,
+ &pid);
+ if (status != errAuthorizationSuccess) {
+ NSLog(@"AuthorizationExecuteWithPrivileges: %s (%d)",
+ GetMacOSStatusErrorString(status), static_cast<int>(status));
+ return NO;
+ }
+ if (pid == -1) {
+ NSLog(@"Failed to get child PID");
+ if (pipe)
+ fclose(pipe);
+
+ return NO;
+ }
+ if (!pipe) {
+ NSLog(@"Unexpected NULL pipe");
+ return NO;
+ }
+
+ // Some cleanup is needed (closing the pipe and waiting for the child
+ // process), so flag any errors before returning.
+ BOOL error = NO;
+
+ if (!input_data.empty()) {
+ size_t bytes_written = fwrite(input_data.data(), sizeof(char),
+ input_data.size(), pipe);
+ // According to the fwrite manpage, a partial count is returned only if a
+ // write error has occurred.
+ if (bytes_written != input_data.size()) {
+ NSLog(@"Failed to write data to child process");
+ error = YES;
+ }
+ }
+
+ // In all cases, fclose() should be called with the returned FILE*. In the
+ // case of sending data to the child, this needs to be done before calling
+ // waitpid(), since the child reads until EOF on its stdin, so calling
+ // waitpid() first would result in deadlock.
+ if (fclose(pipe) != 0) {
+ NSLog(@"fclose failed with error %d", errno);
+ error = YES;
+ }
+
+ int exit_status;
+ pid_t wait_result = HANDLE_EINTR(waitpid(pid, &exit_status, 0));
+ if (wait_result != pid) {
+ NSLog(@"waitpid failed with error %d", errno);
+ error = YES;
+ }
+
+ // No more cleanup needed.
+ if (error)
+ return NO;
+
+ if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0) {
+ return YES;
+ } else {
+ NSLog(@"%s failed with exit status %d", remoting::kHostHelperScriptPath,
+ exit_status);
+ return NO;
+ }
+}
+
+- (BOOL)sendJobControlMessage:(const char*)launch_key {
+ base::mac::ScopedLaunchData response(
+ base::mac::MessageForJob(remoting::kServiceName, launch_key));
+ if (!response) {
+ NSLog(@"Failed to send message to launchd");
+ [self showError];
+ return NO;
+ }
+
+ // Expect a response of type LAUNCH_DATA_ERRNO.
+ launch_data_type_t type = launch_data_get_type(response.get());
+ if (type != LAUNCH_DATA_ERRNO) {
+ NSLog(@"launchd returned unexpected type: %d", type);
+ [self showError];
+ return NO;
+ }
+
+ int error = launch_data_get_errno(response.get());
+ if (error) {
+ NSLog(@"launchd returned error: %d", error);
+ [self showError];
+ return NO;
+ }
+ return YES;
+}
+
+- (void)notifyPlugin:(const char*)message {
+ NSDistributedNotificationCenter* center =
+ [NSDistributedNotificationCenter defaultCenter];
+ NSString* name = [NSString stringWithUTF8String:message];
+ [center postNotificationName:name
+ object:nil
+ userInfo:nil];
+}
+
+- (void)checkInstalledVersion {
+ // There's no point repeating the check if the pane has already been disabled
+ // from a previous call to this method. The pane only gets disabled when a
+ // version-mismatch has been detected here, so skip the check, but continue to
+ // handle the version-mismatch case.
+ if (!restart_pending_or_canceled_) {
+ NSBundle* this_bundle = [NSBundle bundleForClass:[self class]];
+ NSDictionary* this_plist = [this_bundle infoDictionary];
+ NSString* this_version = [this_plist objectForKey:@"CFBundleVersion"];
+
+ NSString* bundle_path = [this_bundle bundlePath];
+ NSString* plist_path =
+ [bundle_path stringByAppendingString:@"/Contents/Info.plist"];
+ NSDictionary* disk_plist =
+ [NSDictionary dictionaryWithContentsOfFile:plist_path];
+ NSString* disk_version = [disk_plist objectForKey:@"CFBundleVersion"];
+
+ if (disk_version == nil) {
+ NSLog(@"Failed to get installed version information");
+ [self showError];
+ return;
+ }
+
+ if ([this_version isEqualToString:disk_version])
+ return;
+
+ restart_pending_or_canceled_ = YES;
+ [self updateUI];
+ }
+
+ NSWindow* window = [[self mainView] window];
+ if (window == nil) {
+ // Defer the alert until |didSelect| is called, which happens just after
+ // the window is created.
+ return;
+ }
+
+ // This alert appears as a sheet over the top of the Chromoting pref-pane,
+ // underneath the title, so it's OK to refer to "this preference pane" rather
+ // than repeat the title "Chromoting" here.
+ NSAlert* alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"System update detected"];
+ [alert setInformativeText:@"To use this preference pane, System Preferences "
+ "needs to be restarted"];
+ [alert addButtonWithTitle:@"OK"];
+ NSButton* cancel_button = [alert addButtonWithTitle:@"Cancel"];
+ [cancel_button setKeyEquivalent:@"\e"];
+ [alert setAlertStyle:NSWarningAlertStyle];
+ [alert beginSheetModalForWindow:window
+ modalDelegate:self
+ didEndSelector:@selector(
+ mismatchAlertDidEnd:returnCode:contextInfo:)
+ contextInfo:nil];
+ [alert release];
+}
+
+- (void)mismatchAlertDidEnd:(NSAlert*)alert
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void*)contextInfo {
+ if (returnCode == NSAlertFirstButtonReturn) {
+ // OK was pressed.
+
+ // Dismiss the alert window here, so that the application will respond to
+ // the NSApp terminate: message.
+ [[alert window] orderOut:nil];
+ [self restartSystemPreferences];
+ } else {
+ // Cancel was pressed.
+
+ // If there is a new config file, delete it and notify the web-app of
+ // failure to apply the config. Otherwise, the web-app will remain in a
+ // spinning state until System Preferences eventually gets restarted and
+ // the user visits this pane again.
+ std::string file;
+ if (!GetTemporaryConfigFilePath(&file)) {
+ // There's no point in alerting the user here. The same error would
+ // happen when the pane is eventually restarted, so the user would be
+ // alerted at that time.
+ NSLog(@"Failed to get path of configuration data.");
+ return;
+ }
+
+ remove(file.c_str());
+ [self notifyPlugin:UPDATE_FAILED_NOTIFICATION_NAME];
+ }
+}
+
+- (void)restartSystemPreferences {
+ NSTask* task = [[NSTask alloc] init];
+ NSString* command =
+ [NSString stringWithUTF8String:remoting::kHostHelperScriptPath];
+ NSArray* arguments = [NSArray arrayWithObjects:@"--relaunch-prefpane", nil];
+ [task setLaunchPath:command];
+ [task setArguments:arguments];
+ [task setStandardInput:[NSPipe pipe]];
+ [task launch];
+ [task release];
+ [NSApp terminate:nil];
+}
+
+@end
diff --git a/remoting/host/mac/me2me_preference_pane.xib b/remoting/host/mac/me2me_preference_pane.xib
new file mode 100644
index 0000000..bf6363c
--- /dev/null
+++ b/remoting/host/mac/me2me_preference_pane.xib
@@ -0,0 +1,364 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">12C54</string>
+ <string key="IBDocument.InterfaceBuilderVersion">2549</string>
+ <string key="IBDocument.AppKitVersion">1187.34</string>
+ <string key="IBDocument.HIToolboxVersion">625.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">2549</string>
+ </object>
+ <array key="IBDocument.IntegratedClassDependencies">
+ <string>NSBox</string>
+ <string>NSCustomObject</string>
+ <string>NSCustomView</string>
+ <string>NSTextField</string>
+ <string>NSTextFieldCell</string>
+ <string>NSView</string>
+ <string>NSWindowTemplate</string>
+ </array>
+ <array key="IBDocument.PluginDependencies">
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </array>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+ <integer value="1" key="NS.object.0"/>
+ </object>
+ <array class="NSMutableArray" key="IBDocument.RootObjects" id="409411428">
+ <object class="NSCustomObject" id="294453543">
+ <string key="NSClassName">Me2MePreferencePane</string>
+ </object>
+ <object class="NSCustomObject" id="308860122">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="278121016">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="660800786">
+ <int key="NSWindowStyleMask">7</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{176, 764}, {667, 243}}</string>
+ <int key="NSWTFlags">1081606144</int>
+ <string key="NSWindowTitle">&lt;&lt; do not localize &gt;&gt;</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <object class="NSMutableString" key="NSViewClass">
+ <characters key="NS.bytes">View</characters>
+ </object>
+ <nil key="NSUserInterfaceItemIdentifier"/>
+ <string key="NSWindowContentMinSize">{224.66399999999999, 10}</string>
+ <object class="NSView" key="NSWindowView" id="1037298196">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <array class="NSMutableArray" key="NSSubviews">
+ <object class="NSCustomView" id="737813299">
+ <reference key="NSNextResponder" ref="1037298196"/>
+ <int key="NSvFlags">303</int>
+ <string key="NSFrame">{{20, 20}, {627, 47}}</string>
+ <reference key="NSSuperview" ref="1037298196"/>
+ <reference key="NSWindow"/>
+ <string key="NSClassName">SFAuthorizationView</string>
+ </object>
+ <object class="NSBox" id="1043481212">
+ <reference key="NSNextResponder" ref="1037298196"/>
+ <int key="NSvFlags">12</int>
+ <array class="NSMutableArray" key="NSSubviews">
+ <object class="NSView" id="659966804">
+ <reference key="NSNextResponder" ref="1043481212"/>
+ <int key="NSvFlags">274</int>
+ <string key="NSFrameSize">{633, 106}</string>
+ <reference key="NSSuperview" ref="1043481212"/>
+ <reference key="NSWindow"/>
+ </object>
+ </array>
+ <string key="NSFrame">{{17, 71}, {633, 106}}</string>
+ <reference key="NSSuperview" ref="1037298196"/>
+ <reference key="NSWindow"/>
+ <string key="NSOffsets">{0, 0}</string>
+ <object class="NSTextFieldCell" key="NSTitleCell">
+ <int key="NSCellFlags">67108864</int>
+ <int key="NSCellFlags2">0</int>
+ <string key="NSContents">Box</string>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">11</double>
+ <int key="NSfFlags">3100</int>
+ </object>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textBackgroundColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MCAwLjgwMDAwMDAxMTkAA</bytes>
+ </object>
+ </object>
+ <reference key="NSContentView" ref="659966804"/>
+ <int key="NSBorderType">0</int>
+ <int key="NSBoxType">0</int>
+ <int key="NSTitlePosition">0</int>
+ <bool key="NSTransparent">NO</bool>
+ </object>
+ <object class="NSTextField" id="129220543">
+ <reference key="NSNextResponder" ref="1037298196"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{17, 183}, {633, 40}}</string>
+ <reference key="NSSuperview" ref="1037298196"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="62099194">
+ <int key="NSCellFlags">67108864</int>
+ <int key="NSCellFlags2">4456448</int>
+ <string key="NSContents">Status</string>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">13</double>
+ <int key="NSfFlags">16</int>
+ </object>
+ <reference key="NSControlView" ref="129220543"/>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlTextColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ <bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+ </object>
+ </array>
+ <string key="NSFrameSize">{667, 243}</string>
+ <reference key="NSSuperview"/>
+ <reference key="NSWindow"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {2560, 1578}}</string>
+ <string key="NSMinSize">{224.66399999999999, 32}</string>
+ <string key="NSMaxSize">{10000000000000, 10000000000000}</string>
+ <bool key="NSWindowIsRestorable">YES</bool>
+ </object>
+ </array>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <array class="NSMutableArray" key="connectionRecords">
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">_window</string>
+ <reference key="source" ref="294453543"/>
+ <reference key="destination" ref="660800786"/>
+ </object>
+ <int key="connectionID">26</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">authorization_view_</string>
+ <reference key="source" ref="294453543"/>
+ <reference key="destination" ref="737813299"/>
+ </object>
+ <int key="connectionID">108</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">box_</string>
+ <reference key="source" ref="294453543"/>
+ <reference key="destination" ref="1043481212"/>
+ </object>
+ <int key="connectionID">126</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">status_message_</string>
+ <reference key="source" ref="294453543"/>
+ <reference key="destination" ref="129220543"/>
+ </object>
+ <int key="connectionID">127</int>
+ </object>
+ </array>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <array key="orderedObjects">
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <array key="object" id="0"/>
+ <reference key="children" ref="409411428"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="294453543"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="308860122"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">12</int>
+ <reference key="object" ref="660800786"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="1037298196"/>
+ </array>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">PrefPane</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">6</int>
+ <reference key="object" ref="1037298196"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="1043481212"/>
+ <reference ref="737813299"/>
+ <reference ref="129220543"/>
+ </array>
+ <reference key="parent" ref="660800786"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="278121016"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">105</int>
+ <reference key="object" ref="737813299"/>
+ <reference key="parent" ref="1037298196"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">123</int>
+ <reference key="object" ref="1043481212"/>
+ <reference key="parent" ref="1037298196"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">124</int>
+ <reference key="object" ref="129220543"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="62099194"/>
+ </array>
+ <reference key="parent" ref="1037298196"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">125</int>
+ <reference key="object" ref="62099194"/>
+ <reference key="parent" ref="129220543"/>
+ </object>
+ </array>
+ </object>
+ <dictionary class="NSMutableDictionary" key="flattenedProperties">
+ <string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="105.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="12.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="12.IBWindowTemplateEditedContentRect">{{1424, 1107}, {667, 243}}</string>
+ <string key="123.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="124.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="125.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="6.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
+ <nil key="activeLocalization"/>
+ <dictionary class="NSMutableDictionary" key="localizations"/>
+ <nil key="sourceID"/>
+ <int key="maxID">127</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <array class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <object class="IBPartialClassDescription">
+ <string key="className">Me2MePreferencePane</string>
+ <string key="superclassName">NSPreferencePane</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="authorization_view_">SFAuthorizationView</string>
+ <string key="box_">NSBox</string>
+ <string key="status_message_">NSTextField</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="authorization_view_">
+ <string key="name">authorization_view_</string>
+ <string key="candidateClassName">SFAuthorizationView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="box_">
+ <string key="name">box_</string>
+ <string key="candidateClassName">NSBox</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="status_message_">
+ <string key="name">status_message_</string>
+ <string key="candidateClassName">NSTextField</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/Me2MePreferencePane.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSPreferencePane</string>
+ <string key="superclassName">NSObject</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="_firstKeyView">NSView</string>
+ <string key="_initialKeyView">NSView</string>
+ <string key="_lastKeyView">NSView</string>
+ <string key="_window">NSWindow</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="_firstKeyView">
+ <string key="name">_firstKeyView</string>
+ <string key="candidateClassName">NSView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="_initialKeyView">
+ <string key="name">_initialKeyView</string>
+ <string key="candidateClassName">NSView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="_lastKeyView">
+ <string key="name">_lastKeyView</string>
+ <string key="candidateClassName">NSView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="_window">
+ <string key="name">_window</string>
+ <string key="candidateClassName">NSWindow</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/NSPreferencePane.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">SFAuthorizationView</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/SFAuthorizationView.h</string>
+ </object>
+ </object>
+ </array>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+ <real value="1050" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+ <real value="1070" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/remoting/host/mac/me2me_preference_pane_confirm_pin.h b/remoting/host/mac/me2me_preference_pane_confirm_pin.h
new file mode 100644
index 0000000..f16ada3
--- /dev/null
+++ b/remoting/host/mac/me2me_preference_pane_confirm_pin.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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.
+
+#import <Cocoa/Cocoa.h>
+
+@protocol Me2MePreferencePaneConfirmPinHandler <NSObject>
+- (void)applyConfiguration:(id)sender pin:(NSString*)pin;
+@end
+
+@interface Me2MePreferencePaneConfirmPin : NSViewController {
+ IBOutlet NSTextField* email_;
+ IBOutlet NSTextField* pin_;
+ IBOutlet NSButton* apply_button_;
+ id delegate_;
+}
+
+@property (retain) id delegate;
+
+- (void)setEmail:(NSString*)email;
+- (void)setButtonText:(NSString*)text;
+- (void)setEnabled:(BOOL)enabled;
+- (void)resetPin;
+- (IBAction)onApply:(id)sender;
+
+@end
diff --git a/remoting/host/mac/me2me_preference_pane_confirm_pin.mm b/remoting/host/mac/me2me_preference_pane_confirm_pin.mm
new file mode 100644
index 0000000..d83468a
--- /dev/null
+++ b/remoting/host/mac/me2me_preference_pane_confirm_pin.mm
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 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.
+
+#import "remoting/host/mac/me2me_preference_pane_confirm_pin.h"
+
+@implementation Me2MePreferencePaneConfirmPin
+
+@synthesize delegate = delegate_;
+
+- (id)init {
+ self = [super initWithNibName:@"me2me_preference_pane_confirm_pin"
+ bundle:[NSBundle bundleForClass:[self class]]];
+ return self;
+}
+
+- (void)dealloc {
+ [delegate_ release];
+ [super dealloc];
+}
+
+- (void)setEmail:(NSString*)email {
+ [email_ setStringValue:email];
+}
+
+- (void)setButtonText:(NSString*)text {
+ [apply_button_ setTitle:text];
+}
+
+- (void)setEnabled:(BOOL)enabled {
+ [apply_button_ setEnabled:enabled];
+ [pin_ setEnabled:enabled];
+ [[[self view] window] makeFirstResponder:pin_];
+ [apply_button_ setKeyEquivalent:@"\r"];
+}
+
+- (void)resetPin {
+ [pin_ setStringValue:@""];
+}
+
+- (void)onApply:(id)sender {
+ [delegate_ applyConfiguration:self
+ pin:[pin_ stringValue]];
+}
+
+@end
diff --git a/remoting/host/mac/me2me_preference_pane_confirm_pin.xib b/remoting/host/mac/me2me_preference_pane_confirm_pin.xib
new file mode 100644
index 0000000..01814e5
--- /dev/null
+++ b/remoting/host/mac/me2me_preference_pane_confirm_pin.xib
@@ -0,0 +1,472 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1070</int>
+ <string key="IBDocument.SystemVersion">12C54</string>
+ <string key="IBDocument.InterfaceBuilderVersion">2549</string>
+ <string key="IBDocument.AppKitVersion">1187.34</string>
+ <string key="IBDocument.HIToolboxVersion">625.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">2549</string>
+ </object>
+ <object class="NSArray" key="IBDocument.IntegratedClassDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSButton</string>
+ <string>NSButtonCell</string>
+ <string>NSCustomObject</string>
+ <string>NSCustomView</string>
+ <string>NSSecureTextField</string>
+ <string>NSSecureTextFieldCell</string>
+ <string>NSTextField</string>
+ <string>NSTextFieldCell</string>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+ <integer value="1" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">Me2MePreferencePaneConfirmPin</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSCustomView" id="533406559">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">268</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSButton" id="614032086">
+ <reference key="NSNextResponder" ref="533406559"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{413, 37}, {98, 25}}</string>
+ <reference key="NSSuperview" ref="533406559"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="245716473">
+ <int key="NSCellFlags">-2080374784</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Apply</string>
+ <object class="NSFont" key="NSSupport" id="141416998">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">13</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="614032086"/>
+ <int key="NSButtonFlags">-2038153216</int>
+ <int key="NSButtonFlags2">163</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">400</int>
+ <int key="NSPeriodicInterval">75</int>
+ </object>
+ <bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+ </object>
+ <object class="NSSecureTextField" id="367717657">
+ <reference key="NSNextResponder" ref="533406559"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{217, 39}, {188, 22}}</string>
+ <reference key="NSSuperview" ref="533406559"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSSecureTextFieldCell" key="NSCell" id="867461627">
+ <int key="NSCellFlags">342884416</int>
+ <int key="NSCellFlags2">272630784</int>
+ <string key="NSContents"/>
+ <reference key="NSSupport" ref="141416998"/>
+ <reference key="NSControlView" ref="367717657"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textBackgroundColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textColor</string>
+ <object class="NSColor" key="NSColor" id="469937612">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ <object class="NSArray" key="NSAllowedInputLocales">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSAllRomanInputSourcesLocaleIdentifier</string>
+ </object>
+ </object>
+ <bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+ </object>
+ <object class="NSTextField" id="48351972">
+ <reference key="NSNextResponder" ref="533406559"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{17, 44}, {195, 17}}</string>
+ <reference key="NSSuperview" ref="533406559"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="315240963">
+ <int key="NSCellFlags">68157504</int>
+ <int key="NSCellFlags2">71304192</int>
+ <string key="NSContents">PIN:</string>
+ <reference key="NSSupport" ref="141416998"/>
+ <reference key="NSControlView" ref="48351972"/>
+ <object class="NSColor" key="NSBackgroundColor" id="201615415">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor" id="167213699">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlTextColor</string>
+ <reference key="NSColor" ref="469937612"/>
+ </object>
+ </object>
+ <bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+ </object>
+ <object class="NSTextField" id="194991364">
+ <reference key="NSNextResponder" ref="533406559"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{214, 69}, {402, 17}}</string>
+ <reference key="NSSuperview" ref="533406559"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="362759672">
+ <int key="NSCellFlags">68157504</int>
+ <int key="NSCellFlags2">4195328</int>
+ <string key="NSContents">username</string>
+ <reference key="NSSupport" ref="141416998"/>
+ <reference key="NSControlView" ref="194991364"/>
+ <reference key="NSBackgroundColor" ref="201615415"/>
+ <reference key="NSTextColor" ref="167213699"/>
+ </object>
+ <bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+ </object>
+ <object class="NSTextField" id="511257665">
+ <reference key="NSNextResponder" ref="533406559"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{17, 69}, {195, 17}}</string>
+ <reference key="NSSuperview" ref="533406559"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="789178234">
+ <int key="NSCellFlags">68157504</int>
+ <int key="NSCellFlags2">71304192</int>
+ <string key="NSContents">User:</string>
+ <reference key="NSSupport" ref="141416998"/>
+ <reference key="NSControlView" ref="511257665"/>
+ <reference key="NSBackgroundColor" ref="201615415"/>
+ <reference key="NSTextColor" ref="167213699"/>
+ </object>
+ <bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+ </object>
+ </object>
+ <string key="NSFrameSize">{633, 106}</string>
+ <reference key="NSSuperview"/>
+ <reference key="NSWindow"/>
+ <string key="NSClassName">NSCustomView</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">email_</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="194991364"/>
+ </object>
+ <int key="connectionID">15</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">pin_</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="367717657"/>
+ </object>
+ <int key="connectionID">16</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">view</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="533406559"/>
+ </object>
+ <int key="connectionID">18</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">onApply:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="614032086"/>
+ </object>
+ <int key="connectionID">21</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">apply_button_</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="614032086"/>
+ </object>
+ <int key="connectionID">22</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="533406559"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="48351972"/>
+ <reference ref="511257665"/>
+ <reference ref="367717657"/>
+ <reference ref="614032086"/>
+ <reference ref="194991364"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">4</int>
+ <reference key="object" ref="511257665"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="789178234"/>
+ </object>
+ <reference key="parent" ref="533406559"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="789178234"/>
+ <reference key="parent" ref="511257665"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">6</int>
+ <reference key="object" ref="48351972"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="315240963"/>
+ </object>
+ <reference key="parent" ref="533406559"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">7</int>
+ <reference key="object" ref="315240963"/>
+ <reference key="parent" ref="48351972"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">8</int>
+ <reference key="object" ref="194991364"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="362759672"/>
+ </object>
+ <reference key="parent" ref="533406559"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">9</int>
+ <reference key="object" ref="362759672"/>
+ <reference key="parent" ref="194991364"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">10</int>
+ <reference key="object" ref="367717657"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="867461627"/>
+ </object>
+ <reference key="parent" ref="533406559"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">11</int>
+ <reference key="object" ref="867461627"/>
+ <reference key="parent" ref="367717657"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="614032086"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="245716473"/>
+ </object>
+ <reference key="parent" ref="533406559"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">20</int>
+ <reference key="object" ref="245716473"/>
+ <reference key="parent" ref="614032086"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.IBPluginDependency</string>
+ <string>-3.IBPluginDependency</string>
+ <string>1.IBPluginDependency</string>
+ <string>10.IBPluginDependency</string>
+ <string>11.IBPluginDependency</string>
+ <string>19.IBPluginDependency</string>
+ <string>20.IBPluginDependency</string>
+ <string>4.IBPluginDependency</string>
+ <string>5.IBPluginDependency</string>
+ <string>6.IBPluginDependency</string>
+ <string>7.IBPluginDependency</string>
+ <string>8.IBPluginDependency</string>
+ <string>9.IBPluginDependency</string>
+ </object>
+ <object class="NSArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <reference key="dict.values" ref="0"/>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <reference key="dict.values" ref="0"/>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">22</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">Me2MePreferencePaneConfirmPin</string>
+ <string key="superclassName">NSViewController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">onApply:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <string key="NS.key.0">onApply:</string>
+ <object class="IBActionInfo" key="NS.object.0">
+ <string key="name">onApply:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>apply_button_</string>
+ <string>email_</string>
+ <string>pin_</string>
+ </object>
+ <object class="NSArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSButton</string>
+ <string>NSTextField</string>
+ <string>NSTextField</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>apply_button_</string>
+ <string>email_</string>
+ <string>pin_</string>
+ </object>
+ <object class="NSArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBToOneOutletInfo">
+ <string key="name">apply_button_</string>
+ <string key="candidateClassName">NSButton</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">email_</string>
+ <string key="candidateClassName">NSTextField</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">pin_</string>
+ <string key="candidateClassName">NSTextField</string>
+ </object>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/Me2MePreferencePaneConfirmPin.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+ <real value="1070" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/remoting/host/mac/me2me_preference_pane_disable.h b/remoting/host/mac/me2me_preference_pane_disable.h
new file mode 100644
index 0000000..9871e7c
--- /dev/null
+++ b/remoting/host/mac/me2me_preference_pane_disable.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 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.
+
+#import <Cocoa/Cocoa.h>
+
+// This view-controller holds just a "Disable" button. It is shown when the
+// pref-pane is launched manually, or when the user has just confirmed their
+// PIN and applied a new configuration.
+@interface Me2MePreferencePaneDisable : NSViewController {
+ IBOutlet NSButton* disable_button_;
+ id delegate_;
+}
+
+@property (retain) id delegate;
+
+- (void)setEnabled:(BOOL)enabled;
+- (IBAction)onDisable:(id)sender;
+
+@end
diff --git a/remoting/host/mac/me2me_preference_pane_disable.mm b/remoting/host/mac/me2me_preference_pane_disable.mm
new file mode 100644
index 0000000..a2aa8b3
--- /dev/null
+++ b/remoting/host/mac/me2me_preference_pane_disable.mm
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 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.
+
+#import "remoting/host/mac/me2me_preference_pane_disable.h"
+
+@implementation Me2MePreferencePaneDisable
+
+@synthesize delegate = delegate_;
+
+- (id)init {
+ self = [super initWithNibName:@"me2me_preference_pane_disable"
+ bundle:[NSBundle bundleForClass:[self class]]];
+ return self;
+}
+
+- (void)dealloc {
+ [delegate_ release];
+ [super dealloc];
+}
+
+- (void)setEnabled:(BOOL)enabled {
+ [disable_button_ setEnabled:enabled];
+}
+
+- (void)onDisable:(id)sender {
+ [delegate_ onDisable:self];
+}
+
+@end
diff --git a/remoting/host/mac/me2me_preference_pane_disable.xib b/remoting/host/mac/me2me_preference_pane_disable.xib
new file mode 100644
index 0000000..e8d23cc
--- /dev/null
+++ b/remoting/host/mac/me2me_preference_pane_disable.xib
@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+ <data>
+ <int key="IBDocument.SystemTarget">1070</int>
+ <string key="IBDocument.SystemVersion">12C54</string>
+ <string key="IBDocument.InterfaceBuilderVersion">2549</string>
+ <string key="IBDocument.AppKitVersion">1187.34</string>
+ <string key="IBDocument.HIToolboxVersion">625.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">2549</string>
+ </object>
+ <object class="NSArray" key="IBDocument.IntegratedClassDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSButton</string>
+ <string>NSButtonCell</string>
+ <string>NSCustomObject</string>
+ <string>NSCustomView</string>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+ <integer value="1" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">Me2MePreferencePaneDisable</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSCustomView" id="533406559">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">268</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSButton" id="3286585">
+ <reference key="NSNextResponder" ref="533406559"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{200, 62}, {232, 25}}</string>
+ <reference key="NSSuperview" ref="533406559"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="1024006216">
+ <int key="NSCellFlags">-2080374784</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Disable Remote Connections</string>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">13</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="3286585"/>
+ <int key="NSButtonFlags">-2038153216</int>
+ <int key="NSButtonFlags2">163</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">400</int>
+ <int key="NSPeriodicInterval">75</int>
+ </object>
+ <bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+ </object>
+ </object>
+ <string key="NSFrameSize">{633, 106}</string>
+ <reference key="NSSuperview"/>
+ <reference key="NSWindow"/>
+ <string key="NSClassName">NSCustomView</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">view</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="533406559"/>
+ </object>
+ <int key="connectionID">16</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">disable_button_</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="3286585"/>
+ </object>
+ <int key="connectionID">21</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">onDisable:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="3286585"/>
+ </object>
+ <int key="connectionID">22</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="0">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="533406559"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="3286585"/>
+ </object>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="3286585"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1024006216"/>
+ </object>
+ <reference key="parent" ref="533406559"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">20</int>
+ <reference key="object" ref="1024006216"/>
+ <reference key="parent" ref="3286585"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.IBPluginDependency</string>
+ <string>-3.IBPluginDependency</string>
+ <string>1.IBPluginDependency</string>
+ <string>19.IBPluginDependency</string>
+ <string>20.IBPluginDependency</string>
+ </object>
+ <object class="NSArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <reference key="dict.values" ref="0"/>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference key="dict.sortedKeys" ref="0"/>
+ <reference key="dict.values" ref="0"/>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">22</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">Me2MePreferencePaneDisable</string>
+ <string key="superclassName">NSViewController</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">onDisable:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <string key="NS.key.0">onDisable:</string>
+ <object class="IBActionInfo" key="NS.object.0">
+ <string key="name">onDisable:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">disable_button_</string>
+ <string key="NS.object.0">NSButton</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">disable_button_</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">disable_button_</string>
+ <string key="candidateClassName">NSButton</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/Me2MePreferencePaneDisable.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+ <real value="1070" key="NS.object.0"/>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+ <integer value="3000" key="NS.object.0"/>
+ </object>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>