mac_updater.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:28k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file mac_updater.cpp
  3.  * @brief 
  4.  *
  5.  * $LicenseInfo:firstyear=2006&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2006-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #include "linden_common.h"
  33. #include <sys/types.h>
  34. #include <sys/stat.h>
  35. #include <unistd.h>
  36. #include <curl/curl.h>
  37. #include <pthread.h>
  38. #include "llerror.h"
  39. #include "lltimer.h"
  40. #include "lldir.h"
  41. #include "llfile.h"
  42. #include "llstring.h"
  43. #include <Carbon/Carbon.h>
  44. #include "MoreFilesX.h"
  45. #include "FSCopyObject.h"
  46. #include "llerrorcontrol.h"
  47. enum
  48. {
  49. kEventClassCustom = 'Cust',
  50. kEventCustomProgress = 'Prog',
  51. kEventParamCustomCurValue = 'Cur ',
  52. kEventParamCustomMaxValue = 'Max ',
  53. kEventParamCustomText = 'Text',
  54. kEventCustomDone = 'Done',
  55. };
  56. WindowRef gWindow = NULL;
  57. EventHandlerRef gEventHandler = NULL;
  58. OSStatus gFailure = noErr;
  59. Boolean gCancelled = false;
  60. const char *gUpdateURL;
  61. const char *gProductName;
  62. void *updatethreadproc(void*);
  63. pthread_t updatethread;
  64. OSStatus setProgress(int cur, int max)
  65. {
  66. OSStatus err;
  67. ControlRef progressBar = NULL;
  68. ControlID id;
  69. id.signature = 'prog';
  70. id.id = 0;
  71. err = GetControlByID(gWindow, &id, &progressBar);
  72. if(err == noErr)
  73. {
  74. Boolean indeterminate;
  75. if(max == 0)
  76. {
  77. indeterminate = true;
  78. err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate);
  79. }
  80. else
  81. {
  82. double percentage = (double)cur / (double)max;
  83. SetControlMinimum(progressBar, 0);
  84. SetControlMaximum(progressBar, 100);
  85. SetControlValue(progressBar, (SInt16)(percentage * 100));
  86. indeterminate = false;
  87. err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate);
  88. Draw1Control(progressBar);
  89. }
  90. }
  91. return(err);
  92. }
  93. OSStatus setProgressText(CFStringRef text)
  94. {
  95. OSStatus err;
  96. ControlRef progressText = NULL;
  97. ControlID id;
  98. id.signature = 'what';
  99. id.id = 0;
  100. err = GetControlByID(gWindow, &id, &progressText);
  101. if(err == noErr)
  102. {
  103. err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&text);
  104. Draw1Control(progressText);
  105. }
  106. return(err);
  107. }
  108. OSStatus sendProgress(long cur, long max, CFStringRef text = NULL)
  109. {
  110. OSStatus result;
  111. EventRef evt;
  112. result = CreateEvent( 
  113. NULL,
  114. kEventClassCustom, 
  115. kEventCustomProgress,
  116. 0, 
  117. kEventAttributeNone, 
  118. &evt);
  119. // This event needs to be targeted at the window so it goes to the window's handler.
  120. if(result == noErr)
  121. {
  122. EventTargetRef target = GetWindowEventTarget(gWindow);
  123. result = SetEventParameter (
  124. evt,
  125. kEventParamPostTarget,
  126. typeEventTargetRef,
  127. sizeof(target),
  128. &target);
  129. }
  130. if(result == noErr)
  131. {
  132. result = SetEventParameter (
  133. evt,
  134. kEventParamCustomCurValue,
  135. typeLongInteger,
  136. sizeof(cur),
  137. &cur);
  138. }
  139. if(result == noErr)
  140. {
  141. result = SetEventParameter (
  142. evt,
  143. kEventParamCustomMaxValue,
  144. typeLongInteger,
  145. sizeof(max),
  146. &max);
  147. }
  148. if(result == noErr)
  149. {
  150. if(text != NULL)
  151. {
  152. result = SetEventParameter (
  153. evt,
  154. kEventParamCustomText,
  155. typeCFStringRef,
  156. sizeof(text),
  157. &text);
  158. }
  159. }
  160. if(result == noErr)
  161. {
  162. // Send the event
  163. PostEventToQueue(
  164. GetMainEventQueue(),
  165. evt,
  166. kEventPriorityStandard);
  167. }
  168. return(result);
  169. }
  170. OSStatus sendDone(void)
  171. {
  172. OSStatus result;
  173. EventRef evt;
  174. result = CreateEvent( 
  175. NULL,
  176. kEventClassCustom, 
  177. kEventCustomDone,
  178. 0, 
  179. kEventAttributeNone, 
  180. &evt);
  181. // This event needs to be targeted at the window so it goes to the window's handler.
  182. if(result == noErr)
  183. {
  184. EventTargetRef target = GetWindowEventTarget(gWindow);
  185. result = SetEventParameter (
  186. evt,
  187. kEventParamPostTarget,
  188. typeEventTargetRef,
  189. sizeof(target),
  190. &target);
  191. }
  192. if(result == noErr)
  193. {
  194. // Send the event
  195. PostEventToQueue(
  196. GetMainEventQueue(),
  197. evt,
  198. kEventPriorityStandard);
  199. }
  200. return(result);
  201. }
  202. OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
  203. {
  204. OSStatus result = eventNotHandledErr;
  205. OSStatus err;
  206. UInt32 evtClass = GetEventClass(event);
  207. UInt32 evtKind = GetEventKind(event);
  208. if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
  209. {
  210. HICommand cmd;
  211. err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd);
  212. if(err == noErr)
  213. {
  214. switch(cmd.commandID)
  215. {
  216. case kHICommandCancel:
  217. gCancelled = true;
  218. // QuitAppModalLoopForWindow(gWindow);
  219. result = noErr;
  220. break;
  221. }
  222. }
  223. }
  224. else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomProgress))
  225. {
  226. // Request to update the progress dialog
  227. long cur = 0;
  228. long max = 0;
  229. CFStringRef text = NULL;
  230. (void) GetEventParameter(event, kEventParamCustomCurValue, typeLongInteger, NULL, sizeof(cur), NULL, &cur);
  231. (void) GetEventParameter(event, kEventParamCustomMaxValue, typeLongInteger, NULL, sizeof(max), NULL, &max);
  232. (void) GetEventParameter(event, kEventParamCustomText, typeCFStringRef, NULL, sizeof(text), NULL, &text);
  233. err = setProgress(cur, max);
  234. if(err == noErr)
  235. {
  236. if(text != NULL)
  237. {
  238. setProgressText(text);
  239. }
  240. }
  241. result = noErr;
  242. }
  243. else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomDone))
  244. {
  245. // We're done.  Exit the modal loop.
  246. QuitAppModalLoopForWindow(gWindow);
  247. result = noErr;
  248. }
  249. return(result);
  250. }
  251. #if 0
  252. size_t curl_download_callback(void *data, size_t size, size_t nmemb,
  253.   void *user_data)
  254. {
  255. S32 bytes = size * nmemb;
  256. char *cdata = (char *) data;
  257. for (int i =0; i < bytes; i += 1)
  258. {
  259. gServerResponse.append(cdata[i]);
  260. }
  261. return bytes;
  262. }
  263. #endif
  264. int curl_progress_callback_func(void *clientp,
  265.   double dltotal,
  266.   double dlnow,
  267.   double ultotal,
  268.   double ulnow)
  269. {
  270. int max = (int)(dltotal / 1024.0);
  271. int cur = (int)(dlnow / 1024.0);
  272. sendProgress(cur, max);
  273. if(gCancelled)
  274. return(1);
  275. return(0);
  276. }
  277. int parse_args(int argc, char **argv)
  278. {
  279. int j;
  280. for (j = 1; j < argc; j++) 
  281. {
  282. if ((!strcmp(argv[j], "-url")) && (++j < argc)) 
  283. {
  284. gUpdateURL = argv[j];
  285. }
  286. else if ((!strcmp(argv[j], "-name")) && (++j < argc)) 
  287. {
  288. gProductName = argv[j];
  289. }
  290. }
  291. return 0;
  292. }
  293. int main(int argc, char **argv)
  294. {
  295. // We assume that all the logs we're looking for reside on the current drive
  296. gDirUtilp->initAppDirs("SecondLife");
  297. LLError::initForApplication( gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
  298. // Rename current log file to ".old"
  299. std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "updater.log.old");
  300. std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "updater.log");
  301. LLFile::rename(log_file.c_str(), old_log_file.c_str());
  302. // Set the log file to updater.log
  303. LLError::logToFile(log_file);
  304. /////////////////////////////////////////
  305. //
  306. // Process command line arguments
  307. //
  308. gUpdateURL  = NULL;
  309. gProductName = NULL;
  310. parse_args(argc, argv);
  311. if (!gUpdateURL)
  312. {
  313. llinfos << "Usage: mac_updater -url <url> [-name <product_name>] [-program <program_name>]" << llendl;
  314. exit(1);
  315. }
  316. else
  317. {
  318. llinfos << "Update url is: " << gUpdateURL << llendl;
  319. if (gProductName)
  320. {
  321. llinfos << "Product name is: " << gProductName << llendl;
  322. }
  323. else
  324. {
  325. gProductName = "Second Life";
  326. }
  327. }
  328. llinfos << "Starting " << gProductName << " Updater" << llendl;
  329. // Real UI...
  330. OSStatus err;
  331. IBNibRef nib = NULL;
  332. err = CreateNibReference(CFSTR("AutoUpdater"), &nib);
  333. char windowTitle[MAX_PATH]; /* Flawfinder: ignore */
  334. snprintf(windowTitle, sizeof(windowTitle), "%s Updater", gProductName);
  335. CFStringRef windowTitleRef = NULL;
  336. windowTitleRef = CFStringCreateWithCString(NULL, windowTitle, kCFStringEncodingUTF8);
  337. if(err == noErr)
  338. {
  339. err = CreateWindowFromNib(nib, CFSTR("Updater"), &gWindow);
  340. }
  341. if (err == noErr)
  342. {
  343. err = SetWindowTitleWithCFString(gWindow, windowTitleRef);
  344. }
  345. CFRelease(windowTitleRef);
  346. if(err == noErr)
  347. {
  348. // Set up an event handler for the window.
  349. EventTypeSpec handlerEvents[] = 
  350. {
  351. { kEventClassCommand, kEventCommandProcess },
  352. { kEventClassCustom, kEventCustomProgress },
  353. { kEventClassCustom, kEventCustomDone }
  354. };
  355. InstallStandardEventHandler(GetWindowEventTarget(gWindow));
  356. InstallWindowEventHandler(
  357. gWindow, 
  358. NewEventHandlerUPP(dialogHandler), 
  359. GetEventTypeCount (handlerEvents), 
  360. handlerEvents, 
  361. 0, 
  362. &gEventHandler);
  363. }
  364. if(err == noErr)
  365. {
  366. ShowWindow(gWindow);
  367. SelectWindow(gWindow);
  368. }
  369. if(err == noErr)
  370. {
  371. pthread_create(&updatethread, 
  372.                          NULL,
  373.                          &updatethreadproc, 
  374.                          NULL);
  375.  
  376. }
  377. if(err == noErr)
  378. {
  379. RunAppModalLoopForWindow(gWindow);
  380. }
  381. void *threadresult;
  382. pthread_join(updatethread, &threadresult);
  383. if(!gCancelled && (gFailure != noErr))
  384. {
  385. // Something went wrong.  Since we always just tell the user to download a new version, we don't really care what.
  386. AlertStdCFStringAlertParamRec params;
  387. SInt16 retval_mac = 1;
  388. DialogRef alert = NULL;
  389. OSStatus err;
  390. params.version = kStdCFStringAlertVersionOne;
  391. params.movable = false;
  392. params.helpButton = false;
  393. params.defaultText = (CFStringRef)kAlertDefaultOKText;
  394. params.cancelText = 0;
  395. params.otherText = 0;
  396. params.defaultButton = 1;
  397. params.cancelButton = 0;
  398. params.position = kWindowDefaultPosition;
  399. params.flags = 0;
  400. err = CreateStandardAlert(
  401. kAlertStopAlert,
  402. CFSTR("Error"),
  403. CFSTR("An error occurred while updating Second Life.  Please download the latest version from www.secondlife.com."),
  404. &params,
  405. &alert);
  406. if(err == noErr)
  407. {
  408. err = RunStandardAlert(
  409. alert,
  410. NULL,
  411. &retval_mac);
  412. }
  413. }
  414. // Don't dispose of things, just exit.  This keeps the update thread from potentially getting hosed.
  415. exit(0);
  416. if(gWindow != NULL)
  417. {
  418. DisposeWindow(gWindow);
  419. }
  420. if(nib != NULL)
  421. {
  422. DisposeNibReference(nib);
  423. }
  424. return 0;
  425. }
  426. bool isDirWritable(FSRef &dir)
  427. {
  428. bool result = false;
  429. // Test for a writable directory by creating a directory, then deleting it again.
  430. // This is kinda lame, but will pretty much always give the right answer.
  431. OSStatus err = noErr;
  432. char temp[PATH_MAX] = ""; /* Flawfinder: ignore */
  433. err = FSRefMakePath(&dir, (UInt8*)temp, sizeof(temp));
  434. if(err == noErr)
  435. {
  436. strncat(temp, "/.test_XXXXXX", (sizeof(temp) - strlen(temp)) - 1);
  437. if(mkdtemp(temp) != NULL)
  438. {
  439. // We were able to make the directory.  This means the directory is writable.
  440. result = true;
  441. // Clean up.
  442. rmdir(temp);
  443. }
  444. }
  445. #if 0
  446. // This seemed like a good idea, but won't tell us if we're on a volume mounted read-only.
  447. UInt8 perm;
  448. err = FSGetUserPrivilegesPermissions(&targetParentRef, &perm, NULL);
  449. if(err == noErr)
  450. {
  451. if(perm & kioACUserNoMakeChangesMask)
  452. {
  453. // Parent directory isn't writable.
  454. llinfos << "Target parent directory not writable." << llendl;
  455. err = -1;
  456. replacingTarget = false;
  457. }
  458. }
  459. #endif
  460. return result;
  461. }
  462. static void utf8str_to_HFSUniStr255(HFSUniStr255 *dest, const char* src)
  463. {
  464. llutf16string utf16str = utf8str_to_utf16str(src);
  465. dest->length = utf16str.size();
  466. if(dest->length > 255)
  467. {
  468. // There's onl room for 255 chars in a HFSUniStr25..
  469. // Truncate to avoid stack smaching or other badness.
  470. dest->length = 255;
  471. }
  472. memcpy(dest->unicode, utf16str.data(), sizeof(UniChar)* dest->length); /* Flawfinder: ignore */
  473. }
  474. static std::string HFSUniStr255_to_utf8str(const HFSUniStr255* src)
  475. {
  476. llutf16string string16((U16*)&(src->unicode), src->length);
  477. std::string result = utf16str_to_utf8str(string16);
  478. return result;
  479. }
  480. int restoreObject(const char* aside, const char* target, const char* path, const char* object)
  481. {
  482. char source[PATH_MAX] = ""; /* Flawfinder: ignore */
  483. char dest[PATH_MAX] = ""; /* Flawfinder: ignore */
  484. snprintf(source, sizeof(source), "%s/%s/%s", aside, path, object);
  485. snprintf(dest, sizeof(dest), "%s/%s", target, path);
  486. FSRef sourceRef;
  487. FSRef destRef;
  488. OSStatus err;
  489. err = FSPathMakeRef((UInt8 *)source, &sourceRef, NULL);
  490. if(err != noErr) return false;
  491. err = FSPathMakeRef((UInt8 *)dest, &destRef, NULL);
  492. if(err != noErr) return false;
  493. llinfos << "Copying " << source << " to " << dest << llendl;
  494. err = FSCopyObject(
  495. &sourceRef,
  496. &destRef,
  497. 0,
  498. kFSCatInfoNone,
  499. kDupeActionReplace,
  500. NULL,
  501. false,
  502. false,
  503. NULL,
  504. NULL,
  505. NULL,
  506. NULL);
  507. if(err != noErr) return false;
  508. return true;
  509. }
  510. // Replace any mention of "Second Life" with the product name.
  511. void filterFile(const char* filename)
  512. {
  513. char temp[PATH_MAX] = ""; /* Flawfinder: ignore */
  514. // First copy the target's version, so we can run it through sed.
  515. snprintf(temp, sizeof(temp), "cp '%s' '%s.tmp'", filename, filename);
  516. system(temp); /* Flawfinder: ignore */
  517. // Now run it through sed.
  518. snprintf(temp, sizeof(temp), 
  519. "sed 's/Second Life/%s/g' '%s.tmp' > '%s'", gProductName, filename, filename);
  520. system(temp); /* Flawfinder: ignore */
  521. }
  522. static bool isFSRefViewerBundle(FSRef *targetRef)
  523. {
  524. bool result = false;
  525. CFURLRef targetURL = NULL;
  526. CFBundleRef targetBundle = NULL;
  527. CFStringRef targetBundleID = NULL;
  528. targetURL = CFURLCreateFromFSRef(NULL, targetRef);
  529. if(targetURL == NULL)
  530. {
  531. llinfos << "Error creating target URL." << llendl;
  532. }
  533. else
  534. {
  535. targetBundle = CFBundleCreate(NULL, targetURL);
  536. }
  537. if(targetBundle == NULL)
  538. {
  539. llinfos << "Failed to create target bundle." << llendl;
  540. }
  541. else
  542. {
  543. targetBundleID = CFBundleGetIdentifier(targetBundle);
  544. }
  545. if(targetBundleID == NULL)
  546. {
  547. llinfos << "Couldn't retrieve target bundle ID." << llendl;
  548. }
  549. else
  550. {
  551. if(CFStringCompare(targetBundleID, CFSTR("com.secondlife.indra.viewer"), 0) == kCFCompareEqualTo)
  552. {
  553. // This is the bundle we're looking for.
  554. result = true;
  555. }
  556. else
  557. {
  558. llinfos << "Target bundle ID mismatch." << llendl;
  559. }
  560. }
  561. // Don't release targetBundleID -- since we don't retain it, it's released when targetBundle is released.
  562. if(targetURL != NULL)
  563. CFRelease(targetURL);
  564. if(targetBundle != NULL)
  565. CFRelease(targetBundle);
  566. return result;
  567. }
  568. // Search through the directory specified by 'parent' for an item that appears to be a Second Life viewer.
  569. static OSErr findAppBundleOnDiskImage(FSRef *parent, FSRef *app)
  570. {
  571. FSIterator iterator;
  572. bool found = false;
  573. OSErr err = FSOpenIterator( parent, kFSIterateFlat, &iterator );
  574. if(!err)
  575. {
  576. do
  577. {
  578. ItemCount actualObjects = 0;
  579. Boolean containerChanged = false;
  580. FSCatalogInfo info;
  581. FSRef ref;
  582. HFSUniStr255 unicodeName;
  583. err = FSGetCatalogInfoBulk( 
  584. iterator, 
  585. 1, 
  586. &actualObjects, 
  587. &containerChanged,
  588. kFSCatInfoNodeFlags, 
  589. &info, 
  590. &ref,
  591. NULL, 
  592. &unicodeName );
  593. if(actualObjects == 0)
  594. break;
  595. if(!err)
  596. {
  597. // Call succeeded and not done with the iteration.
  598. std::string name = HFSUniStr255_to_utf8str(&unicodeName);
  599. llinfos << "Considering "" << name << """ << llendl;
  600. if(info.nodeFlags & kFSNodeIsDirectoryMask)
  601. {
  602. // This is a directory.  See if it's a .app
  603. if(name.find(".app") != std::string::npos)
  604. {
  605. // Looks promising.  Check to see if it has the right bundle identifier.
  606. if(isFSRefViewerBundle(&ref))
  607. {
  608. // This is the one.  Return it.
  609. *app = ref;
  610. found = true;
  611. }
  612. }
  613. }
  614. }
  615. }
  616. while(!err && !found);
  617. FSCloseIterator(iterator);
  618. }
  619. if(!err && !found)
  620. err = fnfErr;
  621. return err;
  622. }
  623. void *updatethreadproc(void*)
  624. {
  625. char tempDir[PATH_MAX] = ""; /* Flawfinder: ignore */
  626. FSRef tempDirRef;
  627. char temp[PATH_MAX] = ""; /* Flawfinder: ignore */
  628. // *NOTE: This buffer length is used in a scanf() below.
  629. char deviceNode[1024] = ""; /* Flawfinder: ignore */
  630. LLFILE *downloadFile = NULL;
  631. OSStatus err;
  632. ProcessSerialNumber psn;
  633. char target[PATH_MAX] = ""; /* Flawfinder: ignore */
  634. FSRef targetRef;
  635. FSRef targetParentRef;
  636. FSVolumeRefNum targetVol;
  637. FSRef trashFolderRef;
  638. Boolean replacingTarget = false;
  639. memset(&tempDirRef, 0, sizeof(tempDirRef));
  640. memset(&targetRef, 0, sizeof(targetRef));
  641. memset(&targetParentRef, 0, sizeof(targetParentRef));
  642. try
  643. {
  644. // Attempt to get a reference to the Second Life application bundle containing this updater.
  645. // Any failures during this process will cause us to default to updating /Applications/Second Life.app
  646. {
  647. FSRef myBundle;
  648. err = GetCurrentProcess(&psn);
  649. if(err == noErr)
  650. {
  651. err = GetProcessBundleLocation(&psn, &myBundle);
  652. }
  653. if(err == noErr)
  654. {
  655. // Sanity check:  Make sure the name of the item referenced by targetRef is "Second Life.app".
  656. FSRefMakePath(&myBundle, (UInt8*)target, sizeof(target));
  657. llinfos << "Updater bundle location: " << target << llendl;
  658. }
  659. // Our bundle should be in Second Life.app/Contents/Resources/AutoUpdater.app
  660. // so we need to go up 3 levels to get the path to the main application bundle.
  661. if(err == noErr)
  662. {
  663. err = FSGetParentRef(&myBundle, &targetRef);
  664. }
  665. if(err == noErr)
  666. {
  667. err = FSGetParentRef(&targetRef, &targetRef);
  668. }
  669. if(err == noErr)
  670. {
  671. err = FSGetParentRef(&targetRef, &targetRef);
  672. }
  673. // And once more to get the parent of the target
  674. if(err == noErr)
  675. {
  676. err = FSGetParentRef(&targetRef, &targetParentRef);
  677. }
  678. if(err == noErr)
  679. {
  680. FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target));
  681. llinfos << "Path to target: " << target << llendl;
  682. }
  683. // Sanity check: make sure the target is a bundle with the right identifier
  684. if(err == noErr)
  685. {
  686. // Assume the worst...
  687. err = -1;
  688. if(isFSRefViewerBundle(&targetRef))
  689. {
  690. // This is the bundle we're looking for.
  691. err = noErr;
  692. replacingTarget = true;
  693. }
  694. }
  695. // Make sure the target's parent directory is writable.
  696. if(err == noErr)
  697. {
  698. if(!isDirWritable(targetParentRef))
  699. {
  700. // Parent directory isn't writable.
  701. llinfos << "Target parent directory not writable." << llendl;
  702. err = -1;
  703. replacingTarget = false;
  704. }
  705. }
  706. if(err != noErr)
  707. {
  708. Boolean isDirectory;
  709. llinfos << "Target search failed, defaulting to /Applications/" << gProductName << ".app." << llendl;
  710. // Set up the parent directory
  711. err = FSPathMakeRef((UInt8*)"/Applications", &targetParentRef, &isDirectory);
  712. if((err != noErr) || (!isDirectory))
  713. {
  714. // We're so hosed.
  715. llinfos << "Applications directory not found, giving up." << llendl;
  716. throw 0;
  717. }
  718. snprintf(target, sizeof(target), "/Applications/%s.app", gProductName);
  719. memset(&targetRef, 0, sizeof(targetRef));
  720. err = FSPathMakeRef((UInt8*)target, &targetRef, NULL);
  721. if(err == fnfErr)
  722. {
  723. // This is fine, just means we're not replacing anything.
  724. err = noErr;
  725. replacingTarget = false;
  726. }
  727. else
  728. {
  729. replacingTarget = true;
  730. }
  731. // Make sure the target's parent directory is writable.
  732. if(err == noErr)
  733. {
  734. if(!isDirWritable(targetParentRef))
  735. {
  736. // Parent directory isn't writable.
  737. llinfos << "Target parent directory not writable." << llendl;
  738. err = -1;
  739. replacingTarget = false;
  740. }
  741. }
  742. }
  743. // If we haven't fixed all problems by this point, just bail.
  744. if(err != noErr)
  745. {
  746. llinfos << "Unable to pick a target, giving up." << llendl;
  747. throw 0;
  748. }
  749. }
  750. // Find the volID of the volume the target resides on
  751. {
  752. FSCatalogInfo info;
  753. err = FSGetCatalogInfo(
  754. &targetParentRef,
  755. kFSCatInfoVolume,
  756. &info,
  757. NULL, 
  758. NULL,  
  759. NULL);
  760. if(err != noErr)
  761. throw 0;
  762. targetVol = info.volume;
  763. }
  764. // Find the temporary items and trash folders on that volume.
  765. err = FSFindFolder(
  766. targetVol,
  767. kTrashFolderType,
  768. true,
  769. &trashFolderRef);
  770. if(err != noErr)
  771. throw 0;
  772. #if 0 // *HACK for DEV-11935 see below for details.
  773. FSRef tempFolderRef;
  774. err = FSFindFolder(
  775. targetVol,
  776. kTemporaryFolderType,
  777. true,
  778. &tempFolderRef);
  779. if(err != noErr)
  780. throw 0;
  781. err = FSRefMakePath(&tempFolderRef, (UInt8*)temp, sizeof(temp));
  782. if(err != noErr)
  783. throw 0;
  784. #else
  785. // *HACK for DEV-11935  the above kTemporaryFolderType query was giving
  786. // back results with path names that seem to be too long to be used as
  787. // mount points.  I suspect this incompatibility was introduced in the
  788. // Leopard 10.5.2 update, but I have not verified this. 
  789. char const HARDCODED_TMP[] = "/tmp";
  790. strncpy(temp, HARDCODED_TMP, sizeof(HARDCODED_TMP));
  791. #endif // 0 *HACK for DEV-11935
  792. strncat(temp, "/SecondLifeUpdate_XXXXXX", (sizeof(temp) - strlen(temp)) - 1);
  793. if(mkdtemp(temp) == NULL)
  794. {
  795. throw 0;
  796. }
  797. strncpy(tempDir, temp, sizeof(tempDir));
  798. temp[sizeof(tempDir) - 1] = '';
  799. llinfos << "tempDir is " << tempDir << llendl;
  800. err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL);
  801. if(err != noErr)
  802. throw 0;
  803. chdir(tempDir);
  804. snprintf(temp, sizeof(temp), "SecondLife.dmg");
  805. downloadFile = LLFile::fopen(temp, "wb"); /* Flawfinder: ignore */
  806. if(downloadFile == NULL)
  807. {
  808. throw 0;
  809. }
  810. {
  811. CURL *curl = curl_easy_init();
  812. curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
  813. // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
  814. curl_easy_setopt(curl, CURLOPT_FILE, downloadFile);
  815. curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
  816. curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &curl_progress_callback_func);
  817. curl_easy_setopt(curl, CURLOPT_URL, gUpdateURL);
  818. curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
  819. sendProgress(0, 1, CFSTR("Downloading..."));
  820. CURLcode result = curl_easy_perform(curl);
  821. curl_easy_cleanup(curl);
  822. if(gCancelled)
  823. {
  824. llinfos << "User cancel, bailing out."<< llendl;
  825. throw 0;
  826. }
  827. if(result != CURLE_OK)
  828. {
  829. llinfos << "Error " << result << " while downloading disk image."<< llendl;
  830. throw 0;
  831. }
  832. fclose(downloadFile);
  833. downloadFile = NULL;
  834. }
  835. sendProgress(0, 0, CFSTR("Mounting image..."));
  836. LLFile::mkdir("mnt", 0700);
  837. // NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder,
  838. // but if our cleanup fails, this makes it much harder for the user to unmount the image.
  839. std::string mountOutput;
  840. FILE* mounter = popen("hdiutil attach SecondLife.dmg -mountpoint mnt", "r"); /* Flawfinder: ignore */
  841. if(mounter == NULL)
  842. {
  843. llinfos << "Failed to mount disk image, exiting."<< llendl;
  844. throw 0;
  845. }
  846. // We need to scan the output from hdiutil to find the device node it uses to attach the disk image.
  847. // If we don't have this information, we can't detach it later.
  848. while(mounter != NULL)
  849. {
  850. size_t len = fread(temp, 1, sizeof(temp)-1, mounter);
  851. temp[len] = 0;
  852. mountOutput.append(temp);
  853. if(len < sizeof(temp)-1)
  854. {
  855. // End of file or error.
  856. int result = pclose(mounter);
  857. if(result != 0)
  858. {
  859. // NOTE: We used to abort here, but pclose() started returning 
  860. // -1, possibly when the size of the DMG passed a certain point 
  861. llinfos << "Unexpected result closing pipe: " << result << llendl; 
  862. }
  863. mounter = NULL;
  864. }
  865. }
  866. if(!mountOutput.empty())
  867. {
  868. const char *s = mountOutput.c_str();
  869. const char *prefix = "/dev/";
  870. char *sub = strstr(s, prefix);
  871. if(sub != NULL)
  872. {
  873. sub += strlen(prefix); /* Flawfinder: ignore */
  874. sscanf(sub, "%1023s", deviceNode); /* Flawfinder: ignore */
  875. }
  876. }
  877. if(deviceNode[0] != 0)
  878. {
  879. llinfos << "Disk image attached on /dev/" << deviceNode << llendl;
  880. }
  881. else
  882. {
  883. llinfos << "Disk image device node not found!" << llendl;
  884. throw 0; 
  885. }
  886. // Get an FSRef to the new application on the disk image
  887. FSRef sourceRef;
  888. FSRef mountRef;
  889. snprintf(temp, sizeof(temp), "%s/mnt", tempDir);
  890. llinfos << "Disk image mount point is: " << temp << llendl;
  891. err = FSPathMakeRef((UInt8 *)temp, &mountRef, NULL);
  892. if(err != noErr)
  893. {
  894. llinfos << "Couldn't make FSRef to disk image mount point." << llendl;
  895. throw 0;
  896. }
  897. err = findAppBundleOnDiskImage(&mountRef, &sourceRef);
  898. if(err != noErr)
  899. {
  900. llinfos << "Couldn't find application bundle on mounted disk image." << llendl;
  901. throw 0;
  902. }
  903. FSRef asideRef;
  904. char aside[MAX_PATH]; /* Flawfinder: ignore */
  905. // this will hold the name of the destination target
  906. HFSUniStr255 appNameUniStr;
  907. if(replacingTarget)
  908. {
  909. // Get the name of the target we're replacing
  910. err = FSGetCatalogInfo(&targetRef, 0, NULL, &appNameUniStr, NULL, NULL);
  911. if(err != noErr)
  912. throw 0;
  913. // Move aside old version (into work directory)
  914. err = FSMoveObject(&targetRef, &tempDirRef, &asideRef);
  915. if(err != noErr)
  916. throw 0;
  917. // Grab the path for later use.
  918. err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside));
  919. }
  920. else
  921. {
  922. // Construct the name of the target based on the product name
  923. char appName[MAX_PATH]; /* Flawfinder: ignore */
  924. snprintf(appName, sizeof(appName), "%s.app", gProductName);
  925. utf8str_to_HFSUniStr255( &appNameUniStr, appName );
  926. }
  927. sendProgress(0, 0, CFSTR("Copying files..."));
  928. llinfos << "Starting copy..." << llendl;
  929. // Copy the new version from the disk image to the target location.
  930. err = FSCopyObject(
  931. &sourceRef,
  932. &targetParentRef,
  933. 0,
  934. kFSCatInfoNone,
  935. kDupeActionStandard,
  936. &appNameUniStr,
  937. false,
  938. false,
  939. NULL,
  940. NULL,
  941. &targetRef,
  942. NULL);
  943. // Grab the path for later use.
  944. err = FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target));
  945. if(err != noErr)
  946. throw 0;
  947. llinfos << "Copy complete. Target = " << target << llendl;
  948. if(err != noErr)
  949. {
  950. // Something went wrong during the copy.  Attempt to put the old version back and bail.
  951. (void)FSDeleteObjects(&targetRef);
  952. if(replacingTarget)
  953. {
  954. (void)FSMoveObject(&asideRef, &targetParentRef, NULL);
  955. }
  956. throw 0;
  957. }
  958. else
  959. {
  960. // The update has succeeded.  Clear the cache directory.
  961. sendProgress(0, 0, CFSTR("Clearing cache..."));
  962. llinfos << "Clearing cache..." << llendl;
  963. char mask[LL_MAX_PATH]; /* Flawfinder: ignore */
  964. snprintf(mask, LL_MAX_PATH, "%s*.*", gDirUtilp->getDirDelimiter().c_str());
  965. gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask);
  966. llinfos << "Clear complete." << llendl;
  967. }
  968. }
  969. catch(...)
  970. {
  971. if(!gCancelled)
  972. if(gFailure == noErr)
  973. gFailure = -1;
  974. }
  975. // Failures from here on out are all non-fatal and not reported.
  976. sendProgress(0, 3, CFSTR("Cleaning up..."));
  977. // Close disk image file if necessary
  978. if(downloadFile != NULL)
  979. {
  980. llinfos << "Closing download file." << llendl;
  981. fclose(downloadFile);
  982. downloadFile = NULL;
  983. }
  984. sendProgress(1, 3);
  985. // Unmount image
  986. if(deviceNode[0] != 0)
  987. {
  988. llinfos << "Detaching disk image." << llendl;
  989. snprintf(temp, sizeof(temp), "hdiutil detach '%s'", deviceNode);
  990. system(temp); /* Flawfinder: ignore */
  991. }
  992. sendProgress(2, 3);
  993. // Move work directory to the trash
  994. if(tempDir[0] != 0)
  995. {
  996. // chdir("/");
  997. // FSDeleteObjects(tempDirRef);
  998. llinfos << "Moving work directory to the trash." << llendl;
  999. err = FSMoveObject(&tempDirRef, &trashFolderRef, NULL);
  1000. // snprintf(temp, sizeof(temp), "rm -rf '%s'", tempDir);
  1001. // printf("%sn", temp);
  1002. // system(temp);
  1003. }
  1004. if(!gCancelled  && !gFailure && (target[0] != 0))
  1005. {
  1006. llinfos << "Touching application bundle." << llendl;
  1007. snprintf(temp, sizeof(temp), "touch '%s'", target);
  1008. system(temp); /* Flawfinder: ignore */
  1009. llinfos << "Launching updated application." << llendl;
  1010. snprintf(temp, sizeof(temp), "open '%s'", target);
  1011. system(temp); /* Flawfinder: ignore */
  1012. }
  1013. sendDone();
  1014. return(NULL);
  1015. }