mac_updater.cpp

Go to the documentation of this file.
00001 
00032 #include "linden_common.h"
00033 
00034 #include <sys/types.h>
00035 #include <sys/stat.h>
00036 #include <unistd.h>
00037 
00038 #include <curl/curl.h>
00039 #include <pthread.h>
00040 
00041 #include "llerror.h"
00042 #include "lltimer.h"
00043 #include "lldir.h"
00044 #include "llfile.h"
00045 
00046 #include "llstring.h"
00047 
00048 #include <Carbon/Carbon.h>
00049 
00050 #include "MoreFilesX.h"
00051 #include "FSCopyObject.h"
00052 
00053 enum
00054 {
00055         kEventClassCustom = 'Cust',
00056         kEventCustomProgress = 'Prog',
00057         kEventParamCustomCurValue = 'Cur ',
00058         kEventParamCustomMaxValue = 'Max ',
00059         kEventParamCustomText = 'Text',
00060         kEventCustomDone = 'Done',
00061 };
00062 
00063 WindowRef gWindow = NULL;
00064 EventHandlerRef gEventHandler = NULL;
00065 OSStatus gFailure = noErr;
00066 Boolean gCancelled = false;
00067 
00068 char *gUpdateURL;
00069 char *gProductName;
00070 
00071 void *updatethreadproc(void*);
00072 
00073 pthread_t updatethread;
00074 
00075 OSStatus setProgress(int cur, int max)
00076 {
00077         OSStatus err;
00078         ControlRef progressBar = NULL;
00079         ControlID id;
00080 
00081         id.signature = 'prog';
00082         id.id = 0;
00083 
00084         err = GetControlByID(gWindow, &id, &progressBar);
00085         if(err == noErr)
00086         {
00087                 Boolean indeterminate;
00088                 
00089                 if(max == 0)
00090                 {
00091                         indeterminate = true;
00092                         err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate);
00093                 }
00094                 else
00095                 {
00096                         double percentage = (double)cur / (double)max;
00097                         SetControlMinimum(progressBar, 0);
00098                         SetControlMaximum(progressBar, 100);
00099                         SetControlValue(progressBar, (SInt16)(percentage * 100));
00100 
00101                         indeterminate = false;
00102                         err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate);
00103 
00104                         Draw1Control(progressBar);
00105                 }
00106         }
00107 
00108         return(err);
00109 }
00110 
00111 OSStatus setProgressText(CFStringRef text)
00112 {
00113         OSStatus err;
00114         ControlRef progressText = NULL;
00115         ControlID id;
00116 
00117         id.signature = 'what';
00118         id.id = 0;
00119 
00120         err = GetControlByID(gWindow, &id, &progressText);
00121         if(err == noErr)
00122         {
00123                 err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&text);
00124                 Draw1Control(progressText);
00125         }
00126 
00127         return(err);
00128 }
00129 
00130 OSStatus sendProgress(long cur, long max, CFStringRef text = NULL)
00131 {
00132         OSStatus result;
00133         EventRef evt;
00134         
00135         result = CreateEvent( 
00136                         NULL,
00137                         kEventClassCustom, 
00138                         kEventCustomProgress,
00139                         0, 
00140                         kEventAttributeNone, 
00141                         &evt);
00142         
00143         // This event needs to be targeted at the window so it goes to the window's handler.
00144         if(result == noErr)
00145         {
00146                 EventTargetRef target = GetWindowEventTarget(gWindow);
00147                 result = SetEventParameter (
00148                         evt,
00149                         kEventParamPostTarget,
00150                         typeEventTargetRef,
00151                         sizeof(target),
00152                         &target);
00153         }
00154 
00155         if(result == noErr)
00156         {
00157                 result = SetEventParameter (
00158                         evt,
00159                         kEventParamCustomCurValue,
00160                         typeLongInteger,
00161                         sizeof(cur),
00162                         &cur);
00163         }
00164 
00165         if(result == noErr)
00166         {
00167                 result = SetEventParameter (
00168                         evt,
00169                         kEventParamCustomMaxValue,
00170                         typeLongInteger,
00171                         sizeof(max),
00172                         &max);
00173         }
00174         
00175         if(result == noErr)
00176         {
00177                 if(text != NULL)
00178                 {
00179                         result = SetEventParameter (
00180                                 evt,
00181                                 kEventParamCustomText,
00182                                 typeCFStringRef,
00183                                 sizeof(text),
00184                                 &text);
00185                 }
00186         }
00187         
00188         if(result == noErr)
00189         {
00190                 // Send the event
00191                 PostEventToQueue(
00192                         GetMainEventQueue(),
00193                         evt,
00194                         kEventPriorityStandard);
00195 
00196         }
00197         
00198         return(result);
00199 }
00200 
00201 OSStatus sendDone(void)
00202 {
00203         OSStatus result;
00204         EventRef evt;
00205         
00206         result = CreateEvent( 
00207                         NULL,
00208                         kEventClassCustom, 
00209                         kEventCustomDone,
00210                         0, 
00211                         kEventAttributeNone, 
00212                         &evt);
00213         
00214         // This event needs to be targeted at the window so it goes to the window's handler.
00215         if(result == noErr)
00216         {
00217                 EventTargetRef target = GetWindowEventTarget(gWindow);
00218                 result = SetEventParameter (
00219                         evt,
00220                         kEventParamPostTarget,
00221                         typeEventTargetRef,
00222                         sizeof(target),
00223                         &target);
00224         }
00225 
00226         if(result == noErr)
00227         {
00228                 // Send the event
00229                 PostEventToQueue(
00230                         GetMainEventQueue(),
00231                         evt,
00232                         kEventPriorityStandard);
00233 
00234         }
00235         
00236         return(result);
00237 }
00238 
00239 OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
00240 {
00241         OSStatus result = eventNotHandledErr;
00242         OSStatus err;
00243         UInt32 evtClass = GetEventClass(event);
00244         UInt32 evtKind = GetEventKind(event);
00245         
00246         if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
00247         {
00248                 HICommand cmd;
00249                 err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd);
00250                 
00251                 if(err == noErr)
00252                 {
00253                         switch(cmd.commandID)
00254                         {                               
00255                                 case kHICommandCancel:
00256                                         gCancelled = true;
00257 //                                      QuitAppModalLoopForWindow(gWindow);
00258                                         result = noErr;
00259                                 break;
00260                         }
00261                 }
00262         }
00263         else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomProgress))
00264         {
00265                 // Request to update the progress dialog
00266                 long cur = 0;
00267                 long max = 0;
00268                 CFStringRef text = NULL;
00269                 (void) GetEventParameter(event, kEventParamCustomCurValue, typeLongInteger, NULL, sizeof(cur), NULL, &cur);
00270                 (void) GetEventParameter(event, kEventParamCustomMaxValue, typeLongInteger, NULL, sizeof(max), NULL, &max);
00271                 (void) GetEventParameter(event, kEventParamCustomText, typeCFStringRef, NULL, sizeof(text), NULL, &text);
00272                 
00273                 err = setProgress(cur, max);
00274                 if(err == noErr)
00275                 {
00276                         if(text != NULL)
00277                         {
00278                                 setProgressText(text);
00279                         }
00280                 }
00281                 
00282                 result = noErr;
00283         }
00284         else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomDone))
00285         {
00286                 // We're done.  Exit the modal loop.
00287                 QuitAppModalLoopForWindow(gWindow);
00288                 result = noErr;
00289         }
00290         
00291         return(result);
00292 }
00293 
00294 #if 0
00295 size_t curl_download_callback(void *data, size_t size, size_t nmemb,
00296                                                                                   void *user_data)
00297 {
00298         S32 bytes = size * nmemb;
00299         char *cdata = (char *) data;
00300         for (int i =0; i < bytes; i += 1)
00301         {
00302                 gServerResponse.append(cdata[i]);
00303         }
00304         return bytes;
00305 }
00306 #endif
00307 
00308 int curl_progress_callback_func(void *clientp,
00309                                                           double dltotal,
00310                                                           double dlnow,
00311                                                           double ultotal,
00312                                                           double ulnow)
00313 {
00314         int max = (int)(dltotal / 1024.0);
00315         int cur = (int)(dlnow / 1024.0);
00316         sendProgress(cur, max);
00317         
00318         if(gCancelled)
00319                 return(1);
00320 
00321         return(0);
00322 }
00323 
00324 int parse_args(int argc, char **argv)
00325 {
00326         int j;
00327 
00328         for (j = 1; j < argc; j++) 
00329         {
00330                 if ((!strcmp(argv[j], "-url")) && (++j < argc)) 
00331                 {
00332                         gUpdateURL = argv[j];
00333                 }
00334                 else if ((!strcmp(argv[j], "-name")) && (++j < argc)) 
00335                 {
00336                         gProductName = argv[j];
00337                 }
00338         }
00339 
00340         return 0;
00341 }
00342 
00343 int main(int argc, char **argv)
00344 {
00345         // We assume that all the logs we're looking for reside on the current drive
00346         gDirUtilp->initAppDirs("SecondLife");
00347 
00349         //
00350         // Process command line arguments
00351         //
00352         gUpdateURL  = NULL;
00353         gProductName = NULL;
00354         parse_args(argc, argv);
00355         if (!gUpdateURL)
00356         {
00357                 llinfos << "Usage: mac_updater -url <url> [-name <product_name>] [-program <program_name>]" << llendl;
00358                 exit(1);
00359         }
00360         else
00361         {
00362                 llinfos << "Update url is: " << gUpdateURL << llendl;
00363                 if (gProductName)
00364                 {
00365                         llinfos << "Product name is: " << gProductName << llendl;
00366                 }
00367                 else
00368                 {
00369                         gProductName = "Second Life";
00370                 }
00371         }
00372         
00373         llinfos << "Starting " << gProductName << " Updater" << llendl;
00374 
00375         // Real UI...
00376         OSStatus err;
00377         IBNibRef nib = NULL;
00378         
00379         err = CreateNibReference(CFSTR("AutoUpdater"), &nib);
00380 
00381         char windowTitle[MAX_PATH];             /* Flawfinder: ignore */
00382         snprintf(windowTitle, sizeof(windowTitle), "%s Updater", gProductName);         
00383         CFStringRef windowTitleRef = NULL;
00384         windowTitleRef = CFStringCreateWithCString(NULL, windowTitle, kCFStringEncodingUTF8);
00385         
00386         if(err == noErr)
00387         {
00388                 err = CreateWindowFromNib(nib, CFSTR("Updater"), &gWindow);
00389         }
00390 
00391         if (err == noErr)
00392         {
00393                 err = SetWindowTitleWithCFString(gWindow, windowTitleRef);      
00394         }
00395         CFRelease(windowTitleRef);
00396 
00397         if(err == noErr)
00398         {
00399                 // Set up an event handler for the window.
00400                 EventTypeSpec handlerEvents[] = 
00401                 {
00402                         { kEventClassCommand, kEventCommandProcess },
00403                         { kEventClassCustom, kEventCustomProgress },
00404                         { kEventClassCustom, kEventCustomDone }
00405                 };
00406                 InstallStandardEventHandler(GetWindowEventTarget(gWindow));
00407                 InstallWindowEventHandler(
00408                                 gWindow, 
00409                                 NewEventHandlerUPP(dialogHandler), 
00410                                 GetEventTypeCount (handlerEvents), 
00411                                 handlerEvents, 
00412                                 0, 
00413                                 &gEventHandler);
00414         }
00415         
00416         if(err == noErr)
00417         {
00418                 ShowWindow(gWindow);
00419         }
00420                 
00421         if(err == noErr)
00422         {
00423                 pthread_create(&updatethread, 
00424                          NULL,
00425                          &updatethreadproc, 
00426                          NULL);
00427                                                  
00428         }
00429         
00430         if(err == noErr)
00431         {
00432                 RunAppModalLoopForWindow(gWindow);
00433         }
00434 
00435         void *threadresult;
00436 
00437         pthread_join(updatethread, &threadresult);
00438 
00439         if(!gCancelled && (gFailure != noErr))
00440         {
00441                 // Something went wrong.  Since we always just tell the user to download a new version, we don't really care what.
00442                 AlertStdCFStringAlertParamRec params;
00443                 SInt16 retval_mac = 1;
00444                 DialogRef alert = NULL;
00445                 OSStatus err;
00446 
00447                 params.version = kStdCFStringAlertVersionOne;
00448                 params.movable = false;
00449                 params.helpButton = false;
00450                 params.defaultText = (CFStringRef)kAlertDefaultOKText;
00451                 params.cancelText = 0;
00452                 params.otherText = 0;
00453                 params.defaultButton = 1;
00454                 params.cancelButton = 0;
00455                 params.position = kWindowDefaultPosition;
00456                 params.flags = 0;
00457 
00458                 err = CreateStandardAlert(
00459                                 kAlertStopAlert,
00460                                 CFSTR("Error"),
00461                                 CFSTR("An error occurred while updating Second Life.  Please download the latest version from www.secondlife.com."),
00462                                 &params,
00463                                 &alert);
00464                 
00465                 if(err == noErr)
00466                 {
00467                         err = RunStandardAlert(
00468                                         alert,
00469                                         NULL,
00470                                         &retval_mac);
00471                 }
00472 
00473         }
00474         
00475         // Don't dispose of things, just exit.  This keeps the update thread from potentially getting hosed.
00476         exit(0);
00477 
00478         if(gWindow != NULL)
00479         {
00480                 DisposeWindow(gWindow);
00481         }
00482         
00483         if(nib != NULL)
00484         {
00485                 DisposeNibReference(nib);
00486         }
00487         
00488         return 0;
00489 }
00490 
00491 bool isDirWritable(FSRef &dir)
00492 {
00493         bool result = false;
00494         
00495         // Test for a writable directory by creating a directory, then deleting it again.
00496         // This is kinda lame, but will pretty much always give the right answer.
00497         
00498         OSStatus err = noErr;
00499         char temp[PATH_MAX];            /* Flawfinder: ignore */
00500 
00501         err = FSRefMakePath(&dir, (UInt8*)temp, sizeof(temp));
00502 
00503         if(err == noErr)
00504         {
00505                 temp[0] = '\0';
00506                 strncat(temp, "/.test_XXXXXX", sizeof(temp) - 1);
00507                 
00508                 if(mkdtemp(temp) != NULL)
00509                 {
00510                         // We were able to make the directory.  This means the directory is writable.
00511                         result = true;
00512                         
00513                         // Clean up.
00514                         rmdir(temp);
00515                 }
00516         }
00517 
00518 #if 0
00519         // This seemed like a good idea, but won't tell us if we're on a volume mounted read-only.
00520         UInt8 perm;
00521         err = FSGetUserPrivilegesPermissions(&targetParentRef, &perm, NULL);
00522         if(err == noErr)
00523         {
00524                 if(perm & kioACUserNoMakeChangesMask)
00525                 {
00526                         // Parent directory isn't writable.
00527                         llinfos << "Target parent directory not writable." << llendl;
00528                         err = -1;
00529                         replacingTarget = false;
00530                 }
00531         }
00532 #endif
00533 
00534         return result;
00535 }
00536 
00537 static void utf8str_to_HFSUniStr255(HFSUniStr255 *dest, const char* src)
00538 {
00539         llutf16string   utf16str = utf8str_to_utf16str(src);
00540 
00541         dest->length = utf16str.size();
00542         if(dest->length > 255)
00543         {
00544                 // There's onl room for 255 chars in a HFSUniStr25..
00545                 // Truncate to avoid stack smaching or other badness.
00546                 dest->length = 255;
00547         }
00548         memcpy(dest->unicode, utf16str.data(), sizeof(UniChar)* dest->length);          /* Flawfinder: ignore */
00549 }
00550 
00551 static std::string HFSUniStr255_to_utf8str(const HFSUniStr255* src)
00552 {
00553         llutf16string string16((U16*)&(src->unicode), src->length);
00554         std::string result = utf16str_to_utf8str(string16);
00555         return result;
00556 }
00557 
00558 int restoreObject(const char* aside, const char* target, const char* path, const char* object)
00559 {
00560         char source[PATH_MAX];          /* Flawfinder: ignore */
00561         char dest[PATH_MAX];            /* Flawfinder: ignore */
00562         snprintf(source, sizeof(source), "%s/%s/%s", aside, path, object);              
00563         snprintf(dest, sizeof(dest), "%s/%s", target, path);            
00564         FSRef sourceRef;
00565         FSRef destRef;
00566         OSStatus err;
00567         err = FSPathMakeRef((UInt8 *)source, &sourceRef, NULL);
00568         if(err != noErr) return false;
00569         err = FSPathMakeRef((UInt8 *)dest, &destRef, NULL);
00570         if(err != noErr) return false;
00571 
00572         llinfos << "Copying " << source << " to " << dest << llendl;
00573 
00574         err = FSCopyObject(     
00575                         &sourceRef,
00576                         &destRef,
00577                         0,
00578                         kFSCatInfoNone,
00579                         kDupeActionReplace,
00580                         NULL,
00581                         false,
00582                         false,
00583                         NULL,
00584                         NULL,
00585                         NULL,
00586                         NULL);
00587 
00588         if(err != noErr) return false;
00589         return true;
00590 }
00591 
00592 // Replace any mention of "Second Life" with the product name.
00593 void filterFile(const char* filename)
00594 {
00595         char temp[PATH_MAX];            /* Flawfinder: ignore */
00596         // First copy the target's version, so we can run it through sed.
00597         snprintf(temp, sizeof(temp), "cp '%s' '%s.tmp'", filename, filename);           
00598         system(temp);           /* Flawfinder: ignore */
00599 
00600         // Now run it through sed.
00601         snprintf(temp, sizeof(temp),            
00602                         "sed 's/Second Life/%s/g' '%s.tmp' > '%s'", gProductName, filename, filename);
00603         system(temp);           /* Flawfinder: ignore */
00604 }
00605 
00606 static bool isFSRefViewerBundle(FSRef *targetRef)
00607 {
00608         bool result = false;
00609         CFURLRef targetURL = NULL;
00610         CFBundleRef targetBundle = NULL;
00611         CFStringRef targetBundleID = NULL;
00612         
00613         targetURL = CFURLCreateFromFSRef(NULL, targetRef);
00614 
00615         if(targetURL == NULL)
00616         {
00617                 llinfos << "Error creating target URL." << llendl;
00618         }
00619         else
00620         {
00621                 targetBundle = CFBundleCreate(NULL, targetURL);
00622         }
00623         
00624         if(targetBundle == NULL)
00625         {
00626                 llinfos << "Failed to create target bundle." << llendl;
00627         }
00628         else
00629         {
00630                 targetBundleID = CFBundleGetIdentifier(targetBundle);
00631         }
00632         
00633         if(targetBundleID == NULL)
00634         {
00635                 llinfos << "Couldn't retrieve target bundle ID." << llendl;
00636         }
00637         else
00638         {
00639                 if(CFStringCompare(targetBundleID, CFSTR("com.secondlife.indra.viewer"), 0) == kCFCompareEqualTo)
00640                 {
00641                         // This is the bundle we're looking for.
00642                         result = true;
00643                 }
00644                 else
00645                 {
00646                         llinfos << "Target bundle ID mismatch." << llendl;
00647                 }
00648         }
00649         
00650         // Don't release targetBundleID -- since we don't retain it, it's released when targetBundle is released.
00651         if(targetURL != NULL)
00652                 CFRelease(targetURL);
00653         if(targetBundle != NULL)
00654                 CFRelease(targetBundle);
00655         
00656         return result;
00657 }
00658 
00659 // Search through the directory specified by 'parent' for an item that appears to be a Second Life viewer.
00660 static OSErr findAppBundleOnDiskImage(FSRef *parent, FSRef *app)
00661 {
00662         FSIterator              iterator;
00663         bool                    found = false;
00664 
00665         OSErr err = FSOpenIterator( parent, kFSIterateFlat, &iterator );
00666         if(!err)
00667         {
00668                 do
00669                 {
00670                         ItemCount actualObjects = 0;
00671                         Boolean containerChanged = false;
00672                         FSCatalogInfo info;
00673                         FSRef ref;
00674                         HFSUniStr255 unicodeName;
00675                         err = FSGetCatalogInfoBulk( 
00676                                         iterator, 
00677                                         1, 
00678                                         &actualObjects, 
00679                                         &containerChanged,
00680                                         kFSCatInfoNodeFlags, 
00681                                         &info, 
00682                                         &ref,
00683                                         NULL, 
00684                                         &unicodeName );
00685                         
00686                         if(actualObjects == 0)
00687                                 break;
00688                                 
00689                         if(!err)
00690                         {
00691                                 // Call succeeded and not done with the iteration.
00692                                 std::string name = HFSUniStr255_to_utf8str(&unicodeName);
00693 
00694                                 llinfos << "Considering \"" << name << "\"" << llendl;
00695 
00696                                 if(info.nodeFlags & kFSNodeIsDirectoryMask)
00697                                 {
00698                                         // This is a directory.  See if it's a .app
00699                                         if(name.find(".app") != std::string::npos)
00700                                         {
00701                                                 // Looks promising.  Check to see if it has the right bundle identifier.
00702                                                 if(isFSRefViewerBundle(&ref))
00703                                                 {
00704                                                         // This is the one.  Return it.
00705                                                         *app = ref;
00706                                                         found = true;
00707                                                 }
00708                                         }
00709                                 }
00710                         }
00711                 }
00712                 while(!err && !found);
00713                 
00714                 FSCloseIterator(iterator);
00715         }
00716         
00717         if(!err && !found)
00718                 err = fnfErr;
00719                 
00720         return err;
00721 }
00722 
00723 void *updatethreadproc(void*)
00724 {
00725         char tempDir[PATH_MAX] = "";            /* Flawfinder: ignore */
00726         FSRef tempDirRef;
00727         char temp[PATH_MAX];    /* Flawfinder: ignore */
00728         // *NOTE: This buffer length is used in a scanf() below.
00729         char deviceNode[1024] = "";     /* Flawfinder: ignore */
00730         FILE *downloadFile = NULL;
00731         OSStatus err;
00732         ProcessSerialNumber psn;
00733         char target[PATH_MAX];          /* Flawfinder: ignore */
00734         FSRef targetRef;
00735         FSRef targetParentRef;
00736         FSVolumeRefNum targetVol;
00737         FSRef trashFolderRef, tempFolderRef;
00738         Boolean replacingTarget = false;
00739 
00740         memset(&tempDirRef, 0, sizeof(tempDirRef));
00741         memset(&targetRef, 0, sizeof(targetRef));
00742         memset(&targetParentRef, 0, sizeof(targetParentRef));
00743         
00744         try
00745         {
00746                 // Attempt to get a reference to the Second Life application bundle containing this updater.
00747                 // Any failures during this process will cause us to default to updating /Applications/Second Life.app
00748                 {
00749                         FSRef myBundle;
00750 
00751                         err = GetCurrentProcess(&psn);
00752                         if(err == noErr)
00753                         {
00754                                 err = GetProcessBundleLocation(&psn, &myBundle);
00755                         }
00756 
00757                         if(err == noErr)
00758                         {
00759                                 // Sanity check:  Make sure the name of the item referenced by targetRef is "Second Life.app".
00760                                 FSRefMakePath(&myBundle, (UInt8*)target, sizeof(target));
00761                                 
00762                                 llinfos << "Updater bundle location: " << target << llendl;
00763                         }
00764                         
00765                         // Our bundle should be in Second Life.app/Contents/Resources/AutoUpdater.app
00766                         // so we need to go up 3 levels to get the path to the main application bundle.
00767                         if(err == noErr)
00768                         {
00769                                 err = FSGetParentRef(&myBundle, &targetRef);
00770                         }
00771                         if(err == noErr)
00772                         {
00773                                 err = FSGetParentRef(&targetRef, &targetRef);
00774                         }
00775                         if(err == noErr)
00776                         {
00777                                 err = FSGetParentRef(&targetRef, &targetRef);
00778                         }
00779                         
00780                         // And once more to get the parent of the target
00781                         if(err == noErr)
00782                         {
00783                                 err = FSGetParentRef(&targetRef, &targetParentRef);
00784                         }
00785                         
00786                         if(err == noErr)
00787                         {
00788                                 FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target));
00789                                 llinfos << "Path to target: " << target << llendl;
00790                         }
00791                         
00792                         // Sanity check: make sure the target is a bundle with the right identifier
00793                         if(err == noErr)
00794                         {
00795                                 // Assume the worst...
00796                                 err = -1;
00797 
00798                                 if(isFSRefViewerBundle(&targetRef))
00799                                 {
00800                                         // This is the bundle we're looking for.
00801                                         err = noErr;
00802                                         replacingTarget = true;
00803                                 }
00804                         }
00805                         
00806                         // Make sure the target's parent directory is writable.
00807                         if(err == noErr)
00808                         {
00809                                 if(!isDirWritable(targetParentRef))
00810                                 {
00811                                         // Parent directory isn't writable.
00812                                         llinfos << "Target parent directory not writable." << llendl;
00813                                         err = -1;
00814                                         replacingTarget = false;
00815                                 }
00816                         }
00817 
00818                         if(err != noErr)
00819                         {
00820                                 Boolean isDirectory;
00821                                 llinfos << "Target search failed, defaulting to /Applications/" << gProductName << ".app." << llendl;
00822                                 
00823                                 // Set up the parent directory
00824                                 err = FSPathMakeRef((UInt8*)"/Applications", &targetParentRef, &isDirectory);
00825                                 if((err != noErr) || (!isDirectory))
00826                                 {
00827                                         // We're so hosed.
00828                                         llinfos << "Applications directory not found, giving up." << llendl;
00829                                         throw 0;
00830                                 }
00831                                 
00832                                 snprintf(target, sizeof(target), "/Applications/%s.app", gProductName);         
00833 
00834                                 memset(&targetRef, 0, sizeof(targetRef));
00835                                 err = FSPathMakeRef((UInt8*)target, &targetRef, NULL);
00836                                 if(err == fnfErr)
00837                                 {
00838                                         // This is fine, just means we're not replacing anything.
00839                                         err = noErr;
00840                                         replacingTarget = false;
00841                                 }
00842                                 else
00843                                 {
00844                                         replacingTarget = true;
00845                                 }
00846 
00847                                 // Make sure the target's parent directory is writable.
00848                                 if(err == noErr)
00849                                 {
00850                                         if(!isDirWritable(targetParentRef))
00851                                         {
00852                                                 // Parent directory isn't writable.
00853                                                 llinfos << "Target parent directory not writable." << llendl;
00854                                                 err = -1;
00855                                                 replacingTarget = false;
00856                                         }
00857                                 }
00858 
00859                         }
00860                         
00861                         // If we haven't fixed all problems by this point, just bail.
00862                         if(err != noErr)
00863                         {
00864                                 llinfos << "Unable to pick a target, giving up." << llendl;
00865                                 throw 0;
00866                         }
00867                 }
00868                 
00869                 // Find the volID of the volume the target resides on
00870                 {
00871                         FSCatalogInfo info;
00872                         err = FSGetCatalogInfo(
00873                                 &targetParentRef,
00874                                 kFSCatInfoVolume,
00875                                 &info,
00876                                 NULL, 
00877                                 NULL,  
00878                                 NULL);
00879                                 
00880                         if(err != noErr)
00881                                 throw 0;
00882                         
00883                         targetVol = info.volume;
00884                 }
00885 
00886                 // Find the temporary items and trash folders on that volume.
00887                 err = FSFindFolder(
00888                         targetVol,
00889                         kTrashFolderType,
00890                         true,
00891                         &trashFolderRef);
00892 
00893                 if(err != noErr)
00894                         throw 0;
00895 
00896                 err = FSFindFolder(
00897                         targetVol,
00898                         kTemporaryFolderType,
00899                         true,
00900                         &tempFolderRef);
00901                 
00902                 if(err != noErr)
00903                         throw 0;
00904                 
00905                 err = FSRefMakePath(&tempFolderRef, (UInt8*)temp, sizeof(temp));
00906 
00907                 if(err != noErr)
00908                         throw 0;
00909                 
00910                 temp[0] = '\0';
00911                 strncat(temp, "/SecondLifeUpdate_XXXXXX", sizeof(temp) - 1);
00912                 if(mkdtemp(temp) == NULL)
00913                 {
00914                         throw 0;
00915                 }
00916                 
00917                 strcpy(tempDir, temp);          /* Flawfinder: ignore */
00918                 
00919                 llinfos << "tempDir is " << tempDir << llendl;
00920 
00921                 err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL);
00922 
00923                 if(err != noErr)
00924                         throw 0;
00925                                 
00926                 chdir(tempDir);
00927                 
00928                 snprintf(temp, sizeof(temp), "SecondLife.dmg");         
00929                 
00930                 downloadFile = fopen(temp, "wb");               /* Flawfinder: ignore */
00931                 if(downloadFile == NULL)
00932                 {
00933                         throw 0;
00934                 }
00935 
00936                 {
00937                         CURL *curl = curl_easy_init();
00938 
00939                         curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
00940         //              curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
00941                         curl_easy_setopt(curl, CURLOPT_FILE, downloadFile);
00942                         curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
00943                         curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &curl_progress_callback_func);
00944                         curl_easy_setopt(curl, CURLOPT_URL,     gUpdateURL);
00945                         curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
00946                         
00947                         sendProgress(0, 1, CFSTR("Downloading..."));
00948                         
00949                         CURLcode result = curl_easy_perform(curl);
00950                         
00951                         curl_easy_cleanup(curl);
00952                         
00953                         if(gCancelled)
00954                         {
00955                                 llinfos << "User cancel, bailing out."<< llendl;
00956                                 throw 0;
00957                         }
00958                         
00959                         if(result != CURLE_OK)
00960                         {
00961                                 llinfos << "Error " << result << " while downloading disk image."<< llendl;
00962                                 throw 0;
00963                         }
00964                         
00965                         fclose(downloadFile);
00966                         downloadFile = NULL;
00967                 }
00968                 
00969                 sendProgress(0, 0, CFSTR("Mounting image..."));
00970                 LLFile::mkdir("mnt", 0700);
00971                 
00972                 // NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder,
00973                 //              but if our cleanup fails, this makes it much harder for the user to unmount the image.
00974                 LLString mountOutput;
00975                 FILE* mounter = popen("hdiutil attach SecondLife.dmg -mountpoint mnt", "r");            /* Flawfinder: ignore */
00976                 
00977                 if(mounter == NULL)
00978                 {
00979                         llinfos << "Failed to mount disk image, exiting."<< llendl;
00980                         throw 0;
00981                 }
00982                 
00983                 // We need to scan the output from hdiutil to find the device node it uses to attach the disk image.
00984                 // If we don't have this information, we can't detach it later.
00985                 while(mounter != NULL)
00986                 {
00987                         size_t len = fread(temp, 1, sizeof(temp)-1, mounter);
00988                         temp[len] = 0;
00989                         mountOutput.append(temp);
00990                         if(len < sizeof(temp)-1)
00991                         {
00992                                 // End of file or error.
00993                                 int result = pclose(mounter);
00994                                 if(result != 0)
00995                                 {
00996                                         // NOTE: We used to abort here, but pclose() started returning 
00997                                         // -1, possibly when the size of the DMG passed a certain point 
00998                                         llinfos << "Unexpected result closing pipe: " << result << llendl; 
00999                                 }
01000                                 mounter = NULL;
01001                         }
01002                 }
01003                 
01004                 if(!mountOutput.empty())
01005                 {
01006                         const char *s = mountOutput.c_str();
01007                         char *prefix = "/dev/";
01008                         char *sub = strstr(s, prefix);
01009                         
01010                         if(sub != NULL)
01011                         {
01012                                 sub += strlen(prefix);  /* Flawfinder: ignore */
01013                                 sscanf(sub, "%1023s", deviceNode);      /* Flawfinder: ignore */
01014                         }
01015                 }
01016                 
01017                 if(deviceNode[0] != 0)
01018                 {
01019                         llinfos << "Disk image attached on /dev/" << deviceNode << llendl;
01020                 }
01021                 else
01022                 {
01023                         llinfos << "Disk image device node not found!" << llendl;
01024                         throw 0; 
01025                 }
01026                 
01027                 // Get an FSRef to the new application on the disk image
01028                 FSRef sourceRef;
01029                 FSRef mountRef;
01030                 snprintf(temp, sizeof(temp), "%s/mnt", tempDir);                
01031 
01032                 llinfos << "Disk image mount point is: " << temp << llendl;
01033 
01034                 err = FSPathMakeRef((UInt8 *)temp, &mountRef, NULL);
01035                 if(err != noErr)
01036                 {
01037                         llinfos << "Couldn't make FSRef to disk image mount point." << llendl;
01038                         throw 0;
01039                 }
01040 
01041                 err = findAppBundleOnDiskImage(&mountRef, &sourceRef);
01042                 if(err != noErr)
01043                 {
01044                         llinfos << "Couldn't find application bundle on mounted disk image." << llendl;
01045                         throw 0;
01046                 }
01047                 
01048                 FSRef asideRef;
01049                 char aside[MAX_PATH];           /* Flawfinder: ignore */
01050                 
01051                 // this will hold the name of the destination target
01052                 HFSUniStr255 appNameUniStr;
01053 
01054                 if(replacingTarget)
01055                 {
01056                         // Get the name of the target we're replacing
01057                         err = FSGetCatalogInfo(&targetRef, 0, NULL, &appNameUniStr, NULL, NULL);
01058                         if(err != noErr)
01059                                 throw 0;
01060                         
01061                         // Move aside old version (into work directory)
01062                         err = FSMoveObject(&targetRef, &tempDirRef, &asideRef);
01063                         if(err != noErr)
01064                                 throw 0;
01065 
01066                         // Grab the path for later use.
01067                         err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside));
01068                 }
01069                 else
01070                 {
01071                         // Construct the name of the target based on the product name
01072                         char appName[MAX_PATH];         /* Flawfinder: ignore */
01073                         snprintf(appName, sizeof(appName), "%s.app", gProductName);             
01074                         utf8str_to_HFSUniStr255( &appNameUniStr, appName );
01075                 }
01076                 
01077                 sendProgress(0, 0, CFSTR("Copying files..."));
01078                 
01079                 llinfos << "Starting copy..." << llendl;
01080 
01081                 // Copy the new version from the disk image to the target location.
01082                 err = FSCopyObject(     
01083                                 &sourceRef,
01084                                 &targetParentRef,
01085                                 0,
01086                                 kFSCatInfoNone,
01087                                 kDupeActionStandard,
01088                                 &appNameUniStr,
01089                                 false,
01090                                 false,
01091                                 NULL,
01092                                 NULL,
01093                                 &targetRef,
01094                                 NULL);
01095                 
01096                 // Grab the path for later use.
01097                 err = FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target));
01098                 if(err != noErr)
01099                         throw 0;
01100 
01101                 llinfos << "Copy complete. Target = " << target << llendl;
01102 
01103                 if(err != noErr)
01104                 {
01105                         // Something went wrong during the copy.  Attempt to put the old version back and bail.
01106                         (void)FSDeleteObjects(&targetRef);
01107                         if(replacingTarget)
01108                         {
01109                                 (void)FSMoveObject(&asideRef, &targetParentRef, NULL);
01110                         }
01111                         throw 0;
01112                 }
01113                 else
01114                 {
01115                         // The update has succeeded.  Clear the cache directory.
01116 
01117                         sendProgress(0, 0, CFSTR("Clearing cache..."));
01118         
01119                         llinfos << "Clearing cache..." << llendl;
01120                         
01121                         char mask[LL_MAX_PATH];         /* Flawfinder: ignore */
01122                         snprintf(mask, LL_MAX_PATH, "%s*.*", gDirUtilp->getDirDelimiter().c_str());             
01123                         gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask);
01124                         
01125                         llinfos << "Clear complete." << llendl;
01126 
01127                 }
01128         }
01129         catch(...)
01130         {
01131                 if(!gCancelled)
01132                         if(gFailure == noErr)
01133                                 gFailure = -1;
01134         }
01135 
01136         // Failures from here on out are all non-fatal and not reported.
01137         sendProgress(0, 3, CFSTR("Cleaning up..."));
01138 
01139         // Close disk image file if necessary
01140         if(downloadFile != NULL)
01141         {
01142                 llinfos << "Closing download file." << llendl;
01143 
01144                 fclose(downloadFile);
01145                 downloadFile = NULL;
01146         }
01147 
01148         sendProgress(1, 3);
01149         // Unmount image
01150         if(deviceNode[0] != 0)
01151         {
01152                 llinfos << "Detaching disk image." << llendl;
01153 
01154                 snprintf(temp, sizeof(temp), "hdiutil detach '%s'", deviceNode);                
01155                 system(temp);           /* Flawfinder: ignore */
01156         }
01157 
01158         sendProgress(2, 3);
01159 
01160         // Move work directory to the trash
01161         if(tempDir[0] != 0)
01162         {
01163 //              chdir("/");
01164 //              FSDeleteObjects(tempDirRef);
01165 
01166                 llinfos << "Moving work directory to the trash." << llendl;
01167 
01168                 err = FSMoveObject(&tempDirRef, &trashFolderRef, NULL);
01169 
01170 //              snprintf(temp, sizeof(temp), "rm -rf '%s'", tempDir);
01171 //              printf("%s\n", temp);
01172 //              system(temp);
01173         }
01174         
01175         if(!gCancelled  && !gFailure && (target[0] != 0))
01176         {
01177                 llinfos << "Touching application bundle." << llendl;
01178 
01179                 snprintf(temp, sizeof(temp), "touch '%s'", target);             
01180                 system(temp);           /* Flawfinder: ignore */
01181 
01182                 llinfos << "Launching updated application." << llendl;
01183 
01184                 snprintf(temp, sizeof(temp), "open '%s'", target);              
01185                 system(temp);           /* Flawfinder: ignore */
01186         }
01187 
01188         sendDone();
01189         
01190         return(NULL);
01191 }

Generated on Thu Jul 1 06:09:54 2010 for Second Life Viewer by  doxygen 1.4.7