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
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
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
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
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
00258 result = noErr;
00259 break;
00260 }
00261 }
00262 }
00263 else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomProgress))
00264 {
00265
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
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
00346 gDirUtilp->initAppDirs("SecondLife");
00347
00349
00350
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
00376 OSStatus err;
00377 IBNibRef nib = NULL;
00378
00379 err = CreateNibReference(CFSTR("AutoUpdater"), &nib);
00380
00381 char windowTitle[MAX_PATH];
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
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
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 ¶ms,
00463 &alert);
00464
00465 if(err == noErr)
00466 {
00467 err = RunStandardAlert(
00468 alert,
00469 NULL,
00470 &retval_mac);
00471 }
00472
00473 }
00474
00475
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
00496
00497
00498 OSStatus err = noErr;
00499 char temp[PATH_MAX];
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
00511 result = true;
00512
00513
00514 rmdir(temp);
00515 }
00516 }
00517
00518 #if 0
00519
00520 UInt8 perm;
00521 err = FSGetUserPrivilegesPermissions(&targetParentRef, &perm, NULL);
00522 if(err == noErr)
00523 {
00524 if(perm & kioACUserNoMakeChangesMask)
00525 {
00526
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
00545
00546 dest->length = 255;
00547 }
00548 memcpy(dest->unicode, utf16str.data(), sizeof(UniChar)* dest->length);
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];
00561 char dest[PATH_MAX];
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
00593 void filterFile(const char* filename)
00594 {
00595 char temp[PATH_MAX];
00596
00597 snprintf(temp, sizeof(temp), "cp '%s' '%s.tmp'", filename, filename);
00598 system(temp);
00599
00600
00601 snprintf(temp, sizeof(temp),
00602 "sed 's/Second Life/%s/g' '%s.tmp' > '%s'", gProductName, filename, filename);
00603 system(temp);
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
00642 result = true;
00643 }
00644 else
00645 {
00646 llinfos << "Target bundle ID mismatch." << llendl;
00647 }
00648 }
00649
00650
00651 if(targetURL != NULL)
00652 CFRelease(targetURL);
00653 if(targetBundle != NULL)
00654 CFRelease(targetBundle);
00655
00656 return result;
00657 }
00658
00659
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
00692 std::string name = HFSUniStr255_to_utf8str(&unicodeName);
00693
00694 llinfos << "Considering \"" << name << "\"" << llendl;
00695
00696 if(info.nodeFlags & kFSNodeIsDirectoryMask)
00697 {
00698
00699 if(name.find(".app") != std::string::npos)
00700 {
00701
00702 if(isFSRefViewerBundle(&ref))
00703 {
00704
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] = "";
00726 FSRef tempDirRef;
00727 char temp[PATH_MAX];
00728
00729 char deviceNode[1024] = "";
00730 FILE *downloadFile = NULL;
00731 OSStatus err;
00732 ProcessSerialNumber psn;
00733 char target[PATH_MAX];
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
00747
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
00760 FSRefMakePath(&myBundle, (UInt8*)target, sizeof(target));
00761
00762 llinfos << "Updater bundle location: " << target << llendl;
00763 }
00764
00765
00766
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
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
00793 if(err == noErr)
00794 {
00795
00796 err = -1;
00797
00798 if(isFSRefViewerBundle(&targetRef))
00799 {
00800
00801 err = noErr;
00802 replacingTarget = true;
00803 }
00804 }
00805
00806
00807 if(err == noErr)
00808 {
00809 if(!isDirWritable(targetParentRef))
00810 {
00811
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
00824 err = FSPathMakeRef((UInt8*)"/Applications", &targetParentRef, &isDirectory);
00825 if((err != noErr) || (!isDirectory))
00826 {
00827
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
00839 err = noErr;
00840 replacingTarget = false;
00841 }
00842 else
00843 {
00844 replacingTarget = true;
00845 }
00846
00847
00848 if(err == noErr)
00849 {
00850 if(!isDirWritable(targetParentRef))
00851 {
00852
00853 llinfos << "Target parent directory not writable." << llendl;
00854 err = -1;
00855 replacingTarget = false;
00856 }
00857 }
00858
00859 }
00860
00861
00862 if(err != noErr)
00863 {
00864 llinfos << "Unable to pick a target, giving up." << llendl;
00865 throw 0;
00866 }
00867 }
00868
00869
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
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);
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");
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
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
00973
00974 LLString mountOutput;
00975 FILE* mounter = popen("hdiutil attach SecondLife.dmg -mountpoint mnt", "r");
00976
00977 if(mounter == NULL)
00978 {
00979 llinfos << "Failed to mount disk image, exiting."<< llendl;
00980 throw 0;
00981 }
00982
00983
00984
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
00993 int result = pclose(mounter);
00994 if(result != 0)
00995 {
00996
00997
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);
01013 sscanf(sub, "%1023s", deviceNode);
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
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];
01050
01051
01052 HFSUniStr255 appNameUniStr;
01053
01054 if(replacingTarget)
01055 {
01056
01057 err = FSGetCatalogInfo(&targetRef, 0, NULL, &appNameUniStr, NULL, NULL);
01058 if(err != noErr)
01059 throw 0;
01060
01061
01062 err = FSMoveObject(&targetRef, &tempDirRef, &asideRef);
01063 if(err != noErr)
01064 throw 0;
01065
01066
01067 err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside));
01068 }
01069 else
01070 {
01071
01072 char appName[MAX_PATH];
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
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
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
01106 (void)FSDeleteObjects(&targetRef);
01107 if(replacingTarget)
01108 {
01109 (void)FSMoveObject(&asideRef, &targetParentRef, NULL);
01110 }
01111 throw 0;
01112 }
01113 else
01114 {
01115
01116
01117 sendProgress(0, 0, CFSTR("Clearing cache..."));
01118
01119 llinfos << "Clearing cache..." << llendl;
01120
01121 char mask[LL_MAX_PATH];
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
01137 sendProgress(0, 3, CFSTR("Cleaning up..."));
01138
01139
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
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);
01156 }
01157
01158 sendProgress(2, 3);
01159
01160
01161 if(tempDir[0] != 0)
01162 {
01163
01164
01165
01166 llinfos << "Moving work directory to the trash." << llendl;
01167
01168 err = FSMoveObject(&tempDirRef, &trashFolderRef, NULL);
01169
01170
01171
01172
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);
01181
01182 llinfos << "Launching updated application." << llendl;
01183
01184 snprintf(temp, sizeof(temp), "open '%s'", target);
01185 system(temp);
01186 }
01187
01188 sendDone();
01189
01190 return(NULL);
01191 }