diff --git a/src/cmd/synmacph/Launchd.plist b/src/cmd/synmacph/Launchd.plist
index d56cca85..9ac34f46 100644
--- a/src/cmd/synmacph/Launchd.plist
+++ b/src/cmd/synmacph/Launchd.plist
@@ -4,11 +4,9 @@
Label
synmacph
- RunAtLoad
-
- LaunchOnlyOnce
-
- KeepAlive
-
-
+ MachServices
+
+ synmacph
+
+
diff --git a/src/cmd/synmacph/synmacph.c b/src/cmd/synmacph/synmacph.c
index e8d117a1..f4026585 100644
--- a/src/cmd/synmacph/synmacph.c
+++ b/src/cmd/synmacph/synmacph.c
@@ -19,16 +19,78 @@
#include
#include
#include
+#include
+
+const char* const label = "synmacph";
+
+static void xpcEventHandler(xpc_connection_t connection, xpc_object_t event)
+{
+ syslog(LOG_NOTICE, "received event in helper");
+
+ xpc_type_t type = xpc_get_type(event);
+
+ if (type == XPC_TYPE_ERROR) {
+ if (event == XPC_ERROR_CONNECTION_INVALID) {
+ // the client process on the other end of the connection has either
+ // crashed or cancelled the connection. After receiving this error,
+ // the connection is in an invalid state, and you do not need to
+ // call xpc_connection_cancel(). Just tear down any associated state
+ // here.
+ }
+ else if (event == XPC_ERROR_TERMINATION_IMMINENT) {
+ // handle per-connection termination cleanup.
+ }
+ }
+ else {
+ xpc_connection_t remote = xpc_dictionary_get_remote_connection(event);
+
+ const char* command = xpc_dictionary_get_string(event, "request");
+ syslog(LOG_NOTICE, "received command in helper: %s", command);
+ system(command);
+
+ xpc_object_t reply = xpc_dictionary_create_reply(event);
+ xpc_dictionary_set_string(reply, "reply", "command has been executed");
+ xpc_connection_send_message(remote, reply);
+ xpc_release(reply);
+ }
+}
+
+static void xpcConnectionHandler(xpc_connection_t connection)
+{
+ syslog(LOG_NOTICE, "configuring message event handler for helper");
+
+ xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
+ xpcEventHandler(connection, event);
+ });
+
+ xpc_connection_resume(connection);
+}
int main(int argc, const char * argv[])
{
- #pragma unused(argc)
- #pragma unused(argv)
-
- system("sudo sqlite3 /Library/Application\\ Support/com.apple.TCC/TCC.db 'delete from access where client like \"%Synergy.app%\"'");
+#pragma unused(argc)
+#pragma unused(argv)
- (void) sleep(10);
+ xpc_connection_t service = xpc_connection_create_mach_service(
+ label,
+ dispatch_get_main_queue(),
+ XPC_CONNECTION_MACH_SERVICE_LISTENER);
+
+ if (!service) {
+ syslog(LOG_NOTICE, "failed to create service");
+ exit(EXIT_FAILURE);
+ }
+
+ syslog(LOG_NOTICE, "configuring connection event handler for helper");
+ xpc_connection_set_event_handler(service, ^(xpc_object_t connection) {
+ xpcConnectionHandler(connection);
+ });
+
+ xpc_connection_resume(service);
+
+ dispatch_main();
+
+ xpc_release(service);
return EXIT_SUCCESS;
}
-
diff --git a/src/gui/src/AXDatabaseCleaner.h b/src/gui/src/AXDatabaseCleaner.h
index 0f8b544b..51b25dc9 100644
--- a/src/gui/src/AXDatabaseCleaner.h
+++ b/src/gui/src/AXDatabaseCleaner.h
@@ -22,9 +22,12 @@ public:
AXDatabaseCleaner();
~AXDatabaseCleaner();
- void loadPrivilegeHelper();
+ bool loadPrivilegeHelper();
+ bool xpcConnect();
+ bool privilegeCommand(const char* command);
private:
class Private;
- Private* d;
+ Private* m_private;
+ bool m_waitForResponse;
};
diff --git a/src/gui/src/AXDatabaseCleaner.mm b/src/gui/src/AXDatabaseCleaner.mm
index 94368c29..f870dd78 100644
--- a/src/gui/src/AXDatabaseCleaner.mm
+++ b/src/gui/src/AXDatabaseCleaner.mm
@@ -21,6 +21,8 @@
#import
#endif
#import
+#import
+#import
const NSString* const label = @"synmacph";
@@ -28,29 +30,31 @@ class AXDatabaseCleaner::Private {
public:
NSAutoreleasePool* autoReleasePool;
AuthorizationRef authRef;
+ xpc_connection_t xpcConnection;
};
AXDatabaseCleaner::AXDatabaseCleaner()
{
- d = new Private;
+ m_private = new Private;
+ m_private->autoReleasePool = [[NSAutoreleasePool alloc] init];
- d->autoReleasePool = [[NSAutoreleasePool alloc] init];
+ m_waitForResponse = false;
}
AXDatabaseCleaner::~AXDatabaseCleaner()
{
- [d->autoReleasePool release];
- delete d;
+ [m_private->autoReleasePool release];
+ delete m_private;
}
-void AXDatabaseCleaner::loadPrivilegeHelper()
+bool AXDatabaseCleaner::loadPrivilegeHelper()
{
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 // mavericks
- OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &d->authRef);
+ OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &m_private->authRef);
if (status != errAuthorizationSuccess) {
assert(NO);
- d->authRef = NULL;
+ m_private->authRef = NULL;
}
AuthorizationItem authItem = {kSMRightBlessPrivilegedHelper, 0, NULL, 0};
@@ -63,13 +67,13 @@ void AXDatabaseCleaner::loadPrivilegeHelper()
BOOL result = NO;
NSError* error = nil;
- status = AuthorizationCopyRights(d->authRef, &authRights, kAuthorizationEmptyEnvironment, flags, NULL);
+ status = AuthorizationCopyRights(m_private->authRef, &authRights, kAuthorizationEmptyEnvironment, flags, NULL);
if (status != errAuthorizationSuccess) {
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
}
else {
CFErrorRef cfError;
- result = (BOOL)SMJobBless(kSMDomainSystemLaunchd, (CFStringRef)label, d->authRef, &cfError);
+ result = (BOOL)SMJobBless(kSMDomainSystemLaunchd, (CFStringRef)label, m_private->authRef, &cfError);
if (!result) {
error = CFBridgingRelease(cfError);
@@ -79,7 +83,79 @@ void AXDatabaseCleaner::loadPrivilegeHelper()
if (!result) {
assert(error != nil);
NSLog(@"bless error: domain= %@ / code= %d", [error domain], (int) [error code]);
+ return false;
}
-#endif
+ return true;
+}
+
+bool AXDatabaseCleaner::xpcConnect()
+{
+ const char *cStr = [label cStringUsingEncoding:NSASCIIStringEncoding];
+ m_private->xpcConnection = xpc_connection_create_mach_service(
+ cStr,
+ NULL,
+ XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
+
+ if (!m_private->xpcConnection) {
+ NSLog(@"failed to create xpc connection");
+ return false;
+ }
+
+ xpc_connection_set_event_handler(m_private->xpcConnection, ^(xpc_object_t event) {
+ xpc_type_t type = xpc_get_type(event);
+
+ if (type == XPC_TYPE_ERROR) {
+ if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
+ NSLog(@"xpc connection interupted");
+
+ }
+ else if (event == XPC_ERROR_CONNECTION_INVALID) {
+ NSLog(@"xpc connection invalid, releasing");
+ xpc_release(m_private->xpcConnection);
+ }
+ else {
+ NSLog(@"unexpected xpc connection error");
+ }
+ }
+ else {
+ NSLog(@"unexpected xpc connection event");
+ }
+ });
+
+ xpc_connection_resume(m_private->xpcConnection);
+
+ return true;
+}
+
+bool AXDatabaseCleaner::privilegeCommand(const char* command)
+{
+ xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
+ xpc_dictionary_set_string(message, "request", command);
+ m_waitForResponse = true;
+
+ xpc_connection_send_message_with_reply(
+ m_private->xpcConnection,
+ message,
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),
+ ^(xpc_object_t event) {
+ const char* response = xpc_dictionary_get_string(event, "reply");
+ NSLog(@"reply from helper tool: %s", response);
+ m_waitForResponse = false;
+ });
+
+ QTime time = QTime::currentTime();
+ time.start();
+
+ while (m_waitForResponse) {
+ sleep(1);
+ if (time.elapsed() > 10000) {
+ QMessageBox::critical(NULL, "Synergy",
+ QObject::tr("No response from helper tool.Restart Synergy may solve this problem."));
+ return false;
+ }
+ }
+#endif
+
+ return true;
}
diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp
index 12bbc852..af42ee4e 100644
--- a/src/gui/src/main.cpp
+++ b/src/gui/src/main.cpp
@@ -126,10 +126,19 @@ int waitForTray()
}
#if defined(Q_OS_MAC)
+bool settingsExist()
+{
+ QSettings settings;
+
+ //FIXME: check settings existance properly
+ int port = settings.value("port", -1).toInt();
+
+ return port == -1 ? false : true;
+}
+
bool checkMacAssistiveDevices()
{
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 // mavericks
-
// new in mavericks, applications are trusted individually
// with use of the accessibility api. this call will show a
// prompt which can show the security/privacy/accessibility
@@ -137,20 +146,56 @@ bool checkMacAssistiveDevices()
// show up there automatically, but will be unchecked.
const void* keys[] = { kAXTrustedCheckOptionPrompt };
- const void* falseValue[] = { kCFBooleanFalse };
const void* trueValue[] = { kCFBooleanTrue };
-
- CFDictionaryRef optionsWithoutPrompt = CFDictionaryCreate(NULL, keys, falseValue, 1, NULL, NULL);
+ const void* falseValue[] = { kCFBooleanFalse };
CFDictionaryRef optionsWithPrompt = CFDictionaryCreate(NULL, keys, trueValue, 1, NULL, NULL);
+ CFDictionaryRef optionsWithoutPrompt = CFDictionaryCreate(NULL, keys, falseValue, 1, NULL, NULL);
bool result;
result = AXIsProcessTrustedWithOptions(optionsWithoutPrompt);
- if (!result) {
- // call privilege help tool
- AXDatabaseCleaner axdc;
- axdc.loadPrivilegeHelper();
- result = AXIsProcessTrustedWithOptions(optionsWithPrompt);
+ // Synergy is not checked in accessibility
+ if (!result) {
+ // if setting doesn't exist, just skip helper tool
+ if (!settingsExist()) {
+ result = AXIsProcessTrustedWithOptions(optionsWithPrompt);
+ }
+ else {
+ int reply;
+ reply = QMessageBox::question(
+ NULL,
+ "Synergy",
+ "Synergy requires access to Assistive Devices, but was not allowed.\n\nShould Synergy attempt to fix this?",
+ QMessageBox::Yes|QMessageBox::Default,
+ QMessageBox::No|QMessageBox::Escape);
+
+ if (reply == QMessageBox::Yes) {
+ // call privilege help tool
+ AXDatabaseCleaner axdc;
+ result = axdc.loadPrivilegeHelper();
+ result = axdc.xpcConnect();
+
+ QMessageBox box;
+ box.setModal(false);
+ box.setStandardButtons(0);
+ box.setText("Please wait.");
+ box.resize(150, 10);
+ box.show();
+
+ const char* command = "sudo sqlite3 /Library/Application\\ Support/com.apple.TCC/TCC.db 'delete from access where client like \"%Synergy.app%\"'";
+ result = axdc.privilegeCommand(command);
+
+ box.hide();
+
+ if (result) {
+ QMessageBox::information(
+ NULL, "Synergy",
+ "Synergy helper tool is complete. Please tick the checkbox in Accessibility.");
+ }
+ }
+
+ result = AXIsProcessTrustedWithOptions(optionsWithPrompt);
+ }
}
CFRelease(optionsWithoutPrompt);