2016-09-12 01:21:14 +01:00
|
|
|
//
|
|
|
|
// Utilities.m
|
|
|
|
// WhatsYourSign
|
|
|
|
//
|
|
|
|
// Created by Patrick Wardle on 7/7/16.
|
|
|
|
// Copyright (c) 2016 Objective-See. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "Consts.h"
|
2016-09-15 09:15:30 +01:00
|
|
|
#import "Logging.h"
|
2016-09-12 01:21:14 +01:00
|
|
|
#import "Utilities.h"
|
|
|
|
|
|
|
|
#import <signal.h>
|
|
|
|
#import <unistd.h>
|
|
|
|
#import <libproc.h>
|
2016-09-15 09:15:30 +01:00
|
|
|
#import <sys/stat.h>
|
2016-09-12 01:21:14 +01:00
|
|
|
#import <sys/sysctl.h>
|
|
|
|
#import <Security/Security.h>
|
|
|
|
#import <Foundation/Foundation.h>
|
|
|
|
#import <CommonCrypto/CommonDigest.h>
|
|
|
|
#import <SystemConfiguration/SystemConfiguration.h>
|
|
|
|
|
2016-09-25 04:08:38 +01:00
|
|
|
//get OS version
|
|
|
|
NSDictionary* getOSVersion()
|
|
|
|
{
|
|
|
|
//os version info
|
|
|
|
NSMutableDictionary* osVersionInfo = nil;
|
|
|
|
|
|
|
|
//major v
|
|
|
|
SInt32 majorVersion = 0;
|
|
|
|
|
|
|
|
//minor v
|
|
|
|
SInt32 minorVersion = 0;
|
|
|
|
|
|
|
|
//alloc dictionary
|
|
|
|
osVersionInfo = [NSMutableDictionary dictionary];
|
|
|
|
|
|
|
|
//get major version
|
|
|
|
if(STATUS_SUCCESS != Gestalt(gestaltSystemVersionMajor, &majorVersion))
|
|
|
|
{
|
|
|
|
//reset
|
|
|
|
osVersionInfo = nil;
|
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//get minor version
|
|
|
|
if(STATUS_SUCCESS != Gestalt(gestaltSystemVersionMinor, &minorVersion))
|
|
|
|
{
|
|
|
|
//reset
|
|
|
|
osVersionInfo = nil;
|
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//set major version
|
|
|
|
osVersionInfo[@"majorVersion"] = [NSNumber numberWithInteger:majorVersion];
|
|
|
|
|
|
|
|
//set minor version
|
|
|
|
osVersionInfo[@"minorVersion"] = [NSNumber numberWithInteger:minorVersion];
|
|
|
|
|
|
|
|
//bail
|
|
|
|
bail:
|
|
|
|
|
|
|
|
return osVersionInfo;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-12 01:21:14 +01:00
|
|
|
//get app's version
|
|
|
|
// ->extracted from Info.plist
|
|
|
|
NSString* getAppVersion()
|
|
|
|
{
|
|
|
|
//read and return 'CFBundleVersion' from bundle
|
|
|
|
return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
|
|
|
|
}
|
|
|
|
|
|
|
|
//given a path to binary
|
|
|
|
// parse it back up to find app's bundle
|
|
|
|
NSBundle* findAppBundle(NSString* binaryPath)
|
|
|
|
{
|
|
|
|
//app's bundle
|
|
|
|
NSBundle* appBundle = nil;
|
|
|
|
|
|
|
|
//app's path
|
|
|
|
NSString* appPath = nil;
|
|
|
|
|
|
|
|
//first just try full path
|
|
|
|
appPath = binaryPath;
|
|
|
|
|
|
|
|
//try to find the app's bundle/info dictionary
|
|
|
|
do
|
|
|
|
{
|
|
|
|
//try to load app's bundle
|
|
|
|
appBundle = [NSBundle bundleWithPath:appPath];
|
|
|
|
|
|
|
|
//check for match
|
|
|
|
// ->binary path's match
|
|
|
|
if( (nil != appBundle) &&
|
|
|
|
(YES == [appBundle.executablePath isEqualToString:binaryPath]))
|
|
|
|
{
|
|
|
|
//all done
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//always unset bundle var since it's being returned
|
|
|
|
// ->and at this point, its not a match
|
|
|
|
appBundle = nil;
|
|
|
|
|
|
|
|
//remove last part
|
|
|
|
// ->will try this next
|
|
|
|
appPath = [appPath stringByDeletingLastPathComponent];
|
|
|
|
|
|
|
|
//scan until we get to root
|
|
|
|
// ->of course, loop will exit if app info dictionary is found/loaded
|
|
|
|
} while( (nil != appPath) &&
|
|
|
|
(YES != [appPath isEqualToString:@"/"]) &&
|
|
|
|
(YES != [appPath isEqualToString:@""]) );
|
|
|
|
|
|
|
|
return appBundle;
|
|
|
|
}
|
|
|
|
|
2016-09-15 09:15:30 +01:00
|
|
|
//set dir's|file's group/owner
|
|
|
|
BOOL setFileOwner(NSString* path, NSNumber* groupID, NSNumber* ownerID, BOOL recursive)
|
|
|
|
{
|
|
|
|
//ret var
|
2016-09-16 09:13:06 +01:00
|
|
|
BOOL bSetOwner = NO;
|
2016-09-15 09:15:30 +01:00
|
|
|
|
|
|
|
//owner dictionary
|
|
|
|
NSDictionary* fileOwner = nil;
|
|
|
|
|
|
|
|
//sub paths
|
|
|
|
NSArray *subPaths = nil;
|
|
|
|
|
|
|
|
//full path
|
|
|
|
// ->for recursive
|
|
|
|
NSString* fullPath = nil;
|
|
|
|
|
|
|
|
//init permissions dictionary
|
|
|
|
fileOwner = @{NSFileGroupOwnerAccountID:groupID, NSFileOwnerAccountID:ownerID};
|
|
|
|
|
|
|
|
//set group/owner
|
|
|
|
if(YES != [[NSFileManager defaultManager] setAttributes:fileOwner ofItemAtPath:path error:NULL])
|
|
|
|
{
|
|
|
|
//err msg
|
|
|
|
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to set ownership for %@ (%@)", path, fileOwner]);
|
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//dbg msg
|
|
|
|
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"set ownership for %@ (%@)", path, fileOwner]);
|
|
|
|
|
|
|
|
//do it recursively
|
|
|
|
if(YES == recursive)
|
|
|
|
{
|
|
|
|
//sanity check
|
|
|
|
// ->make sure root starts with '/'
|
|
|
|
if(YES != [path hasSuffix:@"/"])
|
|
|
|
{
|
|
|
|
//add '/'
|
|
|
|
path = [NSString stringWithFormat:@"%@/", path];
|
|
|
|
}
|
|
|
|
|
|
|
|
//get all subpaths
|
|
|
|
subPaths = [[NSFileManager defaultManager] subpathsAtPath:path];
|
|
|
|
for(NSString *subPath in subPaths)
|
|
|
|
{
|
|
|
|
//init full path
|
|
|
|
fullPath = [path stringByAppendingString:subPath];
|
|
|
|
|
|
|
|
//set group/owner
|
|
|
|
if(YES != [[NSFileManager defaultManager] setAttributes:fileOwner ofItemAtPath:fullPath error:NULL])
|
|
|
|
{
|
|
|
|
//err msg
|
|
|
|
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to set ownership for %@ (%@)", fullPath, fileOwner]);
|
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//no errors
|
2016-09-16 09:13:06 +01:00
|
|
|
bSetOwner = YES;
|
2016-09-15 09:15:30 +01:00
|
|
|
|
|
|
|
//bail
|
|
|
|
bail:
|
|
|
|
|
2016-09-16 09:13:06 +01:00
|
|
|
return bSetOwner;
|
2016-09-15 09:15:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//set permissions for file
|
2016-09-16 09:13:06 +01:00
|
|
|
BOOL setFilePermissions(NSString* file, int permissions, BOOL recursive)
|
2016-09-15 09:15:30 +01:00
|
|
|
{
|
2016-09-16 09:13:06 +01:00
|
|
|
//ret var
|
|
|
|
BOOL bSetPermissions = NO;
|
|
|
|
|
2016-09-15 09:15:30 +01:00
|
|
|
//file permissions
|
|
|
|
NSDictionary* filePermissions = nil;
|
|
|
|
|
2016-09-16 09:13:06 +01:00
|
|
|
//root directory
|
|
|
|
NSURL* root = nil;
|
2016-09-15 09:15:30 +01:00
|
|
|
|
2016-09-16 09:13:06 +01:00
|
|
|
//directory enumerator
|
|
|
|
NSDirectoryEnumerator* enumerator = nil;
|
2016-09-15 09:15:30 +01:00
|
|
|
|
2016-09-16 09:13:06 +01:00
|
|
|
//error
|
|
|
|
NSError* error = nil;
|
2016-09-15 09:15:30 +01:00
|
|
|
|
|
|
|
//init dictionary
|
|
|
|
filePermissions = @{NSFilePosixPermissions: [NSNumber numberWithInt:permissions]};
|
|
|
|
|
2016-09-16 09:13:06 +01:00
|
|
|
//apply file permissions recursively
|
|
|
|
if(YES == recursive)
|
|
|
|
{
|
|
|
|
//init root
|
|
|
|
root = [NSURL fileURLWithPath:file];
|
|
|
|
|
|
|
|
//init enumerator
|
|
|
|
enumerator = [[NSFileManager defaultManager] enumeratorAtURL:root includingPropertiesForKeys:[NSArray arrayWithObject:NSURLIsDirectoryKey] options:0
|
|
|
|
errorHandler:^(NSURL *url, NSError *error) { return YES; }];
|
|
|
|
|
|
|
|
//set file permissions on each
|
|
|
|
for(NSURL* currentFile in enumerator)
|
|
|
|
{
|
|
|
|
//set permissions
|
|
|
|
if(YES != [[NSFileManager defaultManager] setAttributes:filePermissions ofItemAtPath:currentFile.path error:&error])
|
|
|
|
{
|
|
|
|
//err msg
|
|
|
|
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to set permissions for %@ (%@), %@", currentFile.path, filePermissions, error]);
|
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//always set permissions on passed in file (or top-level directory)
|
|
|
|
// ->note: recursive enumerator skips root directory, so execute this always
|
2016-09-15 09:15:30 +01:00
|
|
|
if(YES != [[NSFileManager defaultManager] setAttributes:filePermissions ofItemAtPath:file error:NULL])
|
|
|
|
{
|
|
|
|
//err msg
|
|
|
|
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to set permissions for %@ (%@)", file, filePermissions]);
|
2016-09-16 09:13:06 +01:00
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
2016-09-15 09:15:30 +01:00
|
|
|
}
|
|
|
|
|
2016-09-16 09:13:06 +01:00
|
|
|
//happy
|
|
|
|
bSetPermissions = YES;
|
|
|
|
|
|
|
|
//bail
|
|
|
|
bail:
|
2016-09-15 09:15:30 +01:00
|
|
|
|
2016-09-16 09:13:06 +01:00
|
|
|
return bSetPermissions;
|
2016-09-15 09:15:30 +01:00
|
|
|
}
|
|
|
|
|
2016-09-12 01:21:14 +01:00
|
|
|
//exec a process and grab it's output
|
|
|
|
NSData* execTask(NSString* binaryPath, NSArray* arguments)
|
|
|
|
{
|
|
|
|
//task
|
|
|
|
NSTask *task = nil;
|
|
|
|
|
|
|
|
//output pipe
|
|
|
|
NSPipe *outPipe = nil;
|
|
|
|
|
|
|
|
//read handle
|
|
|
|
NSFileHandle* readHandle = nil;
|
|
|
|
|
|
|
|
//output
|
|
|
|
NSMutableData *output = nil;
|
|
|
|
|
|
|
|
//init task
|
|
|
|
task = [NSTask new];
|
|
|
|
|
|
|
|
//init output pipe
|
|
|
|
outPipe = [NSPipe pipe];
|
|
|
|
|
|
|
|
//init read handle
|
|
|
|
readHandle = [outPipe fileHandleForReading];
|
|
|
|
|
|
|
|
//init output buffer
|
|
|
|
output = [NSMutableData data];
|
|
|
|
|
|
|
|
//set task's path
|
|
|
|
[task setLaunchPath:binaryPath];
|
|
|
|
|
|
|
|
//set task's args
|
|
|
|
[task setArguments:arguments];
|
|
|
|
|
|
|
|
//set task's output
|
|
|
|
[task setStandardOutput:outPipe];
|
|
|
|
|
|
|
|
//wrap task launch
|
|
|
|
@try
|
|
|
|
{
|
|
|
|
//launch
|
|
|
|
[task launch];
|
|
|
|
}
|
2016-09-18 22:10:44 +01:00
|
|
|
@catch(NSException* exception)
|
2016-09-12 01:21:14 +01:00
|
|
|
{
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//read in output
|
|
|
|
while(YES == [task isRunning])
|
|
|
|
{
|
|
|
|
//accumulate output
|
|
|
|
[output appendData:[readHandle readDataToEndOfFile]];
|
|
|
|
}
|
|
|
|
|
|
|
|
//grab any left over data
|
|
|
|
[output appendData:[readHandle readDataToEndOfFile]];
|
|
|
|
|
|
|
|
//bail
|
|
|
|
bail:
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
//get OS's major or minor version
|
|
|
|
SInt32 getVersion(OSType selector)
|
|
|
|
{
|
|
|
|
//version
|
|
|
|
// ->major or minor
|
|
|
|
SInt32 version = -1;
|
|
|
|
|
|
|
|
//get version info
|
|
|
|
if(noErr != Gestalt(selector, &version))
|
|
|
|
{
|
|
|
|
//reset version
|
|
|
|
version = -1;
|
|
|
|
|
|
|
|
//err
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//bail
|
|
|
|
bail:
|
|
|
|
|
|
|
|
return version;
|
|
|
|
}
|
|
|
|
|
|
|
|
//get process's path
|
|
|
|
NSString* getProcessPath(pid_t pid)
|
|
|
|
{
|
|
|
|
//task path
|
|
|
|
NSString* taskPath = nil;
|
|
|
|
|
|
|
|
//buffer for process path
|
|
|
|
char pathBuffer[PROC_PIDPATHINFO_MAXSIZE] = {0};
|
|
|
|
|
|
|
|
//status
|
|
|
|
int status = -1;
|
|
|
|
|
|
|
|
//'management info base' array
|
|
|
|
int mib[3] = {0};
|
|
|
|
|
|
|
|
//system's size for max args
|
|
|
|
int systemMaxArgs = 0;
|
|
|
|
|
|
|
|
//process's args
|
|
|
|
char* taskArgs = NULL;
|
|
|
|
|
|
|
|
//# of args
|
|
|
|
int numberOfArgs = 0;
|
|
|
|
|
|
|
|
//size of buffers, etc
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
|
|
//reset buffer
|
|
|
|
bzero(pathBuffer, PROC_PIDPATHINFO_MAXSIZE);
|
|
|
|
|
|
|
|
//first attempt to get path via 'proc_pidpath()'
|
|
|
|
status = proc_pidpath(pid, pathBuffer, sizeof(pathBuffer));
|
|
|
|
if(0 != status)
|
|
|
|
{
|
|
|
|
//init task's name
|
|
|
|
taskPath = [NSString stringWithUTF8String:pathBuffer];
|
|
|
|
}
|
|
|
|
//otherwise
|
|
|
|
// ->try via task's args ('KERN_PROCARGS2')
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//init mib
|
|
|
|
// ->want system's size for max args
|
|
|
|
mib[0] = CTL_KERN;
|
|
|
|
mib[1] = KERN_ARGMAX;
|
|
|
|
|
|
|
|
//set size
|
|
|
|
size = sizeof(systemMaxArgs);
|
|
|
|
|
|
|
|
//get system's size for max args
|
|
|
|
if(-1 == sysctl(mib, 2, &systemMaxArgs, &size, NULL, 0))
|
|
|
|
{
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//alloc space for args
|
|
|
|
taskArgs = malloc(systemMaxArgs);
|
|
|
|
if(NULL == taskArgs)
|
|
|
|
{
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//init mib
|
|
|
|
// ->want process args
|
|
|
|
mib[0] = CTL_KERN;
|
|
|
|
mib[1] = KERN_PROCARGS2;
|
|
|
|
mib[2] = pid;
|
|
|
|
|
|
|
|
//set size
|
|
|
|
size = (size_t)systemMaxArgs;
|
|
|
|
|
|
|
|
//get process's args
|
|
|
|
if(-1 == sysctl(mib, 3, taskArgs, &size, NULL, 0))
|
|
|
|
{
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//sanity check
|
|
|
|
// ->ensure buffer is somewhat sane
|
|
|
|
if(size <= sizeof(int))
|
|
|
|
{
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//extract number of args
|
|
|
|
// ->at start of buffer
|
|
|
|
memcpy(&numberOfArgs, taskArgs, sizeof(numberOfArgs));
|
|
|
|
|
|
|
|
//extract task's name
|
|
|
|
// ->follows # of args (int) and is NULL-terminated
|
|
|
|
taskPath = [NSString stringWithUTF8String:taskArgs + sizeof(int)];
|
|
|
|
}
|
|
|
|
|
|
|
|
//bail
|
|
|
|
bail:
|
|
|
|
|
|
|
|
//free process args
|
|
|
|
if(NULL != taskArgs)
|
|
|
|
{
|
|
|
|
//free
|
|
|
|
free(taskArgs);
|
|
|
|
|
|
|
|
//reset
|
|
|
|
taskArgs = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return taskPath;
|
|
|
|
}
|
|
|
|
|
2016-09-18 22:10:44 +01:00
|
|
|
//given a pid
|
|
|
|
// ->get the name of the process
|
|
|
|
NSString* getProcessName(pid_t pid)
|
|
|
|
{
|
|
|
|
//task path
|
|
|
|
NSString* processName = nil;
|
|
|
|
|
|
|
|
//process path
|
|
|
|
NSString* processPath = nil;
|
|
|
|
|
|
|
|
//app's bundle
|
|
|
|
NSBundle* appBundle = nil;
|
|
|
|
|
|
|
|
//get process path
|
|
|
|
processPath = getProcessPath(pid);
|
|
|
|
if( (nil == processPath) ||
|
|
|
|
(0 == processPath.length) )
|
|
|
|
{
|
|
|
|
//default to 'unknown'
|
|
|
|
processName = @"<unknown>";
|
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//try find an app bundle
|
|
|
|
appBundle = findAppBundle(processPath);
|
|
|
|
if(nil != appBundle)
|
|
|
|
{
|
|
|
|
//grab name from app's bundle
|
|
|
|
processName = [appBundle infoDictionary][@"CFBundleName"];
|
|
|
|
}
|
|
|
|
|
|
|
|
//still nil?
|
|
|
|
// ->just grab from path
|
|
|
|
if(nil == processName)
|
|
|
|
{
|
|
|
|
//from path
|
|
|
|
processName = [processPath lastPathComponent];
|
|
|
|
}
|
|
|
|
|
|
|
|
//bail
|
|
|
|
bail:
|
|
|
|
|
|
|
|
return processName;
|
|
|
|
}
|
|
|
|
|
2016-09-25 04:08:38 +01:00
|
|
|
//given a process name
|
|
|
|
// ->get the (first) instance of that process
|
|
|
|
pid_t getProcessID(NSString* processName)
|
|
|
|
{
|
|
|
|
//status
|
|
|
|
int status = -1;
|
|
|
|
|
|
|
|
//process id
|
|
|
|
pid_t processID = -1;
|
|
|
|
|
|
|
|
//# of procs
|
|
|
|
int numberOfProcesses = 0;
|
|
|
|
|
|
|
|
//array of pids
|
|
|
|
pid_t* pids = NULL;
|
|
|
|
|
|
|
|
//get # of procs
|
|
|
|
numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
|
|
|
|
|
|
|
|
//alloc buffer for pids
|
|
|
|
pids = calloc(numberOfProcesses, sizeof(pid_t));
|
|
|
|
|
|
|
|
//get list of pids
|
|
|
|
status = proc_listpids(PROC_ALL_PIDS, 0, pids, numberOfProcesses * sizeof(pid_t));
|
|
|
|
if(status < 0)
|
|
|
|
{
|
|
|
|
//err
|
|
|
|
//syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: proc_listpids() failed with %d", status);
|
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//iterate over all pids
|
|
|
|
// ->get name for each
|
|
|
|
for(int i = 0; i < numberOfProcesses; ++i)
|
|
|
|
{
|
|
|
|
//skip blank pids
|
|
|
|
if(0 == pids[i])
|
|
|
|
{
|
|
|
|
//skip
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
//skip if name doesn't match
|
|
|
|
if(YES != [processName isEqualToString:getProcessName(pids[i])])
|
|
|
|
{
|
|
|
|
//next
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
//got match
|
|
|
|
processID = pids[i];
|
|
|
|
|
|
|
|
//exit loop
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//bail
|
|
|
|
bail:
|
|
|
|
|
|
|
|
//free buffer
|
|
|
|
if(NULL != pids)
|
|
|
|
{
|
|
|
|
//free
|
|
|
|
free(pids);
|
|
|
|
|
|
|
|
//reset
|
|
|
|
pids = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return processID;
|
|
|
|
}
|
|
|
|
|
2016-09-13 08:27:45 +01:00
|
|
|
//determine if there is a new version
|
|
|
|
// -1, YES or NO
|
|
|
|
NSInteger isNewVersion(NSMutableString* versionString)
|
|
|
|
{
|
|
|
|
//flag
|
|
|
|
NSInteger newVersionExists = -1;
|
|
|
|
|
|
|
|
//installed version
|
|
|
|
NSString* installedVersion = nil;
|
|
|
|
|
|
|
|
//latest version
|
|
|
|
NSString* latestVersion = nil;
|
|
|
|
|
|
|
|
//get installed version
|
|
|
|
installedVersion = getAppVersion();
|
|
|
|
|
|
|
|
//get latest version
|
|
|
|
// ->will query internet (bb's website)
|
|
|
|
latestVersion = getLatestVersion();
|
|
|
|
if(nil == latestVersion)
|
|
|
|
{
|
|
|
|
//set error msg
|
|
|
|
[versionString setString:@"failed to get latest version"];
|
|
|
|
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//save version
|
|
|
|
[versionString setString:latestVersion];
|
|
|
|
|
|
|
|
//set version flag
|
|
|
|
// ->YES/NO
|
|
|
|
newVersionExists = (NSOrderedAscending == [installedVersion compare:latestVersion options:NSNumericSearch]);
|
|
|
|
|
|
|
|
//bail
|
|
|
|
bail:
|
|
|
|
|
|
|
|
return newVersionExists;
|
|
|
|
}
|
|
|
|
|
|
|
|
//query interwebz to get latest version
|
|
|
|
NSString* getLatestVersion()
|
|
|
|
{
|
|
|
|
//version data
|
|
|
|
NSData* versionData = nil;
|
|
|
|
|
|
|
|
//version dictionary
|
|
|
|
NSDictionary* versionDictionary = nil;
|
|
|
|
|
|
|
|
//latest version
|
|
|
|
NSString* latestVersion = nil;
|
|
|
|
|
|
|
|
//get version from remote URL
|
|
|
|
versionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSION_URL]];
|
|
|
|
|
|
|
|
//sanity check
|
|
|
|
if(nil == versionData)
|
|
|
|
{
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//convert JSON to dictionary
|
|
|
|
versionDictionary = [NSJSONSerialization JSONObjectWithData:versionData options:0 error:nil];
|
|
|
|
|
|
|
|
//sanity check
|
|
|
|
if(nil == versionDictionary)
|
|
|
|
{
|
|
|
|
//bail
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
//extract latest version
|
|
|
|
latestVersion = versionDictionary[@"latestVersion"];
|
|
|
|
|
|
|
|
//bail
|
|
|
|
bail:
|
|
|
|
|
|
|
|
return latestVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//wait until a window is non nil
|
|
|
|
// ->then make it modal
|
|
|
|
void makeModal(NSWindowController* windowController)
|
|
|
|
{
|
|
|
|
//wait up to 1 second window to be non-nil
|
|
|
|
// ->then make modal
|
|
|
|
for(int i=0; i<20; i++)
|
|
|
|
{
|
|
|
|
//can make it modal once we have a window
|
|
|
|
if(nil != windowController.window)
|
|
|
|
{
|
|
|
|
//make modal on main thread
|
|
|
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
|
|
//modal
|
|
|
|
[[NSApplication sharedApplication] runModalForWindow:windowController.window];
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
//all done
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//nap
|
|
|
|
[NSThread sleepForTimeInterval:0.05f];
|
|
|
|
|
|
|
|
}//until 1 second
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-15 09:15:30 +01:00
|
|
|
|
|
|
|
|
2016-09-25 04:08:38 +01:00
|
|
|
|