fixed helper tool:
used xpc communicate between gui and mhp made helper tool optional gave more feedback
This commit is contained in:
parent
ff42afc36c
commit
735fd08400
|
@ -4,11 +4,9 @@
|
||||||
<dict>
|
<dict>
|
||||||
<key>Label</key>
|
<key>Label</key>
|
||||||
<string>synmacph</string>
|
<string>synmacph</string>
|
||||||
<key>RunAtLoad</key>
|
<key>MachServices</key>
|
||||||
|
<dict>
|
||||||
|
<key>synmacph</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>LaunchOnlyOnce</key>
|
</dict></dict>
|
||||||
<true/>
|
|
||||||
<key>KeepAlive</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -19,16 +19,78 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <xpc/xpc.h>
|
||||||
|
|
||||||
|
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[])
|
int main(int argc, const char * argv[])
|
||||||
{
|
{
|
||||||
#pragma unused(argc)
|
#pragma unused(argc)
|
||||||
#pragma unused(argv)
|
#pragma unused(argv)
|
||||||
|
|
||||||
system("sudo sqlite3 /Library/Application\\ Support/com.apple.TCC/TCC.db 'delete from access where client like \"%Synergy.app%\"'");
|
xpc_connection_t service = xpc_connection_create_mach_service(
|
||||||
|
label,
|
||||||
|
dispatch_get_main_queue(),
|
||||||
|
XPC_CONNECTION_MACH_SERVICE_LISTENER);
|
||||||
|
|
||||||
(void) sleep(10);
|
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;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,12 @@ public:
|
||||||
AXDatabaseCleaner();
|
AXDatabaseCleaner();
|
||||||
~AXDatabaseCleaner();
|
~AXDatabaseCleaner();
|
||||||
|
|
||||||
void loadPrivilegeHelper();
|
bool loadPrivilegeHelper();
|
||||||
|
bool xpcConnect();
|
||||||
|
bool privilegeCommand(const char* command);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Private;
|
class Private;
|
||||||
Private* d;
|
Private* m_private;
|
||||||
|
bool m_waitForResponse;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#import <ServiceManagement/ServiceManagement.h>
|
#import <ServiceManagement/ServiceManagement.h>
|
||||||
#endif
|
#endif
|
||||||
#import <Security/Authorization.h>
|
#import <Security/Authorization.h>
|
||||||
|
#import <QMessageBox>
|
||||||
|
#import <QTime>
|
||||||
|
|
||||||
const NSString* const label = @"synmacph";
|
const NSString* const label = @"synmacph";
|
||||||
|
|
||||||
|
@ -28,29 +30,31 @@ class AXDatabaseCleaner::Private {
|
||||||
public:
|
public:
|
||||||
NSAutoreleasePool* autoReleasePool;
|
NSAutoreleasePool* autoReleasePool;
|
||||||
AuthorizationRef authRef;
|
AuthorizationRef authRef;
|
||||||
|
xpc_connection_t xpcConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
AXDatabaseCleaner::AXDatabaseCleaner()
|
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()
|
AXDatabaseCleaner::~AXDatabaseCleaner()
|
||||||
{
|
{
|
||||||
[d->autoReleasePool release];
|
[m_private->autoReleasePool release];
|
||||||
delete d;
|
delete m_private;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AXDatabaseCleaner::loadPrivilegeHelper()
|
bool AXDatabaseCleaner::loadPrivilegeHelper()
|
||||||
{
|
{
|
||||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 // mavericks
|
#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) {
|
if (status != errAuthorizationSuccess) {
|
||||||
assert(NO);
|
assert(NO);
|
||||||
d->authRef = NULL;
|
m_private->authRef = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthorizationItem authItem = {kSMRightBlessPrivilegedHelper, 0, NULL, 0};
|
AuthorizationItem authItem = {kSMRightBlessPrivilegedHelper, 0, NULL, 0};
|
||||||
|
@ -63,13 +67,13 @@ void AXDatabaseCleaner::loadPrivilegeHelper()
|
||||||
BOOL result = NO;
|
BOOL result = NO;
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
|
|
||||||
status = AuthorizationCopyRights(d->authRef, &authRights, kAuthorizationEmptyEnvironment, flags, NULL);
|
status = AuthorizationCopyRights(m_private->authRef, &authRights, kAuthorizationEmptyEnvironment, flags, NULL);
|
||||||
if (status != errAuthorizationSuccess) {
|
if (status != errAuthorizationSuccess) {
|
||||||
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
|
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
CFErrorRef cfError;
|
CFErrorRef cfError;
|
||||||
result = (BOOL)SMJobBless(kSMDomainSystemLaunchd, (CFStringRef)label, d->authRef, &cfError);
|
result = (BOOL)SMJobBless(kSMDomainSystemLaunchd, (CFStringRef)label, m_private->authRef, &cfError);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
error = CFBridgingRelease(cfError);
|
error = CFBridgingRelease(cfError);
|
||||||
|
@ -79,7 +83,79 @@ void AXDatabaseCleaner::loadPrivilegeHelper()
|
||||||
if (!result) {
|
if (!result) {
|
||||||
assert(error != nil);
|
assert(error != nil);
|
||||||
NSLog(@"bless error: domain= %@ / code= %d", [error domain], (int) [error code]);
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,10 +126,19 @@ int waitForTray()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_MAC)
|
#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()
|
bool checkMacAssistiveDevices()
|
||||||
{
|
{
|
||||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 // mavericks
|
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 // mavericks
|
||||||
|
|
||||||
// new in mavericks, applications are trusted individually
|
// new in mavericks, applications are trusted individually
|
||||||
// with use of the accessibility api. this call will show a
|
// with use of the accessibility api. this call will show a
|
||||||
// prompt which can show the security/privacy/accessibility
|
// prompt which can show the security/privacy/accessibility
|
||||||
|
@ -137,21 +146,57 @@ bool checkMacAssistiveDevices()
|
||||||
// show up there automatically, but will be unchecked.
|
// show up there automatically, but will be unchecked.
|
||||||
|
|
||||||
const void* keys[] = { kAXTrustedCheckOptionPrompt };
|
const void* keys[] = { kAXTrustedCheckOptionPrompt };
|
||||||
const void* falseValue[] = { kCFBooleanFalse };
|
|
||||||
const void* trueValue[] = { kCFBooleanTrue };
|
const void* trueValue[] = { kCFBooleanTrue };
|
||||||
|
const void* falseValue[] = { kCFBooleanFalse };
|
||||||
CFDictionaryRef optionsWithoutPrompt = CFDictionaryCreate(NULL, keys, falseValue, 1, NULL, NULL);
|
|
||||||
CFDictionaryRef optionsWithPrompt = CFDictionaryCreate(NULL, keys, trueValue, 1, NULL, NULL);
|
CFDictionaryRef optionsWithPrompt = CFDictionaryCreate(NULL, keys, trueValue, 1, NULL, NULL);
|
||||||
|
CFDictionaryRef optionsWithoutPrompt = CFDictionaryCreate(NULL, keys, falseValue, 1, NULL, NULL);
|
||||||
bool result;
|
bool result;
|
||||||
|
|
||||||
result = AXIsProcessTrustedWithOptions(optionsWithoutPrompt);
|
result = AXIsProcessTrustedWithOptions(optionsWithoutPrompt);
|
||||||
|
|
||||||
|
// Synergy is not checked in accessibility
|
||||||
if (!result) {
|
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
|
// call privilege help tool
|
||||||
AXDatabaseCleaner axdc;
|
AXDatabaseCleaner axdc;
|
||||||
axdc.loadPrivilegeHelper();
|
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);
|
result = AXIsProcessTrustedWithOptions(optionsWithPrompt);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CFRelease(optionsWithoutPrompt);
|
CFRelease(optionsWithoutPrompt);
|
||||||
CFRelease(optionsWithPrompt);
|
CFRelease(optionsWithPrompt);
|
||||||
|
|
Loading…
Reference in New Issue