diff --git a/stepmania/PBProject/Installer/BuildInstaller.cpp b/stepmania/PBProject/Installer/BuildInstaller.cpp index 08bbd29666..ddf614caff 100644 --- a/stepmania/PBProject/Installer/BuildInstaller.cpp +++ b/stepmania/PBProject/Installer/BuildInstaller.cpp @@ -9,31 +9,40 @@ using namespace std; -#include "StdString.h" -#include "InstallerFile.h" -#include "Processor.h" #include #include +#include #include #include #include #include +#include "StdString.h" +#include "InstallerFile.h" +#include "Processor.h" +#include "Util.h" void HandleFile(const CString& file, const CString& dir, const CString& archivePath, bool overwrite) { static bool archiveMade = false; - CString command; + char path[] = "/usr/bin/tar"; + char arg1[] = "-rf"; + char arg2[archivePath.length() + 1]; + char arg3[] = "-C"; + char arg4[dir.length() + 1]; + char arg5[file.length() + 1]; - if (archiveMade) - command = "tar rfPC '"; - else + if (!archiveMade) { + arg1[1] = 'c'; archiveMade = true; - command = "tar cfPC '"; } - command += archivePath + "' '" + dir +"' '" + file + "'"; - - system(command); + + strcpy(arg2, archivePath); + strcpy(arg4, dir); + strcpy(arg5, file); + + if (CallTool(path, arg1, arg2, arg3, arg4, arg5, NULL)) + exit(-10); } const CString GetPath(const CString& ID) @@ -107,13 +116,10 @@ int main(int argc, char *argv[]) printf("Couldn't read config file, \"%s\".\n", inFile.c_str()); return 3; } - - /* Create the directory--the lazy man's way */ - CString command = "mkdir -p '" + outDir + "'"; - if (system(command)) + if (mkdir_p(outDir)) { - printf("The system(\"%s\") call failed.\n", command.c_str()); + printf("The mkdir_p call failed.\n"); return 4; } @@ -126,14 +132,17 @@ int main(int argc, char *argv[]) printf("Compressing the archive.\n"); - /* Compress the archive after it is created */ - command = "gzip '" + archivePath + "'"; - system(command); + char toolPath[] = "/usr/bin/gzip"; + if (CallTool(toolPath, archivePath.c_str(), NULL)) + { + printf("Gzip failed.\n"); + return 6; + } if (!config.WriteFile(outDir + "/config")) { printf("%s\n", strerror(errno)); - return 6; + return 7; } return 0; diff --git a/stepmania/PBProject/Installer/Helper.mm b/stepmania/PBProject/Installer/Helper.mm index 6fd7d0ac7e..96bf1ab406 100644 --- a/stepmania/PBProject/Installer/Helper.mm +++ b/stepmania/PBProject/Installer/Helper.mm @@ -6,23 +6,70 @@ // Copyright (c) 2003 Steve Checkoway. All rights reserved. // +#import +#import +#import +#import #import +#import +#import #import "Helper.h" #import "InstallerWindowController.h" -#include "InstallerFile.h" -#include "Processor.h" +#import "InstallerFile.h" +#import "Processor.h" +#import "Util.h" void Error(const char *fmt, ...); static id c; +static AuthorizationRef auth; +static BOOL privilegedInstall; void HandleFile(const CString& file, const CString& dir, const CString& archivePath, bool overwrite) { - [c postMessage:[NSString stringWithCString:file]]; - - CString command = "tar zxfPC '" + archivePath + "' '" + dir + "' '" + file + "'"; - if (system(command)) - Error(strerror(errno)); + NSString *filePath = [NSString stringWithCString:dir]; + + filePath = [filePath stringByAppendingFormat:@"%s%s", (dir == "/" ? "" : "/"), file.c_str()]; + [c postMessage:filePath]; + /* This is rediculus */ + char f[file.length() + 1]; + char d[dir.length() + 1]; + char p[archivePath.length() + 1]; + char path[] = "/usr/bin/tar"; + char arg1[] = "-xPf"; + char arg3[] = "-C"; + char *const arguments[] = {path, arg1, p, arg3, d, f, NULL}; + + strcpy(p, archivePath); + strcpy(d, dir); + strcpy(f, file); + + if (privilegedInstall) + { + AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | + kAuthorizationFlagExtendRights; + AuthorizationItem item = {kAuthorizationRightExecute, 0, NULL, 0}; + AuthorizationRights rights = {1, &item}; + OSStatus status = AuthorizationCopyRights(auth, &rights, NULL, flags, NULL); + + if (status != errAuthorizationSuccess) + { + [c postMessage:@"Couldn't authorize"]; + return; + } + status = AuthorizationExecuteWithPrivileges(auth, path, kAuthorizationFlagDefaults, arguments+1, NULL); + if (status != errAuthorizationSuccess) + [c postMessage:@"failed"]; + wait(&status); + } + else + { + int fd_dev_null = open("/dev/null", O_RDWR, 0); + + if (CallTool3(true, -1, fd_dev_null, fd_dev_null, path, arguments)) + [c postMessage:@"failed"]; + close(fd_dev_null); + } } const CString GetPath(const CString& ID) @@ -44,7 +91,7 @@ const CString GetPath(const CString& ID) NSString *path = [panel filename]; //No need for NSOpenPanel's filenames method - [c postMessage:[NSString stringWithFormat:@"Choose file \"%@\".", path]]; + [c postMessage:[NSString stringWithFormat:@"Chose file \"%@\".", path]]; return [path cString]; } @@ -68,16 +115,58 @@ bool AskFunc(const CString& question) return answer; } +bool Auth(void) +{ + AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | + kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights; + AuthorizationItem item = {kAuthorizationRightExecute, 0, NULL, 0}; + AuthorizationRights rights = {1, &item}; + OSStatus status = AuthorizationCopyRights(auth, &rights, NULL, flags, NULL); + return status == errAuthorizationSuccess; +} + +void Echo(const CString& message, bool loud) +{ + if (!loud) + { + [c postMessage:[NSString stringWithCString:message.c_str()]]; + return; + } + + NSBeginCriticalAlertSheet(@"Warning", @"OK", nil, nil, [c window], c, nil, nil, NULL, + [NSString stringWithCString:message.c_str()]); +} + +void Priv(bool privileged) +{ + privilegedInstall = privileged; +} + + @implementation Helper - (id)initWithPath:(NSString *)path { [super init]; mPath = [path retain]; + + OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, + kAuthorizationFlagDefaults, &auth); + if (status != errAuthorizationSuccess) + { + auth = NULL; + [self autorelease]; + return nil; + } + + privilegedInstall = NO; + return self; } - (void)dealloc { + if (auth) + AuthorizationFree(auth, kAuthorizationFlagDestroyRights); [mPath autorelease]; [super dealloc]; } @@ -88,26 +177,39 @@ bool AskFunc(const CString& question) NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; CString configPath = [mPath cString]; CString archivePath = configPath + "/archive.tar.gz"; + BOOL success = NO; configPath += "/config"; InstallerFile file(configPath); - Processor p(archivePath, HandleFile, GetPath, AskFunc, YES); - - p.SetErrorFunc(Error); - - [c postMessage:@"Reading from the config file."]; - - if(!file.ReadFile()) + try { - [c postMessage:@"Couldn't read the config file"]; - [c finishedInstalling:NO]; - [pool release]; - return; - } + do { + Processor p(archivePath, HandleFile, GetPath, AskFunc, YES); - unsigned nextLine = 0; - while (nextLine < file.GetNumLines()) - p.ProcessLine(file.GetLine(nextLine), nextLine); + p.SetErrorFunc(Error); + p.SetAuthFunc(Auth); + p.SetEchoFunc(Echo); + p.SetPrivFunc(Priv); + + [c postMessage:@"Reading from the config file."]; + + if(!file.ReadFile()) + { + [c postMessage:@"Couldn't read the config file"]; + break; + } + + unsigned nextLine = 0; + while (nextLine < file.GetNumLines()) + p.ProcessLine(file.GetLine(nextLine), nextLine); + success = YES; + } + while(0); + } + catch (CString& e) + { + [c postMessage:[NSString stringWithCString:e]]; + } [c postMessage:@"Installation complete"]; [c finishedInstalling:YES]; diff --git a/stepmania/PBProject/Installer/InstallerWindowController.m b/stepmania/PBProject/Installer/InstallerWindowController.m index abbe718598..ff5b24fb8c 100644 --- a/stepmania/PBProject/Installer/InstallerWindowController.m +++ b/stepmania/PBProject/Installer/InstallerWindowController.m @@ -3,7 +3,9 @@ @implementation InstallerWindowController - (void)awakeFromNib { - helper = [[Helper alloc] initWithPath:@"files"]; + NSString *path = [[[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] + stringByAppendingPathComponent:@"files"] stringByStandardizingPath]; + helper = [[Helper alloc] initWithPath:path]; finished = NO; } diff --git a/stepmania/PBProject/Installer/Processor.cpp b/stepmania/PBProject/Installer/Processor.cpp index 69109b86a4..79fc083e6d 100644 --- a/stepmania/PBProject/Installer/Processor.cpp +++ b/stepmania/PBProject/Installer/Processor.cpp @@ -9,27 +9,15 @@ using namespace std; -#include #include -#include +#include #include +#include +#include +#include #include "StdString.h" #include "Processor.h" - -void split(const CString& Source, const CString& Deliminator, vector& AddIt) -{ - unsigned startpos = 0; - - do { - unsigned pos = Source.find(Deliminator, startpos); - if (pos == Source.npos) pos=Source.size(); - - CString AddCString = Source.substr(startpos, pos-startpos); - AddIt.push_back(AddCString); - - startpos=pos+Deliminator.size(); - } while (startpos <= Source.size()); -} +#include "Util.h" bool IsAVar(const CString& var) { @@ -44,7 +32,7 @@ CString StripVar(const CString& var) } Processor::Processor(CString& path, handleFileFunc f, getPathFunc p, askFunc a, bool i) -: mDoGoto(false), mPath(path), mHandleFile(f), mGetPath(p), mAsk(a), +: mDoGoto(false), mHandleFile(f), mGetPath(p), mAsk(a), mError(Processor::DefaultError), mInstalling(i) { char cwd[MAXPATHLEN]; @@ -52,6 +40,33 @@ mError(Processor::DefaultError), mInstalling(i) getwd(cwd); mCWD = cwd; mConditionals["INSTALLING"] = i; + + if (mInstalling) + { + char archivePath[] = "/tmp/installerXXXXXX.tar"; + int fd = mkstemps(archivePath, 4); + + if (fd == -1) + throw CString("Couldn't create temporary file"); + + mPath = archivePath; + + char toolPath[] = "/usr/bin/gunzip"; + + if(CallTool2(true, -1, fd, -1, toolPath, "-c", path.c_str(), NULL)) + { + unlink(mPath); + throw CString("Archive file not handled correctly or not present."); + } + } + else + mPath = path; +} + +Processor::~Processor() +{ + if (mInstalling) + unlink(mPath); } void Processor::ProcessLine(const CString& line, unsigned& nextLine) @@ -60,6 +75,7 @@ void Processor::ProcessLine(const CString& line, unsigned& nextLine) split(line, ":", parts); +#pragma mark LABEL if (parts[0] == "LABEL") { printf("%u: %s\n", nextLine, line.c_str()); @@ -82,17 +98,50 @@ void Processor::ProcessLine(const CString& line, unsigned& nextLine) printf("%u: %s\n", nextLine, line.c_str()); +#pragma mark FILE if (parts[0] == "FILE") { if (parts.size() != 3) goto error; - bool overwrite = ResolveConditional(parts[2]); - (*mHandleFile)(parts[1], mCWD, mPath, overwrite); + CString file, dir; + if (parts[1][0] == '/') + { + file = parts[1].substr(1); + dir = "/"; + } + else + { + file = parts[1]; + dir = mCWD; + } + + if (mInstalling) + (*mHandleFile)(file, dir, mPath, ResolveConditional(parts[2])); + else + { + CStringArray list, dirs; + if (IsADirectory((dir == "/" ? dir : dir + "/" ) + file)) + { + int fd = open(".", O_RDONLY, 0); + + chdir(dir); + FileListingForDirectoryWithIgnore(file, list, dirs, mIgnore); + fchdir(fd); + close(fd); + if (list.size() == 0) //empty directory or ignored directory + (*mHandleFile)(file, dir, mPath, true); + for (unsigned i=0; i(), false); + for (unsigned i=0; i #include +#include #include "StdString.h" class Processor @@ -23,6 +24,9 @@ private: typedef const CString (*getPathFunc)(const CString& ID); typedef void (*errorFunc)(const char *fmt, ...); typedef bool (*askFunc)(const CString& question); + typedef bool (*authFunc)(void); + typedef void (*echoFunc)(const CString& message, bool loud); + typedef void (*privFunc)(bool privileged); stack mReturnStack; bool mDoGoto; @@ -30,12 +34,16 @@ private: map mConditionals; map mVars; map mLabels; + set mIgnore; CString mPath; CString mCWD; handleFileFunc mHandleFile; getPathFunc mGetPath; askFunc mAsk; errorFunc mError; + authFunc mAuth; + echoFunc mEcho; + privFunc mPriv; bool mInstalling; const CString& ResolveVar(const CString& var); @@ -43,8 +51,12 @@ private: static void DefaultError(const char *fmt, ...); public: Processor(CString& path, handleFileFunc f, getPathFunc p, askFunc a, bool i); + ~Processor(); void ProcessLine(const CString& line, unsigned& nextLine); void SetErrorFunc(errorFunc f) { mError = f; } + void SetAuthFunc(authFunc f) { mAuth = f; } + void SetEchoFunc(echoFunc f) { mEcho = f; } + void SetPrivFunc(privFunc f) { mPriv = f; } }; #endif diff --git a/stepmania/PBProject/Installer/Util.cpp b/stepmania/PBProject/Installer/Util.cpp new file mode 100644 index 0000000000..e21921b32b --- /dev/null +++ b/stepmania/PBProject/Installer/Util.cpp @@ -0,0 +1,149 @@ +/* + * Util.cpp + * stepmania + * + * Created by Steve Checkoway on Mon Sep 15 2003. + * Copyright (c) 2003 Steve Checkoway. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include +#include "Util.h" + +void split(const CString& Source, const CString& Deliminator, vector& AddIt) +{ + unsigned startpos = 0; + + do { + unsigned pos = Source.find(Deliminator, startpos); + if (pos == Source.npos) pos=Source.size(); + + CString AddCString = Source.substr(startpos, pos-startpos); + AddIt.push_back(AddCString); + + startpos=pos+Deliminator.size(); + } while (startpos <= Source.size()); +} + +inline bool IsADirectory(const CString& path) +{ + struct stat s; + if (stat(path, &s)) + return false; + return s.st_mode & S_IFDIR; +} + +inline CString LastPathComponent(const CString& path) +{ + unsigned pos = path.rfind('/'); + + if (pos == path.npos) + pos = 0; + return path.substr(pos); +} + +void FileListingForDirectoryWithIgnore(const CString& path, CStringArray& list, CStringArray& dirs, + const set& ignore, bool ignoreDot) +{ + DIR *dir = opendir(path); + dirent *entry; + + if (!dir) + return; + + while ((entry = readdir(dir))) + { + if (ignoreDot && entry->d_name[0] == '.') + continue; + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + continue; + if (ignore.find(LastPathComponent(path)) != ignore.end()) + continue; + CString temp = path + "/"; + + temp += entry->d_name; + if (IsADirectory(temp)) + { + dirs.push_back(temp); + FileListingForDirectoryWithIgnore(temp, list, dirs, ignore, ignoreDot); + } + else + list.push_back(temp); + } + closedir(dir); +} + +int mkdir_p(const CString& path) +{ + CStringArray components; + int ret = 0; + int fd = open(".", O_RDONLY, 0); + + split(path, "/", components); + + if (path[0] == '/') + chdir("/"); + for (unsigned i=0; i -1) + dup2(fd_in, 0); + + if (fd_out > -1) + dup2(fd_out, 1); + + if (fd_err > -1) + dup2(fd_err, 2); + + if (fd_err != fd_in && fd_err != fd_out) + close(fd_err); + + if (fd_out != fd_in) + close(fd_out); + + close(fd_in); + execv(path, arguments); + _exit(-1); + } + /* parent */ + if (!blocking) + return 0; + + int status; + + waitpid(pid, &status, 0); + return WEXITSTATUS(status); +} diff --git a/stepmania/PBProject/Installer/Util.h b/stepmania/PBProject/Installer/Util.h new file mode 100644 index 0000000000..0acc7f00b3 --- /dev/null +++ b/stepmania/PBProject/Installer/Util.h @@ -0,0 +1,24 @@ +/* + * Util.h + * stepmania + * + * Created by Steve Checkoway on Mon Sep 15 2003. + * Copyright (c) 2003 Steve Checkoway. All rights reserved. + * + */ + +using namespace std; +#include "StdString.h" +#include +#include + +void split(const CString& Source, const CString& Deliminator, vector& AddIt); +bool IsADirectory(const CString& path); +CString LastPathComponent(const CString& path); +void FileListingForDirectoryWithIgnore(const CString& path, CStringArray& list, CStringArray& dirs, + const set& ignore, bool ignoreDot = true); +int mkdir_p(const CString& path); +int CallTool3(bool blocking, int fd_in, int fd_out, int fd_err, const char *path, char *const *arguments); +inline int CallTool(char *path, ...) { return CallTool3(true, -1, -1, -1, path, &path); } +inline int CallTool2(bool blocking, int fd_in, int fd_out, int fd_err, char *path, ...) +{ return CallTool3(blocking, fd_in, fd_out, fd_err, path, &path); }