diff options
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/mac_updater/AutoUpdater.nib/classes.nib | 4 | ||||
| -rw-r--r-- | indra/mac_updater/AutoUpdater.nib/info.nib | 14 | ||||
| -rw-r--r-- | indra/mac_updater/AutoUpdater.nib/objects.xib | 56 | ||||
| -rw-r--r-- | indra/mac_updater/CMakeLists.txt | 22 | ||||
| -rw-r--r-- | indra/mac_updater/mac_updater.cpp | 1582 | 
5 files changed, 1167 insertions, 511 deletions
| diff --git a/indra/mac_updater/AutoUpdater.nib/classes.nib b/indra/mac_updater/AutoUpdater.nib/classes.nib new file mode 100644 index 0000000000..ea58db1189 --- /dev/null +++ b/indra/mac_updater/AutoUpdater.nib/classes.nib @@ -0,0 +1,4 @@ +{ +IBClasses = (); +IBVersion = 1; +} diff --git a/indra/mac_updater/AutoUpdater.nib/info.nib b/indra/mac_updater/AutoUpdater.nib/info.nib new file mode 100644 index 0000000000..a49a92385b --- /dev/null +++ b/indra/mac_updater/AutoUpdater.nib/info.nib @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> +	<key>IBDocumentLocation</key> +	<string>103 138 356 240 0 0 1280 1002 </string> +	<key>IBFramework Version</key> +	<string>362.0</string> +	<key>IBSystem Version</key> +	<string>7D24</string> +	<key>targetFramework</key> +	<string>IBCarbonFramework</string> +</dict> +</plist> diff --git a/indra/mac_updater/AutoUpdater.nib/objects.xib b/indra/mac_updater/AutoUpdater.nib/objects.xib new file mode 100644 index 0000000000..310411b711 --- /dev/null +++ b/indra/mac_updater/AutoUpdater.nib/objects.xib @@ -0,0 +1,56 @@ +<?xml version="1.0" standalone="yes"?> +<object class="NSIBObjectData"> +  <string name="targetFramework">IBCarbonFramework</string> +  <object name="rootObject" class="NSCustomObject" id="1"> +    <string name="customClass">NSApplication</string> +  </object> +  <array count="5" name="allObjects"> +    <object class="IBCarbonWindow" id="166"> +      <string name="windowRect">405 222 533 663 </string> +      <string name="title">Second Life Updater</string> +      <object name="rootControl" class="IBCarbonRootControl" id="167"> +        <string name="bounds">0 0 128 441 </string> +        <array count="3" name="subviews"> +          <object class="IBCarbonStaticText" id="181"> +            <string name="bounds">20 20 44 421 </string> +            <ostype name="controlSignature">what</ostype> +            <string name="title">Initializing…</string> +          </object> +          <object class="IBCarbonButton" id="183"> +            <string name="bounds">88 351 108 421 </string> +            <string name="title">Cancel</string> +            <ostype name="command">not!</ostype> +            <int name="buttonType">2</int> +          </object> +          <object class="IBCarbonProgressBar" id="193"> +            <string name="bounds">51 19 70 422 </string> +            <ostype name="controlSignature">prog</ostype> +            <int name="initialValue">50</int> +          </object> +        </array> +      </object> +      <boolean name="isResizable">FALSE</boolean> +      <int name="carbonWindowClass">2</int> +      <int name="themeBrush">3</int> +      <int name="windowPosition">7</int> +    </object> +    <reference idRef="167"/> +    <reference idRef="181"/> +    <reference idRef="183"/> +    <reference idRef="193"/> +  </array> +  <array count="5" name="allParents"> +    <reference idRef="1"/> +    <reference idRef="166"/> +    <reference idRef="167"/> +    <reference idRef="167"/> +    <reference idRef="167"/> +  </array> +  <dictionary count="2" name="nameTable"> +    <string>File's Owner</string> +    <reference idRef="1"/> +    <string>Updater</string> +    <reference idRef="166"/> +  </dictionary> +  <unsigned_int name="nextObjectID">194</unsigned_int> +</object> diff --git a/indra/mac_updater/CMakeLists.txt b/indra/mac_updater/CMakeLists.txt index 7382e912bf..00dcedecaa 100644 --- a/indra/mac_updater/CMakeLists.txt +++ b/indra/mac_updater/CMakeLists.txt @@ -18,14 +18,10 @@ include_directories(      )  set(mac_updater_SOURCE_FILES -    main.m -    MacUpdaterAppDelegate.mm      mac_updater.cpp      )  set(mac_updater_HEADER_FILES -    MacUpdaterAppDelegate.h -    mac_updater.h      CMakeLists.txt      ) @@ -36,7 +32,7 @@ list(APPEND mac_updater_SOURCE_FILES ${mac_updater_HEADER_FILES})  set(mac_updater_RESOURCE_FILES -  AutoUpdater.nib +  AutoUpdater.nib/    )  set_source_files_properties(    ${mac_updater_RESOURCE_FILES} @@ -52,19 +48,13 @@ add_executable(mac-updater  set_target_properties(mac-updater    PROPERTIES -  MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/MacUpdater-Info.plist +  MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist    ) -find_library(COCOA_LIBRARY Cocoa) -find_library(IOKIT_LIBRARY IOKit) -  target_link_libraries(mac-updater      ${LLVFS_LIBRARIES}      ${OPENSSL_LIBRARIES}      ${CRYPTO_LIBRARIES} -    ${COCOA_LIBRARIES} -    ${BOOST_FILESYSTEM_LIBRARY} -    ${IOKIT_LIBRARY}      ${CURL_LIBRARIES}      ${CARES_LIBRARIES}      ${LLCOMMON_LIBRARIES} @@ -72,16 +62,10 @@ target_link_libraries(mac-updater  add_custom_command(    TARGET mac-updater POST_BUILD -#  COMMAND ${CMAKE_COMMAND} -#  ARGS -#    -E -#    copy_directory -#    ${CMAKE_CURRENT_SOURCE_DIR}/AutoUpdater.nib -#    ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/mac-updater.app/Contents/Resources/AutoUpdater.nib    COMMAND ${CMAKE_COMMAND}    ARGS      -E -    copy +    copy_directory      ${CMAKE_CURRENT_SOURCE_DIR}/AutoUpdater.nib      ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/mac-updater.app/Contents/Resources/AutoUpdater.nib    ) diff --git a/indra/mac_updater/mac_updater.cpp b/indra/mac_updater/mac_updater.cpp index bc9fec3558..aa45c5d23f 100644 --- a/indra/mac_updater/mac_updater.cpp +++ b/indra/mac_updater/mac_updater.cpp @@ -27,8 +27,6 @@  #include "linden_common.h"  #include <boost/format.hpp> -#include <boost/filesystem/operations.hpp> -#include <boost/filesystem.hpp>  #include <libgen.h>  #include <sys/types.h> @@ -36,6 +34,7 @@  #include <unistd.h>  #include <curl/curl.h> +#include <pthread.h>  #include "llerror.h"  #include "lltimer.h" @@ -44,552 +43,1177 @@  #include "llstring.h" +#include <Carbon/Carbon.h> +  #include "llerrorcontrol.h" -#include "mac_updater.h" -#include <sstream> + +enum +{ +	kEventClassCustom = 'Cust', +	kEventCustomProgress = 'Prog', +	kEventParamCustomCurValue = 'Cur ', +	kEventParamCustomMaxValue = 'Max ', +	kEventParamCustomText = 'Text', +	kEventCustomDone = 'Done', +}; + +WindowRef gWindow = NULL; +EventHandlerRef gEventHandler = NULL; +OSStatus gFailure = noErr; +Boolean gCancelled = false; + +const char *gUpdateURL; +const char *gProductName; +const char *gBundleID; +const char *gDmgFile; +const char *gMarkerPath; + +void *updatethreadproc(void*);  pthread_t updatethread; -LLMacUpdater* LLMacUpdater::sInstance = NULL; +OSStatus setProgress(int cur, int max) +{ +	OSStatus err; +	ControlRef progressBar = NULL; +	ControlID id; + +	id.signature = 'prog'; +	id.id = 0; + +	err = GetControlByID(gWindow, &id, &progressBar); +	if(err == noErr) +	{ +		Boolean indeterminate; +		 +		if(max == 0) +		{ +			indeterminate = true; +			err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate); +		} +		else +		{ +			double percentage = (double)cur / (double)max; +			SetControlMinimum(progressBar, 0); +			SetControlMaximum(progressBar, 100); +			SetControlValue(progressBar, (SInt16)(percentage * 100)); + +			indeterminate = false; +			err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate); + +			Draw1Control(progressBar); +		} +	} + +	return(err); +} + +OSStatus setProgressText(CFStringRef text) +{ +	OSStatus err; +	ControlRef progressText = NULL; +	ControlID id; + +	id.signature = 'what'; +	id.id = 0; + +	err = GetControlByID(gWindow, &id, &progressText); +	if(err == noErr) +	{ +		err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&text); +		Draw1Control(progressText); +	} + +	return(err); +} + +OSStatus sendProgress(long cur, long max, CFStringRef text = NULL) +{ +	OSStatus result; +	EventRef evt; +	 +	result = CreateEvent(  +			NULL, +			kEventClassCustom,  +			kEventCustomProgress, +			0,  +			kEventAttributeNone,  +			&evt); +	 +	// This event needs to be targeted at the window so it goes to the window's handler. +	if(result == noErr) +	{ +		EventTargetRef target = GetWindowEventTarget(gWindow); +		result = SetEventParameter ( +			evt, +			kEventParamPostTarget, +			typeEventTargetRef, +			sizeof(target), +			&target); +	} + +	if(result == noErr) +	{ +		result = SetEventParameter ( +			evt, +			kEventParamCustomCurValue, +			typeLongInteger, +			sizeof(cur), +			&cur); +	} + +	if(result == noErr) +	{ +		result = SetEventParameter ( +			evt, +			kEventParamCustomMaxValue, +			typeLongInteger, +			sizeof(max), +			&max); +	} +	 +	if(result == noErr) +	{ +		if(text != NULL) +		{ +			result = SetEventParameter ( +				evt, +				kEventParamCustomText, +				typeCFStringRef, +				sizeof(text), +				&text); +		} +	} +	 +	if(result == noErr) +	{ +		// Send the event +		PostEventToQueue( +			GetMainEventQueue(), +			evt, +			kEventPriorityStandard); + +	} +	 +	return(result); +} + +OSStatus sendDone(void) +{ +	OSStatus result; +	EventRef evt; +	 +	result = CreateEvent(  +			NULL, +			kEventClassCustom,  +			kEventCustomDone, +			0,  +			kEventAttributeNone,  +			&evt); +	 +	// This event needs to be targeted at the window so it goes to the window's handler. +	if(result == noErr) +	{ +		EventTargetRef target = GetWindowEventTarget(gWindow); +		result = SetEventParameter ( +			evt, +			kEventParamPostTarget, +			typeEventTargetRef, +			sizeof(target), +			&target); +	} + +	if(result == noErr) +	{ +		// Send the event +		PostEventToQueue( +			GetMainEventQueue(), +			evt, +			kEventPriorityStandard); -LLMacUpdater::LLMacUpdater(): -                mUpdateURL   (NULL), -                mProductName (NULL), -                mBundleID    (NULL), -                mDmgFile     (NULL), -                mMarkerPath  (NULL) +	} +	 +	return(result); +} + +OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)  { -    sInstance    = this; +	OSStatus result = eventNotHandledErr; +	OSStatus err; +	UInt32 evtClass = GetEventClass(event); +	UInt32 evtKind = GetEventKind(event); +	 +	if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess)) +	{ +		HICommand cmd; +		err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd); +		 +		if(err == noErr) +		{ +			switch(cmd.commandID) +			{				 +				case kHICommandCancel: +					gCancelled = true; +//					QuitAppModalLoopForWindow(gWindow); +					result = noErr; +				break; +			} +		} +	} +	else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomProgress)) +	{ +		// Request to update the progress dialog +		long cur = 0; +		long max = 0; +		CFStringRef text = NULL; +		(void) GetEventParameter(event, kEventParamCustomCurValue, typeLongInteger, NULL, sizeof(cur), NULL, &cur); +		(void) GetEventParameter(event, kEventParamCustomMaxValue, typeLongInteger, NULL, sizeof(max), NULL, &max); +		(void) GetEventParameter(event, kEventParamCustomText, typeCFStringRef, NULL, sizeof(text), NULL, &text); +		 +		err = setProgress(cur, max); +		if(err == noErr) +		{ +			if(text != NULL) +			{ +				setProgressText(text); +			} +		} +		 +		result = noErr; +	} +	else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomDone)) +	{ +		// We're done.  Exit the modal loop. +		QuitAppModalLoopForWindow(gWindow); +		result = noErr; +	} +	 +	return(result);  } -void LLMacUpdater::doUpdate() +#if 0 +size_t curl_download_callback(void *data, size_t size, size_t nmemb, +										  void *user_data)  { -  	// We assume that all the logs we're looking for reside on the current drive +	S32 bytes = size * nmemb; +	char *cdata = (char *) data; +	for (int i =0; i < bytes; i += 1) +	{ +		gServerResponse.append(cdata[i]); +	} +	return bytes; +} +#endif + +int curl_progress_callback_func(void *clientp, +							  double dltotal, +							  double dlnow, +							  double ultotal, +							  double ulnow) +{ +	int max = (int)(dltotal / 1024.0); +	int cur = (int)(dlnow / 1024.0); +	sendProgress(cur, max); +	 +	if(gCancelled) +		return(1); + +	return(0); +} + +int parse_args(int argc, char **argv) +{ +	int j; + +	for (j = 1; j < argc; j++)  +	{ +		if ((!strcmp(argv[j], "-url")) && (++j < argc))  +		{ +			gUpdateURL = argv[j]; +		} +		else if ((!strcmp(argv[j], "-name")) && (++j < argc))  +		{ +			gProductName = argv[j]; +		} +		else if ((!strcmp(argv[j], "-bundleid")) && (++j < argc))  +		{ +			gBundleID = argv[j]; +		} +		else if ((!strcmp(argv[j], "-dmg")) && (++j < argc))  +		{ +			gDmgFile = argv[j]; +		} +		else if ((!strcmp(argv[j], "-marker")) && (++j < argc))  +		{ +			gMarkerPath = argv[j];; +		} +	} + +	return 0; +} + +int main(int argc, char **argv) +{ +	// We assume that all the logs we're looking for reside on the current drive  	gDirUtilp->initAppDirs("SecondLife"); -     +  	LLError::initForApplication( gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); -     +  	// Rename current log file to ".old"  	std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "updater.log.old");  	std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "updater.log");  	LLFile::rename(log_file.c_str(), old_log_file.c_str()); -     +  	// Set the log file to updater.log  	LLError::logToFile(log_file); -     -	if ((mUpdateURL == NULL) && (mDmgFile == NULL)) + +	///////////////////////////////////////// +	// +	// Process command line arguments +	// +	gUpdateURL  = NULL; +	gProductName = NULL; +	gBundleID = NULL; +	gDmgFile = NULL; +	gMarkerPath = NULL; +	parse_args(argc, argv); +	if ((gUpdateURL == NULL) && (gDmgFile == NULL))  	{  		llinfos << "Usage: mac_updater -url <url> | -dmg <dmg file> [-name <product_name>] [-program <program_name>]" << llendl;  		exit(1);  	}  	else  	{ -		llinfos << "Update url is: " << mUpdateURL << llendl; -		if (mProductName) +		llinfos << "Update url is: " << gUpdateURL << llendl; +		if (gProductName)  		{ -			llinfos << "Product name is: " << *mProductName << llendl; +			llinfos << "Product name is: " << gProductName << llendl;  		}  		else  		{ -			mProductName = new std::string("Second Life"); +			gProductName = "Second Life";  		} -		if (mBundleID) +		if (gBundleID)  		{ -			llinfos << "Bundle ID is: " << *mBundleID << llendl; +			llinfos << "Bundle ID is: " << gBundleID << llendl;  		}  		else  		{ -			mBundleID = new std::string("com.secondlife.indra.viewer"); +			gBundleID = "com.secondlife.indra.viewer";  		}  	} -	llinfos << "Starting " << *mProductName << " Updater" << llendl; -     -    pthread_create(&updatethread,  -                   NULL, -                   &sUpdatethreadproc,  -                   NULL); -     -     +	llinfos << "Starting " << gProductName << " Updater" << llendl; + +	// Real UI... +	OSStatus err; +	IBNibRef nib = NULL; +	 +	err = CreateNibReference(CFSTR("AutoUpdater"), &nib); + +	char windowTitle[MAX_PATH];		/* Flawfinder: ignore */ +	snprintf(windowTitle, sizeof(windowTitle), "%s Updater", gProductName);		 +	CFStringRef windowTitleRef = NULL; +	windowTitleRef = CFStringCreateWithCString(NULL, windowTitle, kCFStringEncodingUTF8); +	 +	if(err == noErr) +	{ +		err = CreateWindowFromNib(nib, CFSTR("Updater"), &gWindow); +	} + +	if (err == noErr) +	{ +		err = SetWindowTitleWithCFString(gWindow, windowTitleRef);	 +	} +	CFRelease(windowTitleRef); + +	if(err == noErr) +	{ +		// Set up an event handler for the window. +		EventTypeSpec handlerEvents[] =  +		{ +			{ kEventClassCommand, kEventCommandProcess }, +			{ kEventClassCustom, kEventCustomProgress }, +			{ kEventClassCustom, kEventCustomDone } +		}; +		InstallStandardEventHandler(GetWindowEventTarget(gWindow)); +		InstallWindowEventHandler( +				gWindow,  +				NewEventHandlerUPP(dialogHandler),  +				GetEventTypeCount (handlerEvents),  +				handlerEvents,  +				0,  +				&gEventHandler); +	} +	 +	if(err == noErr) +	{ +		ShowWindow(gWindow); +		SelectWindow(gWindow); +	} +		 +	if(err == noErr) +	{ +		pthread_create(&updatethread,  +                         NULL, +                         &updatethreadproc,  +                         NULL); +						  +	} +	 +	if(err == noErr) +	{ +		RunAppModalLoopForWindow(gWindow); +	} +  	void *threadresult; -     +  	pthread_join(updatethread, &threadresult); -     -	if(gCancelled || gFailure) + +	if(!gCancelled && (gFailure != noErr))  	{ -        sendStopAlert(); -         -		if(mMarkerPath != 0) +		// Something went wrong.  Since we always just tell the user to download a new version, we don't really care what. +		AlertStdCFStringAlertParamRec params; +		SInt16 retval_mac = 1; +		DialogRef alert = NULL; +		OSStatus err; + +		params.version = kStdCFStringAlertVersionOne; +		params.movable = false; +		params.helpButton = false; +		params.defaultText = (CFStringRef)kAlertDefaultOKText; +		params.cancelText = 0; +		params.otherText = 0; +		params.defaultButton = 1; +		params.cancelButton = 0; +		params.position = kWindowDefaultPosition; +		params.flags = 0; + +		err = CreateStandardAlert( +				kAlertStopAlert, +				CFSTR("Error"), +				CFSTR("An error occurred while updating Second Life.  Please download the latest version from www.secondlife.com."), +				¶ms, +				&alert); +		 +		if(err == noErr) +		{ +			err = RunStandardAlert( +					alert, +					NULL, +					&retval_mac); +		} +		 +		if(gMarkerPath != 0)  		{  			// Create a install fail marker that can be used by the viewer to  			// detect install problems. -			std::ofstream stream(mMarkerPath->c_str()); +			std::ofstream stream(gMarkerPath);  			if(stream) stream << -1;  		}  		exit(-1);  	} else {  		exit(0);  	} + +	if(gWindow != NULL) +	{ +		DisposeWindow(gWindow); +	} -	return;   +	if(nib != NULL) +	{ +		DisposeNibReference(nib); +	} +	 +	return 0;  } -//SPATTERS TODO this should be moved to lldir_mac.cpp -const std::string LLMacUpdater::walkParents( signed int depth, const std::string& childpath ) +bool isDirWritable(FSRef &dir)  { -    boost::filesystem::path  fullpath(childpath.c_str()); -     -    while (depth > 0 && fullpath.has_parent_path()) -    { -        fullpath = boost::filesystem::path(fullpath.parent_path()); -        --depth; -    } -     -    return fullpath.string(); -} +	bool result = false; +	 +	// Test for a writable directory by creating a directory, then deleting it again. +	// This is kinda lame, but will pretty much always give the right answer. +	 +	OSStatus err = noErr; +	char temp[PATH_MAX] = "";		/* Flawfinder: ignore */ -//#if 0 -//size_t curl_download_callback(void *data, size_t size, size_t nmemb, -//										  void *user_data) -//{ -//	S32 bytes = size * nmemb; -//	char *cdata = (char *) data; -//	for (int i =0; i < bytes; i += 1) -//	{ -//		gServerResponse.append(cdata[i]); -//	} -//	return bytes; -//} -//#endif +	err = FSRefMakePath(&dir, (UInt8*)temp, sizeof(temp)); -int curl_progress_callback_func(void *clientp, -							  double dltotal, -							  double dlnow, -							  double ultotal, -							  double ulnow) -{ -	int max = (int)(dltotal / 1024.0); -	int cur = (int)(dlnow / 1024.0); -	setProgress(cur, max); -	 -	if(gCancelled) -		return(1); +	if(err == noErr) +	{ +		strncat(temp, "/.test_XXXXXX", (sizeof(temp) - strlen(temp)) - 1); +		 +		if(mkdtemp(temp) != NULL) +		{ +			// We were able to make the directory.  This means the directory is writable. +			result = true; +			 +			// Clean up. +			rmdir(temp); +		} +	} -	return(0); -} +#if 0 +	// This seemed like a good idea, but won't tell us if we're on a volume mounted read-only. +	UInt8 perm; +	err = FSGetUserPrivilegesPermissions(&targetParentRef, &perm, NULL); +	if(err == noErr) +	{ +		if(perm & kioACUserNoMakeChangesMask) +		{ +			// Parent directory isn't writable. +			llinfos << "Target parent directory not writable." << llendl; +			err = -1; +			replacingTarget = false; +		} +	} +#endif -bool LLMacUpdater::isApplication(const std::string& app_str) -{ -    return  !(bool) app_str.compare( app_str.length()-4, 4, ".app"); +	return result;  } -                                      -// Search through the directory specified by 'parent' for an item that appears to be a Second Life viewer. -bool LLMacUpdater::findAppBundleOnDiskImage(const boost::filesystem::path& dir_path, -                              boost::filesystem::path& path_found) + +static std::string HFSUniStr255_to_utf8str(const HFSUniStr255* src)  { -    if ( !boost::filesystem::exists( dir_path ) ) return false; - -    boost::filesystem::directory_iterator end_itr;  -         -    for ( boost::filesystem::directory_iterator itr( dir_path ); -         itr != end_itr; -         ++itr ) -    { -        if ( boost::filesystem::is_directory(itr->status()) ) -        { -            std::string dir_name = itr->path().string(); -            if ( isApplication(dir_name) )  -            { -                if(isFSRefViewerBundle(dir_name)) -                { -                    llinfos << dir_name << " is the one" << llendl; - -                    path_found = itr->path(); -                    return true; -                } -            } -        } -    } -    return false; +	llutf16string string16((U16*)&(src->unicode), src->length); +	std::string result = utf16str_to_utf8str(string16); +	return result;  } -bool LLMacUpdater::verifyDirectory(const boost::filesystem::path* directory, bool isParent) +int restoreObject(const char* aside, const char* target, const char* path, const char* object)  { -    bool replacingTarget; -    std::string app_str = directory->string();  -         -    if (boost::filesystem::is_directory(*directory)) -    {         -        // This is fine, just means we're not replacing anything. -        replacingTarget = true; -    } -    else -    { -        replacingTarget = isParent; -    } -     -    //Check that the directory is writeable.  -    if(!isDirWritable(app_str)) -    { -        // Parent directory isn't writable. -        llinfos << "Target directory not writable." << llendl; -        replacingTarget = false; -    } -    return replacingTarget; +	char source[PATH_MAX] = "";		/* Flawfinder: ignore */ +	char dest[PATH_MAX] = "";		/* Flawfinder: ignore */ +	snprintf(source, sizeof(source), "%s/%s/%s", aside, path, object);		 +	snprintf(dest, sizeof(dest), "%s/%s", target, path);		 +	FSRef sourceRef; +	FSRef destRef; +	OSStatus err; +	err = FSPathMakeRef((UInt8 *)source, &sourceRef, NULL); +	if(err != noErr) return false; +	err = FSPathMakeRef((UInt8 *)dest, &destRef, NULL); +	if(err != noErr) return false; + +	llinfos << "Copying " << source << " to " << dest << llendl; + +	err = FSCopyObjectSync( +			&sourceRef, +			&destRef, +			NULL, +			NULL, +			kFSFileOperationOverwrite); + +	if(err != noErr) return false; +	return true;  } -                                      -bool LLMacUpdater::getViewerDir(boost::filesystem::path &app_dir) + +// Replace any mention of "Second Life" with the product name. +void filterFile(const char* filename)  { -    std::string  app_dir_str; -     -    //Walk up 6 levels from the App Updater's installation point. -    app_dir_str = walkParents( 6, *mApplicationPath ); - -    app_dir = boost::filesystem::path(app_dir_str); -     -    //Check to see that the directory's name ends in .app  Lame but it's the best thing we have to go on. -    //If it's not there, we're going to default to /Applications/VIEWERNAME -    if (!isApplication(app_dir_str)) -    { -        llinfos << "Target search failed, defaulting to /Applications/" << *mProductName << ".app." << llendl; -        std::string newpath = std::string("/Applications/") + mProductName->c_str(); -        app_dir = boost::filesystem::path(newpath); -    }     -    return verifyDirectory(&app_dir);     +	char temp[PATH_MAX] = "";		/* Flawfinder: ignore */ +	// First copy the target's version, so we can run it through sed. +	snprintf(temp, sizeof(temp), "cp '%s' '%s.tmp'", filename, filename);		 +	system(temp);		/* Flawfinder: ignore */ + +	// Now run it through sed. +	snprintf(temp, sizeof(temp), 		 +			"sed 's/Second Life/%s/g' '%s.tmp' > '%s'", gProductName, filename, filename); +	system(temp);		/* Flawfinder: ignore */  } -bool LLMacUpdater::downloadDMG(const std::string& dmgName, boost::filesystem::path* temp_dir) +static bool isFSRefViewerBundle(FSRef *targetRef)  { -	LLFILE *downloadFile = NULL; -	char temp[PATH_MAX] = "";	/* Flawfinder: ignore */ +	bool result = false; +	CFURLRef targetURL = NULL; +	CFBundleRef targetBundle = NULL; +	CFStringRef targetBundleID = NULL; +	CFStringRef sourceBundleID = NULL; -    chdir(temp_dir->string().c_str()); -     -    snprintf(temp, sizeof(temp), "SecondLife.dmg");		 -     -    downloadFile = LLFile::fopen(temp, "wb");		/* Flawfinder: ignore */ -    if(downloadFile == NULL) -    { -        return false; -    } -     -    bool success = false; -     -    CURL *curl = curl_easy_init(); -     -    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); -    //		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback); -    curl_easy_setopt(curl, CURLOPT_FILE, downloadFile); -    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); -    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &curl_progress_callback_func); -    curl_easy_setopt(curl, CURLOPT_URL,	mUpdateURL); -    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); -     -    sendProgress(0, 1, std::string("Downloading...")); -     -    CURLcode result = curl_easy_perform(curl); -     -    curl_easy_cleanup(curl); -     -    if(gCancelled) -    { -        llinfos << "User cancel, bailing out."<< llendl; -        goto close_file; -    } -     -    if(result != CURLE_OK) -    { -        llinfos << "Error " << result << " while downloading disk image."<< llendl; -        goto close_file; -    } -     -    fclose(downloadFile); -    downloadFile = NULL; -     -    success = true; -     -close_file: -    // Close disk image file if necessary -	if(downloadFile != NULL) +	targetURL = CFURLCreateFromFSRef(NULL, targetRef); + +	if(targetURL == NULL)  	{ -		llinfos << "Closing download file." << llendl; -         -		fclose(downloadFile); -		downloadFile = NULL; +		llinfos << "Error creating target URL." << llendl;  	} - -    return success; -} -                                      -bool LLMacUpdater::doMount(const std::string& dmgName, char* deviceNode, const boost::filesystem::path& temp_dir) -{ -	char temp[PATH_MAX] = "";	/* Flawfinder: ignore */ -     -    sendProgress(0, 0, std::string("Mounting image...")); -    chdir(temp_dir.string().c_str()); -    std::string mnt_dir = temp_dir.string() + std::string("/mnt"); -    LLFile::mkdir(mnt_dir.c_str(), 0700); -     -    // NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder, -    //		but if our cleanup fails, this makes it much harder for the user to unmount the image. -    std::string mountOutput; -    boost::format cmdFormat("hdiutil attach %s -mountpoint mnt"); -    cmdFormat % dmgName; -    FILE* mounter = popen(cmdFormat.str().c_str(), "r");		/* Flawfinder: ignore */ -     -    if(mounter == NULL) -    { -        llinfos << "Failed to mount disk image, exiting."<< llendl; -        return false; -    } -     -    // We need to scan the output from hdiutil to find the device node it uses to attach the disk image. -    // If we don't have this information, we can't detach it later. -    while(mounter != NULL) -    { -        size_t len = fread(temp, 1, sizeof(temp)-1, mounter); -        temp[len] = 0; -        mountOutput.append(temp); -        if(len < sizeof(temp)-1) -        { -            // End of file or error. -            int result = pclose(mounter); -            if(result != 0) -            { -                // NOTE: We used to abort here, but pclose() started returning  -                // -1, possibly when the size of the DMG passed a certain point  -                llinfos << "Unexpected result closing pipe: " << result << llendl;  -            } -            mounter = NULL; -        } -    } -     -    if(!mountOutput.empty()) -    { -        const char *s = mountOutput.c_str(); -        const char *prefix = "/dev/"; -        char *sub = strstr(s, prefix); -         -        if(sub != NULL) -        { -            sub += strlen(prefix);	/* Flawfinder: ignore */ -            sscanf(sub, "%1023s", deviceNode);	/* Flawfinder: ignore */ -        } -    } -     -    if(deviceNode[0] != 0) -    { -        llinfos << "Disk image attached on /dev/" << deviceNode << llendl; -    } -    else -    { -        llinfos << "Disk image device node not found!" << llendl; -        return false;  -    } -     -    return true; +	else +	{ +		targetBundle = CFBundleCreate(NULL, targetURL); +	} +	 +	if(targetBundle == NULL) +	{ +		llinfos << "Failed to create target bundle." << llendl; +	} +	else +	{ +		targetBundleID = CFBundleGetIdentifier(targetBundle); +	} +	 +	if(targetBundleID == NULL) +	{ +		llinfos << "Couldn't retrieve target bundle ID." << llendl; +	} +	else +	{ +		sourceBundleID = CFStringCreateWithCString(NULL, gBundleID, kCFStringEncodingUTF8); +		if(CFStringCompare(sourceBundleID, targetBundleID, 0) == kCFCompareEqualTo) +		{ +			// This is the bundle we're looking for. +			result = true; +		} +		else +		{ +			llinfos << "Target bundle ID mismatch." << llendl; +		} +	} +	 +	// Don't release targetBundleID -- since we don't retain it, it's released when targetBundle is released. +	if(targetURL != NULL) +		CFRelease(targetURL); +	if(targetBundle != NULL) +		CFRelease(targetBundle); +	 +	return result;  } -bool LLMacUpdater::moveApplication (const boost::filesystem::path& app_dir,  -                 const boost::filesystem::path& temp_dir,  -                 boost::filesystem::path& aside_dir) +// Search through the directory specified by 'parent' for an item that appears to be a Second Life viewer. +static OSErr findAppBundleOnDiskImage(FSRef *parent, FSRef *app)  { -    try -    { -        //Grab filename from installdir append to tempdir move set aside_dir to moved path. -        std::string install_str = app_dir.parent_path().string(); -        std::string temp_str = temp_dir.string(); -        std::string app_str = app_dir.filename().string(); -        aside_dir = boost::filesystem::path( boost::filesystem::operator/(temp_dir,app_str) ); -        std::cout << "Attempting to move " << app_dir.string() << " to " << aside_dir.string() << std::endl; -     -        boost::filesystem::rename(app_dir, aside_dir); -    } -    catch(boost::filesystem::filesystem_error e)  -    { -        llinfos << "Application move failed." << llendl; -        return false; -    } -    return true; -} +	FSIterator		iterator; +	bool			found = false; + +	OSErr err = FSOpenIterator( parent, kFSIterateFlat, &iterator ); +	if(!err) +	{ +		do +		{ +			ItemCount actualObjects = 0; +			Boolean containerChanged = false; +			FSCatalogInfo info; +			FSRef ref; +			HFSUniStr255 unicodeName; +			err = FSGetCatalogInfoBulk(  +					iterator,  +					1,  +					&actualObjects,  +					&containerChanged, +					kFSCatInfoNodeFlags,  +					&info,  +					&ref, +					NULL,  +					&unicodeName ); +			 +			if(actualObjects == 0) +				break; +				 +			if(!err) +			{ +				// Call succeeded and not done with the iteration. +				std::string name = HFSUniStr255_to_utf8str(&unicodeName); + +				llinfos << "Considering \"" << name << "\"" << llendl; + +				if(info.nodeFlags & kFSNodeIsDirectoryMask) +				{ +					// This is a directory.  See if it's a .app +					if(name.find(".app") != std::string::npos) +					{ +						// Looks promising.  Check to see if it has the right bundle identifier. +						if(isFSRefViewerBundle(&ref)) +						{ +							llinfos << name << " is the one" << llendl; +							// This is the one.  Return it. +							*app = ref; +							found = true; +							break; +						} else { +							llinfos << name << " is not the bundle we are looking for; move along" << llendl; +						} -bool LLMacUpdater::doInstall(const boost::filesystem::path& app_dir,  -               const boost::filesystem::path& temp_dir, -               boost::filesystem::path& mount_dir, -               bool replacingTarget) -{        -    std::string temp_name = temp_dir.string() + std::string("/mnt"); -     -    llinfos << "Disk image mount point is: " << temp_name << llendl; -     -    mount_dir = boost::filesystem::path(temp_name.c_str()); -     -    if (! boost::filesystem::exists ( mount_dir ) ) -    { -        llinfos << "Couldn't make FSRef to disk image mount point." << llendl; -        return false; -    } -     -    sendProgress(0, 0, std::string("Searching for the app bundle...")); -     -    boost::filesystem::path source_dir; -     -    if ( !findAppBundleOnDiskImage(mount_dir, source_dir) ) -    { -        llinfos << "Couldn't find application bundle on mounted disk image." << llendl; -        return false; -    } -    else -    { -        llinfos << "found the bundle." << llendl; -    } -     -    sendProgress(0, 0, std::string("Preparing to copy files...")); -     -    // this will hold the name of the destination target -    boost::filesystem::path aside_dir; -     -    if(replacingTarget) -    { -         -        if (! moveApplication (app_dir, temp_dir, aside_dir) ) -        { -            llwarns << "failed to move aside old version." << llendl; -            return false; -        } -    } -     -    sendProgress(0, 0, std::string("Copying files...")); -     -    llinfos << "Starting copy..." << llendl; -    //  If we were replacingTarget, we've moved the app to a temp directory. -    //  Otherwise the destination should be empty. -    //  We have mounted the DMG as a volume so we should be able to just  -    //  move the app from the volume to the destination and everything  will just work. -     -     -    // Copy the new version from the disk image to the target location.    -  -    //The installer volume is mounted read-only so we can't move.  Instead copy and then unmount. -    if (! copyDir(source_dir.string(), app_dir.string()) ) -    { -        llwarns << "Failed to copy " << source_dir.string() << " to " << app_dir.string() << llendl; -         -        // Something went wrong during the copy.  Attempt to put the old version back and bail. -        boost::filesystem::rename(app_dir, aside_dir); -        return false; -         -    } -         -    // The update has succeeded.  Clear the cache directory. -     -    sendProgress(0, 0, std::string("Clearing cache...")); -     -    llinfos << "Clearing cache..." << llendl; -     -    gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), "*.*"); -     -    llinfos << "Clear complete." << llendl; -         -    return true; +					} +				} +			} +		} +		while(!err); +		 +		llinfos << "closing the iterator" << llendl; +		 +		FSCloseIterator(iterator); +		 +		llinfos << "closed" << llendl; +	} +	 +	if(!err && !found) +		err = fnfErr; +		 +	return err;  } -void* LLMacUpdater::updatethreadproc(void*) +void *updatethreadproc(void*)  {  	char tempDir[PATH_MAX] = "";		/* Flawfinder: ignore */ +	FSRef tempDirRef;  	char temp[PATH_MAX] = "";	/* Flawfinder: ignore */  	// *NOTE: This buffer length is used in a scanf() below.  	char deviceNode[1024] = "";	/* Flawfinder: ignore */ -     -	bool replacingTarget = false; - -    boost::filesystem::path app_dir; -    boost::filesystem::path temp_dir; -    boost::filesystem::path mount_dir; -     -    // Attempt to get a reference to the Second Life application bundle containing this updater. -    // Any failures during this process will cause us to default to updating /Applications/Second Life.app +	LLFILE *downloadFile = NULL; +	OSStatus err; +	ProcessSerialNumber psn; +	char target[PATH_MAX] = "";		/* Flawfinder: ignore */ +	FSRef targetRef; +	FSRef targetParentRef; +	FSVolumeRefNum targetVol; +	FSRef trashFolderRef; +	Boolean replacingTarget = false; +	memset(&tempDirRef, 0, sizeof(tempDirRef)); +	memset(&targetRef, 0, sizeof(targetRef)); +	memset(&targetParentRef, 0, sizeof(targetParentRef)); +	  	try -	{         -        replacingTarget = getViewerDir( app_dir ); -         -        if (!mkTempDir(temp_dir)) -        { -            throw 0; -        } -        -        //In case the dir doesn't exist, try to create it.  If create fails, verify it exists.  -        if (! boost::filesystem::create_directory(app_dir)) -        { - - -            if(isFSRefViewerBundle(app_dir.string())) -            { -                // This is the bundle we're looking for. -                replacingTarget = true; -            } -            else  -            { -                throw 0;  -            } -        } -         -        if ( !verifyDirectory(&app_dir, true) ) -        { -            // We're so hosed. -            llinfos << "Applications directory not found, giving up." << llendl; -            throw 0; -        }     +	{ +		// Attempt to get a reference to the Second Life application bundle containing this updater. +		// Any failures during this process will cause us to default to updating /Applications/Second Life.app +		{ +			FSRef myBundle; + +			err = GetCurrentProcess(&psn); +			if(err == noErr) +			{ +				err = GetProcessBundleLocation(&psn, &myBundle); +			} + +			if(err == noErr) +			{ +				// Sanity check:  Make sure the name of the item referenced by targetRef is "Second Life.app". +				FSRefMakePath(&myBundle, (UInt8*)target, sizeof(target)); +				 +				llinfos << "Updater bundle location: " << target << llendl; +			} +			 +			// Our bundle should be in Second Life.app/Contents/Resources/AutoUpdater.app +			// so we need to go up 3 levels to get the path to the main application bundle. +			if(err == noErr) +			{ +				err = FSGetCatalogInfo(&myBundle, kFSCatInfoNone, NULL, NULL, NULL, &targetRef); +			} +			if(err == noErr) +			{ +				err = FSGetCatalogInfo(&targetRef, kFSCatInfoNone, NULL, NULL, NULL, &targetRef); +			} +			if(err == noErr) +			{ +				err = FSGetCatalogInfo(&targetRef, kFSCatInfoNone, NULL, NULL, NULL, &targetRef); +			} +			 +			// And once more to get the parent of the target +			if(err == noErr) +			{ +				err = FSGetCatalogInfo(&targetRef, kFSCatInfoNone, NULL, NULL, NULL, &targetParentRef); +			} +			 +			if(err == noErr) +			{ +				FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target)); +				llinfos << "Path to target: " << target << llendl; +			} +			 +			// Sanity check: make sure the target is a bundle with the right identifier +			if(err == noErr) +			{ +				// Assume the worst... +				err = -1; + +				if(isFSRefViewerBundle(&targetRef)) +				{ +					// This is the bundle we're looking for. +					err = noErr; +					replacingTarget = true; +				} +			} +			 +			// Make sure the target's parent directory is writable. +			if(err == noErr) +			{ +				if(!isDirWritable(targetParentRef)) +				{ +					// Parent directory isn't writable. +					llinfos << "Target parent directory not writable." << llendl; +					err = -1; +					replacingTarget = false; +				} +			} + +			if(err != noErr) +			{ +				Boolean isDirectory; +				llinfos << "Target search failed, defaulting to /Applications/" << gProductName << ".app." << llendl; +				 +				// Set up the parent directory +				err = FSPathMakeRef((UInt8*)"/Applications", &targetParentRef, &isDirectory); +				if((err != noErr) || (!isDirectory)) +				{ +					// We're so hosed. +					llinfos << "Applications directory not found, giving up." << llendl; +					throw 0; +				} +				 +				snprintf(target, sizeof(target), "/Applications/%s.app", gProductName);		 + +				memset(&targetRef, 0, sizeof(targetRef)); +				err = FSPathMakeRef((UInt8*)target, &targetRef, NULL); +				if(err == fnfErr) +				{ +					// This is fine, just means we're not replacing anything. +					err = noErr; +					replacingTarget = false; +				} +				else +				{ +					replacingTarget = true; +				} + +				// Make sure the target's parent directory is writable. +				if(err == noErr) +				{ +					if(!isDirWritable(targetParentRef)) +					{ +						// Parent directory isn't writable. +						llinfos << "Target parent directory not writable." << llendl; +						err = -1; +						replacingTarget = false; +					} +				} + +			} +			 +			// If we haven't fixed all problems by this point, just bail. +			if(err != noErr) +			{ +				llinfos << "Unable to pick a target, giving up." << llendl; +				throw 0; +			} +		} +		 +		// Find the volID of the volume the target resides on +		{ +			FSCatalogInfo info; +			err = FSGetCatalogInfo( +				&targetParentRef, +				kFSCatInfoVolume, +				&info, +				NULL,  +				NULL,   +				NULL); +				 +			if(err != noErr) +				throw 0; +			 +			targetVol = info.volume; +		} + +		// Find the temporary items and trash folders on that volume. +		err = FSFindFolder( +			targetVol, +			kTrashFolderType, +			true, +			&trashFolderRef); + +		if(err != noErr) +			throw 0; + +#if 0 // *HACK for DEV-11935 see below for details. + +		FSRef tempFolderRef; + +		err = FSFindFolder( +			targetVol, +			kTemporaryFolderType, +			true, +			&tempFolderRef); +		 +		if(err != noErr) +			throw 0; +		 +		err = FSRefMakePath(&tempFolderRef, (UInt8*)temp, sizeof(temp)); + +		if(err != noErr) +			throw 0; + +#else		 + +		// *HACK for DEV-11935  the above kTemporaryFolderType query was giving +		// back results with path names that seem to be too long to be used as +		// mount points.  I suspect this incompatibility was introduced in the +		// Leopard 10.5.2 update, but I have not verified this.  +		char const HARDCODED_TMP[] = "/tmp"; +		strncpy(temp, HARDCODED_TMP, sizeof(HARDCODED_TMP)); + +#endif // 0 *HACK for DEV-11935  		// Skip downloading the file if the dmg was passed on the command line.  		std::string dmgName; -		if(mDmgFile != NULL) { -            //Create a string from the mDmgFile then a dir reference to that. -            //change to that directory and begin install. -             -            boost::filesystem::path dmg_path(*mDmgFile); -             -			dmgName = dmg_path.string();   -            std::string* dmgPath = new std::string(dmg_path.parent_path().string()); -            if ( !boost::filesystem::exists( dmg_path.parent_path() ) )            { -                llinfos << "Path " << *dmgPath << " is not writeable.   Aborting." << llendl; -                throw 0; -            } - -			chdir(dmgPath->c_str()); +		if(gDmgFile != NULL) { +			dmgName = basename((char *)gDmgFile); +			char * dmgDir = dirname((char *)gDmgFile); +			strncpy(tempDir, dmgDir, sizeof(tempDir)); +			err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL); +			if(err != noErr) throw 0; +			chdir(tempDir); +			goto begin_install;  		} else {  			// Continue on to download file.  			dmgName = "SecondLife.dmg"; -             - -            if (!downloadDMG(dmgName, &temp_dir)) -            { -                throw 0; -            } -        } -         -        if (!doMount(dmgName, deviceNode, temp_dir)) -        { -            throw 0; -        } -         -        if (!doInstall( app_dir, temp_dir, mount_dir, replacingTarget )) -        { -            throw 0; -        } +		} + +		 +		strncat(temp, "/SecondLifeUpdate_XXXXXX", (sizeof(temp) - strlen(temp)) - 1); +		if(mkdtemp(temp) == NULL) +		{ +			throw 0; +		} +		 +		strncpy(tempDir, temp, sizeof(tempDir)); +		temp[sizeof(tempDir) - 1] = '\0'; +		 +		llinfos << "tempDir is " << tempDir << llendl; + +		err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL); + +		if(err != noErr) +			throw 0; +				 +		chdir(tempDir); +		 +		snprintf(temp, sizeof(temp), "SecondLife.dmg");		 +		 +		downloadFile = LLFile::fopen(temp, "wb");		/* Flawfinder: ignore */ +		if(downloadFile == NULL) +		{ +			throw 0; +		} + +		{ +			CURL *curl = curl_easy_init(); + +			curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); +	//		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback); +			curl_easy_setopt(curl, CURLOPT_FILE, downloadFile); +			curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); +			curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &curl_progress_callback_func); +			curl_easy_setopt(curl, CURLOPT_URL,	gUpdateURL); +			curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); +			 +			sendProgress(0, 1, CFSTR("Downloading...")); +			 +			CURLcode result = curl_easy_perform(curl); +			 +			curl_easy_cleanup(curl); +			 +			if(gCancelled) +			{ +				llinfos << "User cancel, bailing out."<< llendl; +				throw 0; +			} +			 +			if(result != CURLE_OK) +			{ +				llinfos << "Error " << result << " while downloading disk image."<< llendl; +				throw 0; +			} +			 +			fclose(downloadFile); +			downloadFile = NULL; +		} + +	begin_install: +		sendProgress(0, 0, CFSTR("Mounting image...")); +		LLFile::mkdir("mnt", 0700); +		 +		// NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder, +		//		but if our cleanup fails, this makes it much harder for the user to unmount the image. +		std::string mountOutput; +		boost::format cmdFormat("hdiutil attach %s -mountpoint mnt"); +		cmdFormat % dmgName; +		FILE* mounter = popen(cmdFormat.str().c_str(), "r");		/* Flawfinder: ignore */ +		 +		if(mounter == NULL) +		{ +			llinfos << "Failed to mount disk image, exiting."<< llendl; +			throw 0; +		} +		 +		// We need to scan the output from hdiutil to find the device node it uses to attach the disk image. +		// If we don't have this information, we can't detach it later. +		while(mounter != NULL) +		{ +			size_t len = fread(temp, 1, sizeof(temp)-1, mounter); +			temp[len] = 0; +			mountOutput.append(temp); +			if(len < sizeof(temp)-1) +			{ +				// End of file or error. +				int result = pclose(mounter); +				if(result != 0) +				{ +					// NOTE: We used to abort here, but pclose() started returning  +					// -1, possibly when the size of the DMG passed a certain point  +					llinfos << "Unexpected result closing pipe: " << result << llendl;  +				} +				mounter = NULL; +			} +		} +		 +		if(!mountOutput.empty()) +		{ +			const char *s = mountOutput.c_str(); +			const char *prefix = "/dev/"; +			char *sub = strstr(s, prefix); +			 +			if(sub != NULL) +			{ +				sub += strlen(prefix);	/* Flawfinder: ignore */ +				sscanf(sub, "%1023s", deviceNode);	/* Flawfinder: ignore */ +			} +		} +		 +		if(deviceNode[0] != 0) +		{ +			llinfos << "Disk image attached on /dev/" << deviceNode << llendl; +		} +		else +		{ +			llinfos << "Disk image device node not found!" << llendl; +			throw 0;  +		} +		 +		// Get an FSRef to the new application on the disk image +		FSRef sourceRef; +		FSRef mountRef; +		snprintf(temp, sizeof(temp), "%s/mnt", tempDir);		 + +		llinfos << "Disk image mount point is: " << temp << llendl; +		err = FSPathMakeRef((UInt8 *)temp, &mountRef, NULL); +		if(err != noErr) +		{ +			llinfos << "Couldn't make FSRef to disk image mount point." << llendl; +			throw 0; +		} + +		sendProgress(0, 0, CFSTR("Searching for the app bundle...")); +		err = findAppBundleOnDiskImage(&mountRef, &sourceRef); +		if(err != noErr) +		{ +			llinfos << "Couldn't find application bundle on mounted disk image." << llendl; +			throw 0; +		} +		else +		{ +			llinfos << "found the bundle." << llendl; +		} + +		sendProgress(0, 0, CFSTR("Preparing to copy files...")); +		 +		FSRef asideRef; +		char aside[MAX_PATH];		/* Flawfinder: ignore */ +		 +		// this will hold the name of the destination target +		CFStringRef appNameRef; + +		if(replacingTarget) +		{ +			// Get the name of the target we're replacing +			HFSUniStr255 appNameUniStr; +			err = FSGetCatalogInfo(&targetRef, 0, NULL, &appNameUniStr, NULL, NULL); +			if(err != noErr) +				throw 0; +			appNameRef = FSCreateStringFromHFSUniStr(NULL, &appNameUniStr); +			 +			// Move aside old version (into work directory) +			err = FSMoveObject(&targetRef, &tempDirRef, &asideRef); +			if(err != noErr) +			{ +				llwarns << "failed to move aside old version (error code " <<  +					err << ")" << llendl; +				throw 0; +			} + +			// Grab the path for later use. +			err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside)); +		} +		else +		{ +			// Construct the name of the target based on the product name +			char appName[MAX_PATH];		/* Flawfinder: ignore */ +			snprintf(appName, sizeof(appName), "%s.app", gProductName);		 +			appNameRef = CFStringCreateWithCString(NULL, appName, kCFStringEncodingUTF8); +		} +		 +		sendProgress(0, 0, CFSTR("Copying files...")); +		 +		llinfos << "Starting copy..." << llendl; + +		// Copy the new version from the disk image to the target location. +		err = FSCopyObjectSync( +				&sourceRef, +				&targetParentRef, +				appNameRef, +				&targetRef, +				kFSFileOperationDefaultOptions); +		 +		// Grab the path for later use. +		err = FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target)); +		if(err != noErr) +			throw 0; + +		llinfos << "Copy complete. Target = " << target << llendl; + +		if(err != noErr) +		{ +			// Something went wrong during the copy.  Attempt to put the old version back and bail. +			(void)FSDeleteObject(&targetRef); +			if(replacingTarget) +			{ +				(void)FSMoveObject(&asideRef, &targetParentRef, NULL); +			} +			throw 0; +		} +		else +		{ +			// The update has succeeded.  Clear the cache directory. + +			sendProgress(0, 0, CFSTR("Clearing cache...")); +	 +			llinfos << "Clearing cache..." << llendl; +			 +			gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), "*.*"); +			 +			llinfos << "Clear complete." << llendl; + +		}  	}  	catch(...)  	{  		if(!gCancelled) -            gFailure = true; +			if(gFailure == noErr) +				gFailure = -1;  	}  	// Failures from here on out are all non-fatal and not reported. -	sendProgress(0, 3, std::string("Cleaning up...")); +	sendProgress(0, 3, CFSTR("Cleaning up...")); -	setProgress(1, 3); +	// Close disk image file if necessary +	if(downloadFile != NULL) +	{ +		llinfos << "Closing download file." << llendl; + +		fclose(downloadFile); +		downloadFile = NULL; +	} + +	sendProgress(1, 3);  	// Unmount image  	if(deviceNode[0] != 0)  	{ @@ -599,61 +1223,35 @@ void* LLMacUpdater::updatethreadproc(void*)  		system(temp);		/* Flawfinder: ignore */  	} -	setProgress(2, 3); -    std::string *trash_str=getUserTrashFolder(); +	sendProgress(2, 3);  	// Move work directory to the trash  	if(tempDir[0] != 0)  	{  		llinfos << "Moving work directory to the trash." << llendl; -                 -        try  -        { -            boost::filesystem::path trash_dir(*trash_str); -            boost::filesystem::rename(mount_dir, trash_dir); -        } -        catch(boost::filesystem::filesystem_error e)  -        {  -            llwarns << "Failed to move " << mount_dir.string() << " to " << *trash_str << llendl; -            return (NULL); -        } + +		FSRef trashRef; +		OSStatus err = FSMoveObjectToTrashSync(&tempDirRef, &trashRef, 0);  +		if(err != noErr) { +			llwarns << "failed to move files to trash, (error code " << +				err << ")" << llendl; +		}  	} -    std::string app_name_str = app_dir.string(); - -	if(!gCancelled  && !gFailure && !app_name_str.empty()) +	if(!gCancelled  && !gFailure && (target[0] != 0))  	{ -        //SPATTERS todo is there no better way to do this than system calls?  		llinfos << "Touching application bundle." << llendl; -         -        std::stringstream touch_str; -        touch_str << "touch '" << app_name_str << "'"; -         -		system(touch_str.str().c_str());		/* Flawfinder: ignore */ +		snprintf(temp, sizeof(temp), "touch '%s'", target);		 +		system(temp);		/* Flawfinder: ignore */  		llinfos << "Launching updated application." << llendl; -         -        std::stringstream open_str; -         -        open_str << "open '" << app_name_str << "'"; -		system(open_str.str().c_str());		/* Flawfinder: ignore */ +		snprintf(temp, sizeof(temp), "open '%s'", target);		 +		system(temp);		/* Flawfinder: ignore */  	}  	sendDone(); -	return (NULL); +	return(NULL);  } - -//static -void* LLMacUpdater::sUpdatethreadproc(void* vptr) -{ -    if (!sInstance) -    { -        llerrs << "LLMacUpdater not instantiated before use.  Aborting." << llendl; -        return (NULL); -    } -    return sInstance->updatethreadproc(vptr); -} - | 
