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                 SelectWindow(gWindow);
00420         }
00421                 
00422         if(err == noErr)
00423         {
00424                 pthread_create(&updatethread, 
00425                          NULL,
00426                          &updatethreadproc, 
00427                          NULL);
00428                                                  
00429         }
00430         
00431         if(err == noErr)
00432         {
00433                 RunAppModalLoopForWindow(gWindow);
00434         }
00435 
00436         void *threadresult;
00437 
00438         pthread_join(updatethread, &threadresult);
00439 
00440         if(!gCancelled && (gFailure != noErr))
00441         {
00442                 // Something went wrong.  Since we always just tell the user to download a new version, we don't really care what.
00443                 AlertStdCFStringAlertParamRec params;
00444                 SInt16 retval_mac = 1;
00445                 DialogRef alert = NULL;
00446                 OSStatus err;
00447 
00448                 params.version = kStdCFStringAlertVersionOne;
00449                 params.movable = false;
00450                 params.helpButton = false;
00451                 params.defaultText = (CFStringRef)kAlertDefaultOKText;
00452                 params.cancelText = 0;
00453                 params.otherText = 0;
00454                 params.defaultButton = 1;
00455                 params.cancelButton = 0;
00456                 params.position = kWindowDefaultPosition;
00457                 params.flags = 0;
00458 
00459                 err = CreateStandardAlert(
00460                                 kAlertStopAlert,
00461                                 CFSTR("Error"),
00462                                 CFSTR("An error occurred while updating Second Life.  Please download the latest version from www.secondlife.com."),
00463                                 &params,
00464                                 &alert);
00465                 
00466                 if(err == noErr)
00467                 {
00468                         err = RunStandardAlert(
00469                                         alert,
00470                                         NULL,
00471                                         &retval_mac);
00472                 }
00473 
00474         }
00475         
00476         // Don't dispose of things, just exit.  This keeps the update thread from potentially getting hosed.
00477         exit(0);
00478 
00479         if(gWindow != NULL)
00480         {
00481                 DisposeWindow(gWindow);
00482         }
00483         
00484         if(nib != NULL)
00485         {
00486                 DisposeNibReference(nib);
00487         }
00488         
00489         return 0;
00490 }
00491 
00492 bool isDirWritable(FSRef &dir)
00493 {
00494         bool result = false;
00495         
00496         // Test for a writable directory by creating a directory, then deleting it again.
00497         // This is kinda lame, but will pretty much always give the right answer.
00498         
00499         OSStatus err = noErr;
00500         char temp[PATH_MAX] = "";               /* Flawfinder: ignore */
00501 
00502         err = FSRefMakePath(&dir, (UInt8*)temp, sizeof(temp));
00503 
00504         if(err == noErr)
00505         {
00506                 strncat(temp, "/.test_XXXXXX", (sizeof(temp) - strlen(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         LLFILE *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;
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 #if 0 // *HACK for DEV-11935 see below for details.
00897 
00898                 FSRef tempFolderRef;
00899 
00900                 err = FSFindFolder(
00901                         targetVol,
00902                         kTemporaryFolderType,
00903                         true,
00904                         &tempFolderRef);
00905                 
00906                 if(err != noErr)
00907                         throw 0;
00908                 
00909                 err = FSRefMakePath(&tempFolderRef, (UInt8*)temp, sizeof(temp));
00910 
00911                 if(err != noErr)
00912                         throw 0;
00913 
00914 #else           
00915 
00916                 // *HACK for DEV-11935  the above kTemporaryFolderType query was giving
00917                 // back results with path names that seem to be too long to be used as
00918                 // mount points.  I suspect this incompatibility was introduced in the
00919                 // Leopard 10.5.2 update, but I have not verified this. 
00920                 char const HARDCODED_TMP[] = "/tmp";
00921                 strncpy(temp, HARDCODED_TMP, sizeof(HARDCODED_TMP));
00922 
00923 #endif // 0 *HACK for DEV-11935
00924                 
00925                 strncat(temp, "/SecondLifeUpdate_XXXXXX", (sizeof(temp) - strlen(temp)) - 1);
00926                 if(mkdtemp(temp) == NULL)
00927                 {
00928                         throw 0;
00929                 }
00930                 
00931                 strncpy(tempDir, temp, sizeof(tempDir));
00932                 temp[sizeof(tempDir) - 1] = '\0';
00933                 
00934                 llinfos << "tempDir is " << tempDir << llendl;
00935 
00936                 err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL);
00937 
00938                 if(err != noErr)
00939                         throw 0;
00940                                 
00941                 chdir(tempDir);
00942                 
00943                 snprintf(temp, sizeof(temp), "SecondLife.dmg");         
00944                 
00945                 downloadFile = LLFile::fopen(temp, "wb");               /* Flawfinder: ignore */
00946                 if(downloadFile == NULL)
00947                 {
00948                         throw 0;
00949                 }
00950 
00951                 {
00952                         CURL *curl = curl_easy_init();
00953 
00954                         curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
00955         //              curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
00956                         curl_easy_setopt(curl, CURLOPT_FILE, downloadFile);
00957                         curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
00958                         curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &curl_progress_callback_func);
00959                         curl_easy_setopt(curl, CURLOPT_URL,     gUpdateURL);
00960                         curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
00961                         
00962                         sendProgress(0, 1, CFSTR("Downloading..."));
00963                         
00964                         CURLcode result = curl_easy_perform(curl);
00965                         
00966                         curl_easy_cleanup(curl);
00967                         
00968                         if(gCancelled)
00969                         {
00970                                 llinfos << "User cancel, bailing out."<< llendl;
00971                                 throw 0;
00972                         }
00973                         
00974                         if(result != CURLE_OK)
00975                         {
00976                                 llinfos << "Error " << result << " while downloading disk image."<< llendl;
00977                                 throw 0;
00978                         }
00979                         
00980                         fclose(downloadFile);
00981                         downloadFile = NULL;
00982                 }
00983                 
00984                 sendProgress(0, 0, CFSTR("Mounting image..."));
00985                 LLFile::mkdir("mnt", 0700);
00986                 
00987                 // NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder,
00988                 //              but if our cleanup fails, this makes it much harder for the user to unmount the image.
00989                 LLString mountOutput;
00990                 FILE* mounter = popen("hdiutil attach SecondLife.dmg -mountpoint mnt", "r");            /* Flawfinder: ignore */
00991                 
00992                 if(mounter == NULL)
00993                 {
00994                         llinfos << "Failed to mount disk image, exiting."<< llendl;
00995                         throw 0;
00996                 }
00997                 
00998                 // We need to scan the output from hdiutil to find the device node it uses to attach the disk image.
00999                 // If we don't have this information, we can't detach it later.
01000                 while(mounter != NULL)
01001                 {
01002                         size_t len = fread(temp, 1, sizeof(temp)-1, mounter);
01003                         temp[len] = 0;
01004                         mountOutput.append(temp);
01005                         if(len < sizeof(temp)-1)
01006                         {
01007                                 // End of file or error.
01008                                 int result = pclose(mounter);
01009                                 if(result != 0)
01010                                 {
01011                                         // NOTE: We used to abort here, but pclose() started returning 
01012                                         // -1, possibly when the size of the DMG passed a certain point 
01013                                         llinfos << "Unexpected result closing pipe: " << result << llendl; 
01014                                 }
01015                                 mounter = NULL;
01016                         }
01017                 }
01018                 
01019                 if(!mountOutput.empty())
01020                 {
01021                         const char *s = mountOutput.c_str();
01022                         char *prefix = "/dev/";
01023                         char *sub = strstr(s, prefix);
01024                         
01025                         if(sub != NULL)
01026                         {
01027                                 sub += strlen(prefix);  /* Flawfinder: ignore */
01028                                 sscanf(sub, "%1023s", deviceNode);      /* Flawfinder: ignore */
01029                         }
01030                 }
01031                 
01032                 if(deviceNode[0] != 0)
01033                 {
01034                         llinfos << "Disk image attached on /dev/" << deviceNode << llendl;
01035                 }
01036                 else
01037                 {
01038                         llinfos << "Disk image device node not found!" << llendl;
01039                         throw 0; 
01040                 }
01041                 
01042                 // Get an FSRef to the new application on the disk image
01043                 FSRef sourceRef;
01044                 FSRef mountRef;
01045                 snprintf(temp, sizeof(temp), "%s/mnt", tempDir);                
01046 
01047                 llinfos << "Disk image mount point is: " << temp << llendl;
01048 
01049                 err = FSPathMakeRef((UInt8 *)temp, &mountRef, NULL);
01050                 if(err != noErr)
01051                 {
01052                         llinfos << "Couldn't make FSRef to disk image mount point." << llendl;
01053                         throw 0;
01054                 }
01055 
01056                 err = findAppBundleOnDiskImage(&mountRef, &sourceRef);
01057                 if(err != noErr)
01058                 {
01059                         llinfos << "Couldn't find application bundle on mounted disk image." << llendl;
01060                         throw 0;
01061                 }
01062                 
01063                 FSRef asideRef;
01064                 char aside[MAX_PATH];           /* Flawfinder: ignore */
01065                 
01066                 // this will hold the name of the destination target
01067                 HFSUniStr255 appNameUniStr;
01068 
01069                 if(replacingTarget)
01070                 {
01071                         // Get the name of the target we're replacing
01072                         err = FSGetCatalogInfo(&targetRef, 0, NULL, &appNameUniStr, NULL, NULL);
01073                         if(err != noErr)
01074                                 throw 0;
01075                         
01076                         // Move aside old version (into work directory)
01077                         err = FSMoveObject(&targetRef, &tempDirRef, &asideRef);
01078                         if(err != noErr)
01079                                 throw 0;
01080 
01081                         // Grab the path for later use.
01082                         err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside));
01083                 }
01084                 else
01085                 {
01086                         // Construct the name of the target based on the product name
01087                         char appName[MAX_PATH];         /* Flawfinder: ignore */
01088                         snprintf(appName, sizeof(appName), "%s.app", gProductName);             
01089                         utf8str_to_HFSUniStr255( &appNameUniStr, appName );
01090                 }
01091                 
01092                 sendProgress(0, 0, CFSTR("Copying files..."));
01093                 
01094                 llinfos << "Starting copy..." << llendl;
01095 
01096                 // Copy the new version from the disk image to the target location.
01097                 err = FSCopyObject(     
01098                                 &sourceRef,
01099                                 &targetParentRef,
01100                                 0,
01101                                 kFSCatInfoNone,
01102                                 kDupeActionStandard,
01103                                 &appNameUniStr,
01104                                 false,
01105                                 false,
01106                                 NULL,
01107                                 NULL,
01108                                 &targetRef,
01109                                 NULL);
01110                 
01111                 // Grab the path for later use.
01112                 err = FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target));
01113                 if(err != noErr)
01114                         throw 0;
01115 
01116                 llinfos << "Copy complete. Target = " << target << llendl;
01117 
01118                 if(err != noErr)
01119                 {
01120                         // Something went wrong during the copy.  Attempt to put the old version back and bail.
01121                         (void)FSDeleteObjects(&targetRef);
01122                         if(replacingTarget)
01123                         {
01124                                 (void)FSMoveObject(&asideRef, &targetParentRef, NULL);
01125                         }
01126                         throw 0;
01127                 }
01128                 else
01129                 {
01130                         // The update has succeeded.  Clear the cache directory.
01131 
01132                         sendProgress(0, 0, CFSTR("Clearing cache..."));
01133         
01134                         llinfos << "Clearing cache..." << llendl;
01135                         
01136                         char mask[LL_MAX_PATH];         /* Flawfinder: ignore */
01137                         snprintf(mask, LL_MAX_PATH, "%s*.*", gDirUtilp->getDirDelimiter().c_str());             
01138                         gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask);
01139                         
01140                         llinfos << "Clear complete." << llendl;
01141 
01142                 }
01143         }
01144         catch(...)
01145         {
01146                 if(!gCancelled)
01147                         if(gFailure == noErr)
01148                                 gFailure = -1;
01149         }
01150 
01151         // Failures from here on out are all non-fatal and not reported.
01152         sendProgress(0, 3, CFSTR("Cleaning up..."));
01153 
01154         // Close disk image file if necessary
01155         if(downloadFile != NULL)
01156         {
01157                 llinfos << "Closing download file." << llendl;
01158 
01159                 fclose(downloadFile);
01160                 downloadFile = NULL;
01161         }
01162 
01163         sendProgress(1, 3);
01164         // Unmount image
01165         if(deviceNode[0] != 0)
01166         {
01167                 llinfos << "Detaching disk image." << llendl;
01168 
01169                 snprintf(temp, sizeof(temp), "hdiutil detach '%s'", deviceNode);                
01170                 system(temp);           /* Flawfinder: ignore */
01171         }
01172 
01173         sendProgress(2, 3);
01174 
01175         // Move work directory to the trash
01176         if(tempDir[0] != 0)
01177         {
01178 //              chdir("/");
01179 //              FSDeleteObjects(tempDirRef);
01180 
01181                 llinfos << "Moving work directory to the trash." << llendl;
01182 
01183                 err = FSMoveObject(&tempDirRef, &trashFolderRef, NULL);
01184 
01185 //              snprintf(temp, sizeof(temp), "rm -rf '%s'", tempDir);
01186 //              printf("%s\n", temp);
01187 //              system(temp);
01188         }
01189         
01190         if(!gCancelled  && !gFailure && (target[0] != 0))
01191         {
01192                 llinfos << "Touching application bundle." << llendl;
01193 
01194                 snprintf(temp, sizeof(temp), "touch '%s'", target);             
01195                 system(temp);           /* Flawfinder: ignore */
01196 
01197                 llinfos << "Launching updated application." << llendl;
01198 
01199                 snprintf(temp, sizeof(temp), "open '%s'", target);              
01200                 system(temp);           /* Flawfinder: ignore */
01201         }
01202 
01203         sendDone();
01204         
01205         return(NULL);
01206 }

Generated on Fri May 16 08:33:13 2008 for SecondLife by  doxygen 1.5.5