1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00
qTox/osx/updater/qtox_sudo.m

274 lines
6.6 KiB
Objective-C

// Modifications listed here GPLv3: https://gist.githubusercontent.com/stqism/2e82352026915f8f6ab3/raw/fca6f6f16fb8d61a64b6053dacf50c3433c2e0af/gistfile1.txt
//
// Copyright 2009 Performant Design, LLC. All rights reserved.
// Copyright (C) 2014 Tox Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <http://www.gnu.org/licenses/>.
//
#import <Cocoa/Cocoa.h>
#include <sys/stat.h>
#include <unistd.h>
#include <Security/Authorization.h>
#include <Security/AuthorizationTags.h>
char *addFileToPath(const char *path, const char *filename) {
char *outbuf;
char *lc;
lc = (char *)path + strlen(path) - 1;
if (lc < path || *lc != '/') {
lc = NULL;
}
while (*filename == '/') {
filename++;
}
outbuf = malloc(strlen(path) + strlen(filename) + 1 + (lc == NULL ? 1 : 0));
sprintf(outbuf, "%s%s%s", path, (lc == NULL) ? "/" : "", filename);
return outbuf;
}
int isExecFile(const char *name) {
struct stat s;
return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode));
}
char *which(const char *filename)
{
char *path, *p, *n;
path = getenv("PATH");
if (!path) {
return NULL;
}
p = path = strdup(path);
while (p) {
n = strchr(p, ':');
if (n) {
*n++ = '\0';
}
if (*p != '\0') {
p = addFileToPath(p, filename);
if (isExecFile(p)) {
free(path);
return p;
}
free(p);
}
p = n;
}
free(path);
return NULL;
}
int cocoaSudo(char *executable, char *commandArgs[], char *icon, char *prompt) {
int retVal = 1;
OSStatus status;
AuthorizationRef authRef;
AuthorizationItem right = {kAuthorizationRightExecute, 0, NULL, 0};
AuthorizationRights rightSet = {1, &right};
AuthorizationEnvironment myAuthorizationEnvironment;
AuthorizationItem kAuthEnv[2];
myAuthorizationEnvironment.items = kAuthEnv;
AuthorizationFlags flags = kAuthorizationFlagDefaults;
if (prompt && icon) {
kAuthEnv[0].name = kAuthorizationEnvironmentPrompt;
kAuthEnv[0].valueLength = strlen(prompt);
kAuthEnv[0].value = prompt;
kAuthEnv[0].flags = 0;
kAuthEnv[1].name = kAuthorizationEnvironmentIcon;
kAuthEnv[1].valueLength = strlen(icon);
kAuthEnv[1].value = icon;
kAuthEnv[1].flags = 0;
myAuthorizationEnvironment.count = 2;
}
else if (prompt) {
kAuthEnv[0].name = kAuthorizationEnvironmentPrompt;
kAuthEnv[0].valueLength = strlen(prompt);
kAuthEnv[0].value = prompt;
kAuthEnv[0].flags = 0;
myAuthorizationEnvironment.count = 1;
}
else if (icon) {
kAuthEnv[0].name = kAuthorizationEnvironmentIcon;
kAuthEnv[0].valueLength = strlen(icon);
kAuthEnv[0].value = icon;
kAuthEnv[0].flags = 0;
myAuthorizationEnvironment.count = 1;
}
else {
myAuthorizationEnvironment.count = 0;
}
status = AuthorizationCreate(NULL, &myAuthorizationEnvironment, flags, &authRef);
if (status != errAuthorizationSuccess) {
NSLog(@"Could not create authorization reference object.");
status = errAuthorizationBadAddress;
}
else {
flags = kAuthorizationFlagDefaults |
kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize |
kAuthorizationFlagExtendRights;
status = AuthorizationCopyRights(authRef, &rightSet, &myAuthorizationEnvironment, flags, NULL);
}
if (status == errAuthorizationSuccess) {
FILE *ioPipe;
char buffer[1024];
int bytesRead;
flags = kAuthorizationFlagDefaults;
status = AuthorizationExecuteWithPrivileges(authRef, executable, flags, commandArgs, &ioPipe);
/* Just pipe processes' stdout to our stdout for now; hopefully can add stdin pipe later as well */
for (;;) {
bytesRead = fread(buffer, sizeof(char), 1024, ioPipe);
if (bytesRead < 1) {
break;
}
write(STDOUT_FILENO, buffer, bytesRead * sizeof(char));
}
pid_t pid;
int pidStatus;
do {
pid = wait(&pidStatus);
}
while (pid != -1);
if (status == errAuthorizationSuccess) {
retVal = 0;
}
}
else {
AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
authRef = NULL;
if (status != errAuthorizationCanceled) {
// pre-auth failed
NSLog(@"Pre-auth failed");
}
}
return retVal;
}
void usage(char *appNameFull) {
char *appName = strrchr(appNameFull, '/');
if (appName == NULL) {
appName = appNameFull;
}
else {
appName++;
}
fprintf(stderr, "usage: %s command\n", appName);
}
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = 1;
int programArgsStartAt = 1;
char *icon = NULL;
char *prompt = NULL;
if (programArgsStartAt >= argc) {
usage(argv[0]);
}
else {
char *executable;
if (strchr(argv[programArgsStartAt], '/')) {
executable = isExecFile(argv[programArgsStartAt]) ? strdup(argv[programArgsStartAt]) : NULL;
}
else {
executable = which(argv[programArgsStartAt]);
}
if (executable) {
char **commandArgs = malloc((argc - programArgsStartAt) * sizeof(char**));
memcpy(commandArgs, argv + programArgsStartAt + 1, (argc - programArgsStartAt - 1) * sizeof(char**));
commandArgs[argc - programArgsStartAt - 1] = NULL;
retVal = cocoaSudo(executable, commandArgs, icon, prompt);
free(commandArgs);
free(executable);
}
else {
fprintf(stderr, "Unable to find %s\n", argv[programArgsStartAt]);
usage(argv[0]);
}
}
if (prompt) {
free(prompt);
}
[pool release];
return retVal;
}