diff options
author | Aura Linden <aura@lindenlab.com> | 2012-11-12 16:21:07 -0800 |
---|---|---|
committer | Aura Linden <aura@lindenlab.com> | 2012-11-12 16:21:07 -0800 |
commit | a03b654797f80ebc4b3660930acbed55a7fc2ce2 (patch) | |
tree | 7f6b47794bc2230f5214e1be265c18c9aaeff21e /indra | |
parent | f8749ec9bd03072cc5b50f4cb1a61289b2989c72 (diff) |
Mac updater changes ported from another branch.
Diffstat (limited to 'indra')
-rw-r--r-- | indra/mac_updater/AutoUpdater.xib | 520 | ||||
-rw-r--r-- | indra/mac_updater/CMakeLists.txt | 23 | ||||
-rw-r--r-- | indra/mac_updater/MacUpdater-Info.plist | 30 | ||||
-rw-r--r-- | indra/mac_updater/MacUpdaterAppDelegate.h | 58 | ||||
-rw-r--r-- | indra/mac_updater/MacUpdaterAppDelegate.mm | 274 | ||||
-rw-r--r-- | indra/mac_updater/mac_updater.cpp | 1578 | ||||
-rw-r--r-- | indra/mac_updater/mac_updater.h | 89 | ||||
-rw-r--r-- | indra/mac_updater/main.m | 34 |
8 files changed, 1513 insertions, 1093 deletions
diff --git a/indra/mac_updater/AutoUpdater.xib b/indra/mac_updater/AutoUpdater.xib new file mode 100644 index 0000000000..b29fffba3a --- /dev/null +++ b/indra/mac_updater/AutoUpdater.xib @@ -0,0 +1,520 @@ +<?xml version="1.0" encoding="UTF-8"?> +<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00"> + <data> + <int key="IBDocument.SystemTarget">1070</int> + <string key="IBDocument.SystemVersion">11G63</string> + <string key="IBDocument.InterfaceBuilderVersion">2182</string> + <string key="IBDocument.AppKitVersion">1138.51</string> + <string key="IBDocument.HIToolboxVersion">569.00</string> + <object class="NSMutableDictionary" key="IBDocument.PluginVersions"> + <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="NS.object.0">2182</string> + </object> + <array key="IBDocument.IntegratedClassDependencies"> + <string>NSTextField</string> + <string>NSView</string> + <string>NSWindowTemplate</string> + <string>NSProgressIndicator</string> + <string>NSCustomObject</string> + <string>IBNSLayoutConstraint</string> + <string>NSButtonCell</string> + <string>NSButton</string> + <string>NSUserDefaultsController</string> + <string>NSTextFieldCell</string> + </array> + <array key="IBDocument.PluginDependencies"> + <string>com.apple.InterfaceBuilder.CocoaPlugin</string> + </array> + <object class="NSMutableDictionary" key="IBDocument.Metadata"> + <string key="NS.key.0">PluginDependencyRecalculationVersion</string> + <integer value="1" key="NS.object.0"/> + </object> + <array class="NSMutableArray" key="IBDocument.RootObjects" id="1000"> + <object class="NSCustomObject" id="1001"> + <string key="NSClassName">NSObject</string> + </object> + <object class="NSCustomObject" id="1003"> + <string key="NSClassName">FirstResponder</string> + </object> + <object class="NSCustomObject" id="1004"> + <string key="NSClassName">NSApplication</string> + </object> + <object class="NSWindowTemplate" id="1005"> + <int key="NSWindowStyleMask">15</int> + <int key="NSWindowBacking">2</int> + <string key="NSWindowRect">{{196, 240}, {402, 120}}</string> + <int key="NSWTFlags">544735232</int> + <string key="NSWindowTitle">Window</string> + <string key="NSWindowClass">NSWindow</string> + <nil key="NSViewClass"/> + <nil key="NSUserInterfaceItemIdentifier"/> + <object class="NSView" key="NSWindowView" id="1006"> + <reference key="NSNextResponder"/> + <int key="NSvFlags">256</int> + <array class="NSMutableArray" key="NSSubviews"> + <object class="NSTextField" id="269124353"> + <reference key="NSNextResponder" ref="1006"/> + <int key="NSvFlags">268</int> + <string key="NSFrame">{{17, 83}, {79, 17}}</string> + <reference key="NSSuperview" ref="1006"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="730867742"/> + <string key="NSReuseIdentifierKey">_NS:1505</string> + <bool key="NSEnabled">YES</bool> + <object class="NSTextFieldCell" key="NSCell" id="702170046"> + <int key="NSCellFlags">68288064</int> + <int key="NSCellFlags2">272630784</int> + <string key="NSContents">Initalizing...</string> + <object class="NSFont" key="NSSupport"> + <string key="NSName">LucidaGrande</string> + <double key="NSSize">13</double> + <int key="NSfFlags">1044</int> + </object> + <string key="NSCellIdentifier">_NS:1505</string> + <reference key="NSControlView" ref="269124353"/> + <object class="NSColor" key="NSBackgroundColor"> + <int key="NSColorSpace">6</int> + <string key="NSCatalogName">System</string> + <string key="NSColorName">controlColor</string> + <object class="NSColor" key="NSColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes> + </object> + </object> + <object class="NSColor" key="NSTextColor"> + <int key="NSColorSpace">6</int> + <string key="NSCatalogName">System</string> + <string key="NSColorName">controlTextColor</string> + <object class="NSColor" key="NSColor"> + <int key="NSColorSpace">3</int> + <bytes key="NSWhite">MAA</bytes> + </object> + </object> + </object> + </object> + <object class="NSProgressIndicator" id="730867742"> + <reference key="NSNextResponder" ref="1006"/> + <int key="NSvFlags">268</int> + <string key="NSFrame">{{18, 55}, {366, 20}}</string> + <reference key="NSSuperview" ref="1006"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="302149677"/> + <string key="NSReuseIdentifierKey">_NS:9</string> + <string key="NSHuggingPriority">{250, 250}</string> + <int key="NSpiFlags">16399</int> + <double key="NSMaxValue">100</double> + </object> + <object class="NSButton" id="302149677"> + <reference key="NSNextResponder" ref="1006"/> + <int key="NSvFlags">268</int> + <string key="NSFrame">{{308, 19}, {74, 19}}</string> + <reference key="NSSuperview" ref="1006"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView"/> + <string key="NSReuseIdentifierKey">_NS:9</string> + <bool key="NSEnabled">YES</bool> + <object class="NSButtonCell" key="NSCell" id="677565961"> + <int key="NSCellFlags">-2080244224</int> + <int key="NSCellFlags2">134217728</int> + <string key="NSContents">Cancel</string> + <object class="NSFont" key="NSSupport"> + <string key="NSName">LucidaGrande</string> + <double key="NSSize">12</double> + <int key="NSfFlags">16</int> + </object> + <string key="NSCellIdentifier">_NS:9</string> + <reference key="NSControlView" ref="302149677"/> + <int key="NSButtonFlags">-2038152961</int> + <int key="NSButtonFlags2">164</int> + <string key="NSAlternateContents"/> + <string key="NSKeyEquivalent"/> + <int key="NSPeriodicDelay">400</int> + <int key="NSPeriodicInterval">75</int> + </object> + </object> + </array> + <string key="NSFrameSize">{402, 120}</string> + <reference key="NSSuperview"/> + <reference key="NSWindow"/> + <reference key="NSNextKeyView" ref="269124353"/> + </object> + <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string> + <string key="NSMaxSize">{10000000000000, 10000000000000}</string> + <bool key="NSWindowIsRestorable">YES</bool> + </object> + <object class="NSCustomObject" id="492080840"> + <string key="NSClassName">MacUpdaterAppDelegate</string> + </object> + <object class="NSUserDefaultsController" id="21008314"> + <bool key="NSSharedInstance">YES</bool> + </object> + </array> + <object class="IBObjectContainer" key="IBDocument.Objects"> + <array class="NSMutableArray" key="connectionRecords"> + <object class="IBConnectionRecord"> + <object class="IBBindingConnection" key="connection"> + <string key="label">title: values</string> + <reference key="source" ref="1005"/> + <reference key="destination" ref="21008314"/> + <object class="NSNibBindingConnector" key="connector"> + <reference key="NSSource" ref="1005"/> + <reference key="NSDestination" ref="21008314"/> + <string key="NSLabel">title: values</string> + <string key="NSBinding">title</string> + <string key="NSKeyPath">values</string> + <int key="NSNibBindingConnectorVersion">2</int> + </object> + </object> + <int key="connectionID">41</int> + </object> + </array> + <object class="IBMutableOrderedSet" key="objectRecords"> + <array key="orderedObjects"> + <object class="IBObjectRecord"> + <int key="objectID">0</int> + <array key="object" id="0"/> + <reference key="children" ref="1000"/> + <nil key="parent"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-2</int> + <reference key="object" ref="1001"/> + <reference key="parent" ref="0"/> + <string key="objectName">File's Owner</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-1</int> + <reference key="object" ref="1003"/> + <reference key="parent" ref="0"/> + <string key="objectName">First Responder</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">-3</int> + <reference key="object" ref="1004"/> + <reference key="parent" ref="0"/> + <string key="objectName">Application</string> + </object> + <object class="IBObjectRecord"> + <int key="objectID">1</int> + <reference key="object" ref="1005"/> + <array class="NSMutableArray" key="children"> + <reference ref="1006"/> + </array> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">2</int> + <reference key="object" ref="1006"/> + <array class="NSMutableArray" key="children"> + <reference ref="269124353"/> + <reference ref="730867742"/> + <object class="IBNSLayoutConstraint" id="463541650"> + <reference key="firstItem" ref="269124353"/> + <int key="firstAttribute">5</int> + <int key="relation">0</int> + <reference key="secondItem" ref="1006"/> + <int key="secondAttribute">5</int> + <float key="multiplier">1</float> + <object class="IBNSLayoutSymbolicConstant" key="constant"> + <double key="value">20</double> + </object> + <float key="priority">1000</float> + <int key="scoringType">8</int> + <float key="scoringTypeFloat">29</float> + <int key="contentType">3</int> + <reference key="containingView" ref="1006"/> + </object> + <object class="IBNSLayoutConstraint" id="772497817"> + <reference key="firstItem" ref="730867742"/> + <int key="firstAttribute">5</int> + <int key="relation">0</int> + <reference key="secondItem" ref="1006"/> + <int key="secondAttribute">5</int> + <float key="multiplier">1</float> + <object class="IBNSLayoutSymbolicConstant" key="constant"> + <double key="value">20</double> + </object> + <float key="priority">1000</float> + <int key="scoringType">8</int> + <float key="scoringTypeFloat">29</float> + <int key="contentType">3</int> + <reference key="containingView" ref="1006"/> + </object> + <object class="IBNSLayoutConstraint" id="929212820"> + <reference key="firstItem" ref="1006"/> + <int key="firstAttribute">6</int> + <int key="relation">0</int> + <reference key="secondItem" ref="730867742"/> + <int key="secondAttribute">6</int> + <float key="multiplier">1</float> + <object class="IBNSLayoutSymbolicConstant" key="constant"> + <double key="value">20</double> + </object> + <float key="priority">1000</float> + <int key="scoringType">8</int> + <float key="scoringTypeFloat">29</float> + <int key="contentType">3</int> + <reference key="containingView" ref="1006"/> + </object> + <reference ref="302149677"/> + <object class="IBNSLayoutConstraint" id="813415053"> + <reference key="firstItem" ref="1006"/> + <int key="firstAttribute">6</int> + <int key="relation">0</int> + <reference key="secondItem" ref="302149677"/> + <int key="secondAttribute">6</int> + <float key="multiplier">1</float> + <object class="IBNSLayoutSymbolicConstant" key="constant"> + <double key="value">20</double> + </object> + <float key="priority">1000</float> + <int key="scoringType">8</int> + <float key="scoringTypeFloat">29</float> + <int key="contentType">3</int> + <reference key="containingView" ref="1006"/> + </object> + <object class="IBNSLayoutConstraint" id="178579609"> + <reference key="firstItem" ref="269124353"/> + <int key="firstAttribute">3</int> + <int key="relation">0</int> + <reference key="secondItem" ref="1006"/> + <int key="secondAttribute">3</int> + <float key="multiplier">1</float> + <object class="IBNSLayoutSymbolicConstant" key="constant"> + <double key="value">20</double> + </object> + <float key="priority">1000</float> + <int key="scoringType">8</int> + <float key="scoringTypeFloat">29</float> + <int key="contentType">3</int> + <reference key="containingView" ref="1006"/> + </object> + <object class="IBNSLayoutConstraint" id="594621082"> + <reference key="firstItem" ref="730867742"/> + <int key="firstAttribute">3</int> + <int key="relation">0</int> + <reference key="secondItem" ref="269124353"/> + <int key="secondAttribute">4</int> + <float key="multiplier">1</float> + <object class="IBNSLayoutSymbolicConstant" key="constant"> + <double key="value">8</double> + </object> + <float key="priority">1000</float> + <int key="scoringType">6</int> + <float key="scoringTypeFloat">24</float> + <int key="contentType">3</int> + <reference key="containingView" ref="1006"/> + </object> + <object class="IBNSLayoutConstraint" id="658067790"> + <reference key="firstItem" ref="1006"/> + <int key="firstAttribute">4</int> + <int key="relation">0</int> + <reference key="secondItem" ref="302149677"/> + <int key="secondAttribute">4</int> + <float key="multiplier">1</float> + <object class="IBNSLayoutSymbolicConstant" key="constant"> + <double key="value">20</double> + </object> + <float key="priority">1000</float> + <int key="scoringType">8</int> + <float key="scoringTypeFloat">29</float> + <int key="contentType">3</int> + <reference key="containingView" ref="1006"/> + </object> + </array> + <reference key="parent" ref="1005"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">3</int> + <reference key="object" ref="269124353"/> + <array class="NSMutableArray" key="children"> + <reference ref="702170046"/> + </array> + <reference key="parent" ref="1006"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">4</int> + <reference key="object" ref="702170046"/> + <reference key="parent" ref="269124353"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">8</int> + <reference key="object" ref="730867742"/> + <array class="NSMutableArray" key="children"/> + <reference key="parent" ref="1006"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">20</int> + <reference key="object" ref="463541650"/> + <reference key="parent" ref="1006"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">22</int> + <reference key="object" ref="302149677"/> + <array class="NSMutableArray" key="children"> + <reference ref="677565961"/> + <object class="IBNSLayoutConstraint" id="981064020"> + <reference key="firstItem" ref="302149677"/> + <int key="firstAttribute">7</int> + <int key="relation">0</int> + <nil key="secondItem"/> + <int key="secondAttribute">0</int> + <float key="multiplier">1</float> + <object class="IBLayoutConstant" key="constant"> + <double key="value">74</double> + </object> + <float key="priority">1000</float> + <int key="scoringType">3</int> + <float key="scoringTypeFloat">9</float> + <int key="contentType">1</int> + <reference key="containingView" ref="302149677"/> + </object> + </array> + <reference key="parent" ref="1006"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">23</int> + <reference key="object" ref="677565961"/> + <reference key="parent" ref="302149677"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">31</int> + <reference key="object" ref="772497817"/> + <reference key="parent" ref="1006"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">32</int> + <reference key="object" ref="929212820"/> + <reference key="parent" ref="1006"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">35</int> + <reference key="object" ref="813415053"/> + <reference key="parent" ref="1006"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">36</int> + <reference key="object" ref="981064020"/> + <reference key="parent" ref="302149677"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">37</int> + <reference key="object" ref="178579609"/> + <reference key="parent" ref="1006"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">38</int> + <reference key="object" ref="594621082"/> + <reference key="parent" ref="1006"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">39</int> + <reference key="object" ref="658067790"/> + <reference key="parent" ref="1006"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">40</int> + <reference key="object" ref="21008314"/> + <reference key="parent" ref="0"/> + </object> + <object class="IBObjectRecord"> + <int key="objectID">42</int> + <reference key="object" ref="492080840"/> + <reference key="parent" ref="0"/> + </object> + </array> + </object> + <dictionary class="NSMutableDictionary" key="flattenedProperties"> + <string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="1.IBWindowTemplateEditedContentRect">{{357, 418}, {480, 270}}</string> + <integer value="1" key="1.NSWindowTemplate.visibleAtLaunch"/> + <array class="NSMutableArray" key="2.IBNSViewMetadataConstraints"> + <reference ref="463541650"/> + <reference ref="772497817"/> + <reference ref="929212820"/> + <reference ref="813415053"/> + <reference ref="178579609"/> + <reference ref="594621082"/> + <reference ref="658067790"/> + </array> + <string key="2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="20.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <array class="NSMutableArray" key="22.IBNSViewMetadataConstraints"> + <reference ref="981064020"/> + </array> + <boolean value="NO" key="22.IBNSViewMetadataTranslatesAutoresizingMaskIntoConstraints"/> + <string key="22.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="23.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <boolean value="NO" key="3.IBNSViewMetadataTranslatesAutoresizingMaskIntoConstraints"/> + <string key="3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="31.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="32.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="35.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="36.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="37.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="38.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="39.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="4.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="40.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <string key="42.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + <boolean value="NO" key="8.IBNSViewMetadataTranslatesAutoresizingMaskIntoConstraints"/> + <string key="8.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string> + </dictionary> + <dictionary class="NSMutableDictionary" key="unlocalizedProperties"/> + <nil key="activeLocalization"/> + <dictionary class="NSMutableDictionary" key="localizations"/> + <nil key="sourceID"/> + <int key="maxID">42</int> + </object> + <object class="IBClassDescriber" key="IBDocument.Classes"> + <array class="NSMutableArray" key="referencedPartialClassDescriptions"> + <object class="IBPartialClassDescription"> + <string key="className">MacUpdaterAppDelegate</string> + <string key="superclassName">NSObject</string> + <object class="NSMutableDictionary" key="actions"> + <string key="NS.key.0">cancel:</string> + <string key="NS.object.0">id</string> + </object> + <object class="NSMutableDictionary" key="actionInfosByName"> + <string key="NS.key.0">cancel:</string> + <object class="IBActionInfo" key="NS.object.0"> + <string key="name">cancel:</string> + <string key="candidateClassName">id</string> + </object> + </object> + <dictionary class="NSMutableDictionary" key="outlets"> + <string key="mProgressBar">NSProgressIndicator</string> + <string key="mProgressText">NSTextField</string> + <string key="window">NSWindow</string> + </dictionary> + <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName"> + <object class="IBToOneOutletInfo" key="mProgressBar"> + <string key="name">mProgressBar</string> + <string key="candidateClassName">NSProgressIndicator</string> + </object> + <object class="IBToOneOutletInfo" key="mProgressText"> + <string key="name">mProgressText</string> + <string key="candidateClassName">NSTextField</string> + </object> + <object class="IBToOneOutletInfo" key="window"> + <string key="name">window</string> + <string key="candidateClassName">NSWindow</string> + </object> + </dictionary> + <object class="IBClassDescriptionSource" key="sourceIdentifier"> + <string key="majorKey">IBProjectSource</string> + <string key="minorKey">./Classes/MacUpdaterAppDelegate.h</string> + </object> + </object> + </array> + </object> + <int key="IBDocument.localizationMode">0</int> + <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string> + <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> + <int key="IBDocument.defaultPropertyAccessControl">3</int> + <bool key="IBDocument.UseAutolayout">YES</bool> + </data> +</archive> diff --git a/indra/mac_updater/CMakeLists.txt b/indra/mac_updater/CMakeLists.txt index 00dcedecaa..aa70e64b71 100644 --- a/indra/mac_updater/CMakeLists.txt +++ b/indra/mac_updater/CMakeLists.txt @@ -18,10 +18,14 @@ include_directories( ) set(mac_updater_SOURCE_FILES - mac_updater.cpp + main.m + MacUpdaterAppDelegate.mm + mac_updater.cpp ) set(mac_updater_HEADER_FILES + MacUpdaterAppDelegate.h + mac_updater.h CMakeLists.txt ) @@ -32,7 +36,7 @@ list(APPEND mac_updater_SOURCE_FILES ${mac_updater_HEADER_FILES}) set(mac_updater_RESOURCE_FILES - AutoUpdater.nib/ + AutoUpdater.nib ) set_source_files_properties( ${mac_updater_RESOURCE_FILES} @@ -48,13 +52,18 @@ add_executable(mac-updater set_target_properties(mac-updater PROPERTIES - MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/MacUpdater-Info.plist ) +find_library(COCOA_LIBRARY Cocoa) +find_library(IOKIT_LIBRARY IOKit) + target_link_libraries(mac-updater ${LLVFS_LIBRARIES} ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARIES} + ${COCOA_LIBRARIES} + ${IOKIT_LIBRARY} ${CURL_LIBRARIES} ${CARES_LIBRARIES} ${LLCOMMON_LIBRARIES} @@ -62,10 +71,16 @@ target_link_libraries(mac-updater add_custom_command( TARGET mac-updater POST_BUILD +# COMMAND ${CMAKE_COMMAND} +# ARGS +# -E +# copy_directory +# ${CMAKE_CURRENT_SOURCE_DIR}/AutoUpdater.nib +# ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/mac-updater.app/Contents/Resources/AutoUpdater.nib COMMAND ${CMAKE_COMMAND} ARGS -E - copy_directory + copy ${CMAKE_CURRENT_SOURCE_DIR}/AutoUpdater.nib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/mac-updater.app/Contents/Resources/AutoUpdater.nib ) diff --git a/indra/mac_updater/MacUpdater-Info.plist b/indra/mac_updater/MacUpdater-Info.plist new file mode 100644 index 0000000000..92137095ff --- /dev/null +++ b/indra/mac_updater/MacUpdater-Info.plist @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>mac-updater</string> + <key>CFBundleGetInfoString</key> + <string></string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>com.secondlife.indra.autoupdater</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string></string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0.0</string> + <key>NSMainNibFile</key> + <string>AutoUpdater</string> + <key>NSPrincipalClass</key> + <string>NSApplication</string> +</dict> +</plist> diff --git a/indra/mac_updater/MacUpdaterAppDelegate.h b/indra/mac_updater/MacUpdaterAppDelegate.h new file mode 100644 index 0000000000..ba5744b398 --- /dev/null +++ b/indra/mac_updater/MacUpdaterAppDelegate.h @@ -0,0 +1,58 @@ +/** + * @file MacUpdaterAppDelegate.h + * @brief + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#import <Cocoa/Cocoa.h> +#include <iostream> +#include "mac_updater.h" + +#ifndef LL_MAC_UPDATE_DELEGATE_H +#define LL_MAC_UPDATE_DELEGATE_H + +@interface MacUpdaterAppDelegate : NSObject <NSApplicationDelegate> +{ + IBOutlet NSProgressIndicator *mProgressBar; + IBOutlet NSTextField *mProgressText; +} +- (void)setWindow:(NSWindow *)newWindow; +- (NSWindow *)window; +- (IBAction)cancel:(id)sender; +- (void) setProgress:(int)cur max:(int) max; +- (void) setProgressText:(const std::string&)str; +- (int) parse_args:(NSArray *) args; +- (void)stopAlert; +- (void)stopAlertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo; + + +NSWindow *_window; +bool mAnimated; +double mProgressPercentage; +@property (assign) IBOutlet NSWindow *window; +LLMacUpdater mUpdater; + +@end + +#endif //LL_MAC_UPDATE_DELEGATE_H
\ No newline at end of file diff --git a/indra/mac_updater/MacUpdaterAppDelegate.mm b/indra/mac_updater/MacUpdaterAppDelegate.mm new file mode 100644 index 0000000000..80206cb508 --- /dev/null +++ b/indra/mac_updater/MacUpdaterAppDelegate.mm @@ -0,0 +1,274 @@ +/** + * @file MacUpdaterAppDelegate.mm + * @brief + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#import "MacUpdaterAppDelegate.h" +#include "llvfs_objc.h" +#include <string.h> +#include <boost/filesystem.hpp> + +@implementation MacUpdaterAppDelegate + +MacUpdaterAppDelegate *gWindow; +bool gCancelled = false; +bool gFailure =false; + + +//@synthesize window = _window; +- (void)setWindow:(NSWindow *)window +{ + _window = window; +} + +- (NSWindow *)window +{ + return _window; +} + +- (id)init +{ + self = [super init]; + if (self) { + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + mAnimated = false; + mProgressPercentage = 0.0; + NSArray *arguments = [[NSProcessInfo processInfo] arguments]; + + [self parse_args:arguments]; + gWindow = self; + + mUpdater.doUpdate(); + [pool drain]; + [pool release]; + } + return self; +} + +- (void)dealloc +{ + [super dealloc]; +} + +std::string* NSToString( NSString *ns_str ) +{ + return ( new std::string([ns_str UTF8String]) ); +} + + +- (void) setProgress:(int)cur max:(int) max +{ + bool indeterminate = false; + if (max==0) + { + indeterminate = true; + } + else + { + double percentage = ((double)cur / (double)max) * 100.0; + [mProgressBar setDoubleValue:percentage]; + } + [mProgressBar setIndeterminate:indeterminate]; +} + +- (void) setProgressText:(const std::string& )str +{ + [mProgressText setStringValue:[NSString stringWithUTF8String:str.c_str()]]; +} + +void sendDone() +{ + [ [ (id) gWindow window ] close]; +} + +void sendStopAlert() +{ + [ gWindow stopAlert ]; +} + +void setProgress(int cur, int max) +{ + [ (id) gWindow setProgress:cur max:max]; +} + +void setProgressText(const std::string& str) +{ + [ (id) gWindow setProgressText:str]; +} + +void sendProgress(int cur, int max, const std::string str) +{ + setProgress(cur,max); + setProgressText(str); +} + +bool mkTempDir(boost::filesystem::path& temp_dir) +{ + NSString * tempDir = NSTemporaryDirectory(); + if (tempDir == nil) + tempDir = @"/tmp/"; + + std::string* temp_str = NSToString(tempDir); + *temp_str += std::string("SecondLifeUpdate_XXXXXX"); + + std::cout << "tempDir is " << temp_str << std::endl; + + char temp[PATH_MAX] = ""; /* Flawfinder: ignore */ + strncpy(temp, temp_str->c_str(), temp_str->length()); + + if(mkdtemp(temp) == NULL) + { + return false; + } + + temp_dir = boost::filesystem::path(temp); + + delete temp_str; + + return true; +} + +bool copyDir(const std::string& src_dir, const std::string& dest_dir) +{ + NSString* file = [NSString stringWithCString:src_dir.c_str() + encoding:[NSString defaultCStringEncoding]]; + NSString* toParent = [NSString stringWithCString:dest_dir.c_str() + encoding:[NSString defaultCStringEncoding]]; + NSError* error = nil; + + bool result = [[NSFileManager defaultManager] copyItemAtPath: file toPath: toParent error:&error]; + + if (!result) { + NSLog(@"Error during copy: %@", [error localizedDescription]); + } + return result; +} + +- (int) parse_args:(NSArray *) args +{ + int i; + int argc = [args count]; + + mUpdater.mApplicationPath = NSToString( [args objectAtIndex:0] ); + + for( i = 1; i < argc; i++ ) + { + NSString* ns_arg = [args objectAtIndex:i]; + const char *arg = [ns_arg UTF8String]; + + if ((!strcmp(arg, "-url")) && (i < argc)) + { + mUpdater.mUpdateURL = NSToString( [args objectAtIndex:(++i)] ); + } + else if ((!strcmp(arg, "-name")) && (i < argc)) + { + mUpdater.mProductName = NSToString( [args objectAtIndex:(++i)] ); + } + else if ((!strcmp(arg, "-bundleid")) && (i < argc)) + { + mUpdater.mBundleID = NSToString( [args objectAtIndex:(++i)] ); + } + else if ((!strcmp(arg, "-dmg")) && (i < argc)) + { + mUpdater.mDmgFile = NSToString( [args objectAtIndex:(++i)] ); + } + else if ((!strcmp(arg, "-marker")) && (i < argc)) + { + mUpdater.mMarkerPath = NSToString( [args objectAtIndex:(++i)] ); + } + } + return 0; +} + +bool isDirWritable(const std::string& dir_name) +{ + + NSString *fullPath = [NSString stringWithCString:dir_name.c_str() + encoding:[NSString defaultCStringEncoding]]; + + NSFileManager *fm = [NSFileManager defaultManager]; + bool result = [fm isWritableFileAtPath:fullPath]; + + return result; +} + +std::string* getUserTrashFolder() +{ + NSString *trash_str=[NSHomeDirectory() stringByAppendingPathComponent:@".Trash"]; + return NSToString( trash_str ); + +} + +bool isFSRefViewerBundle(const std::string& targetURL) +{ + bool result = false; + + NSString *fullPath = [NSString stringWithCString:targetURL.c_str() + encoding:[NSString defaultCStringEncoding]]; + NSBundle *targetBundle = [NSBundle bundleWithPath:fullPath]; + NSString *targetBundleStr = [targetBundle bundleIdentifier]; + NSString *sourceBundleStr = [NSString stringWithCString:mUpdater.mBundleID->c_str() + encoding:[NSString defaultCStringEncoding]]; + + result = [targetBundleStr isEqualToString:sourceBundleStr]; + + if(!result) + { + std::cout << "Target bundle ID mismatch." << std::endl; + } + + return result; +} + + +- (IBAction)cancel:(id)sender +{ + gCancelled = true; + sendDone(); +} + +- (void)stopAlert +{ + NSAlert *alert = [[NSAlert alloc] init]; + [alert setAlertStyle:NSInformationalAlertStyle]; + [alert setMessageText:@"Error"]; + [alert setInformativeText:@"An error occurred while updating Second Life. Please download the latest version from www.secondlife.com."]; + + [alert beginSheetModalForWindow:_window + modalDelegate:self + + didEndSelector:@selector(stopAlertDidEnd:returnCode: + contextInfo:) + contextInfo:nil]; + } + + - (void)stopAlertDidEnd:(NSAlert *)alert + returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + [alert release]; +} + + +@end diff --git a/indra/mac_updater/mac_updater.cpp b/indra/mac_updater/mac_updater.cpp index aa45c5d23f..379e69e033 100644 --- a/indra/mac_updater/mac_updater.cpp +++ b/indra/mac_updater/mac_updater.cpp @@ -27,6 +27,8 @@ #include "linden_common.h" #include <boost/format.hpp> +#include <boost/filesystem/operations.hpp> +#include <boost/filesystem.hpp> #include <libgen.h> #include <sys/types.h> @@ -34,7 +36,6 @@ #include <unistd.h> #include <curl/curl.h> -#include <pthread.h> #include "llerror.h" #include "lltimer.h" @@ -43,1177 +44,550 @@ #include "llstring.h" -#include <Carbon/Carbon.h> - #include "llerrorcontrol.h" - -enum -{ - kEventClassCustom = 'Cust', - kEventCustomProgress = 'Prog', - kEventParamCustomCurValue = 'Cur ', - kEventParamCustomMaxValue = 'Max ', - kEventParamCustomText = 'Text', - kEventCustomDone = 'Done', -}; - -WindowRef gWindow = NULL; -EventHandlerRef gEventHandler = NULL; -OSStatus gFailure = noErr; -Boolean gCancelled = false; - -const char *gUpdateURL; -const char *gProductName; -const char *gBundleID; -const char *gDmgFile; -const char *gMarkerPath; - -void *updatethreadproc(void*); +#include "mac_updater.h" +#include <sstream> pthread_t updatethread; -OSStatus setProgress(int cur, int max) -{ - OSStatus err; - ControlRef progressBar = NULL; - ControlID id; - - id.signature = 'prog'; - id.id = 0; - - err = GetControlByID(gWindow, &id, &progressBar); - if(err == noErr) - { - Boolean indeterminate; - - if(max == 0) - { - indeterminate = true; - err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate); - } - else - { - double percentage = (double)cur / (double)max; - SetControlMinimum(progressBar, 0); - SetControlMaximum(progressBar, 100); - SetControlValue(progressBar, (SInt16)(percentage * 100)); - - indeterminate = false; - err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate); - - Draw1Control(progressBar); - } - } - - return(err); -} - -OSStatus setProgressText(CFStringRef text) -{ - OSStatus err; - ControlRef progressText = NULL; - ControlID id; - - id.signature = 'what'; - id.id = 0; - - err = GetControlByID(gWindow, &id, &progressText); - if(err == noErr) - { - err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&text); - Draw1Control(progressText); - } - - return(err); -} - -OSStatus sendProgress(long cur, long max, CFStringRef text = NULL) -{ - OSStatus result; - EventRef evt; - - result = CreateEvent( - NULL, - kEventClassCustom, - kEventCustomProgress, - 0, - kEventAttributeNone, - &evt); - - // This event needs to be targeted at the window so it goes to the window's handler. - if(result == noErr) - { - EventTargetRef target = GetWindowEventTarget(gWindow); - result = SetEventParameter ( - evt, - kEventParamPostTarget, - typeEventTargetRef, - sizeof(target), - &target); - } - - if(result == noErr) - { - result = SetEventParameter ( - evt, - kEventParamCustomCurValue, - typeLongInteger, - sizeof(cur), - &cur); - } - - if(result == noErr) - { - result = SetEventParameter ( - evt, - kEventParamCustomMaxValue, - typeLongInteger, - sizeof(max), - &max); - } - - if(result == noErr) - { - if(text != NULL) - { - result = SetEventParameter ( - evt, - kEventParamCustomText, - typeCFStringRef, - sizeof(text), - &text); - } - } - - if(result == noErr) - { - // Send the event - PostEventToQueue( - GetMainEventQueue(), - evt, - kEventPriorityStandard); - - } - - return(result); -} - -OSStatus sendDone(void) -{ - OSStatus result; - EventRef evt; - - result = CreateEvent( - NULL, - kEventClassCustom, - kEventCustomDone, - 0, - kEventAttributeNone, - &evt); - - // This event needs to be targeted at the window so it goes to the window's handler. - if(result == noErr) - { - EventTargetRef target = GetWindowEventTarget(gWindow); - result = SetEventParameter ( - evt, - kEventParamPostTarget, - typeEventTargetRef, - sizeof(target), - &target); - } - - if(result == noErr) - { - // Send the event - PostEventToQueue( - GetMainEventQueue(), - evt, - kEventPriorityStandard); - - } - - return(result); -} - -OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata) -{ - OSStatus result = eventNotHandledErr; - OSStatus err; - UInt32 evtClass = GetEventClass(event); - UInt32 evtKind = GetEventKind(event); - - if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess)) - { - HICommand cmd; - err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd); - - if(err == noErr) - { - switch(cmd.commandID) - { - case kHICommandCancel: - gCancelled = true; -// QuitAppModalLoopForWindow(gWindow); - result = noErr; - break; - } - } - } - else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomProgress)) - { - // Request to update the progress dialog - long cur = 0; - long max = 0; - CFStringRef text = NULL; - (void) GetEventParameter(event, kEventParamCustomCurValue, typeLongInteger, NULL, sizeof(cur), NULL, &cur); - (void) GetEventParameter(event, kEventParamCustomMaxValue, typeLongInteger, NULL, sizeof(max), NULL, &max); - (void) GetEventParameter(event, kEventParamCustomText, typeCFStringRef, NULL, sizeof(text), NULL, &text); - - err = setProgress(cur, max); - if(err == noErr) - { - if(text != NULL) - { - setProgressText(text); - } - } - - result = noErr; - } - else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomDone)) - { - // We're done. Exit the modal loop. - QuitAppModalLoopForWindow(gWindow); - result = noErr; - } - - return(result); -} - -#if 0 -size_t curl_download_callback(void *data, size_t size, size_t nmemb, - void *user_data) -{ - S32 bytes = size * nmemb; - char *cdata = (char *) data; - for (int i =0; i < bytes; i += 1) - { - gServerResponse.append(cdata[i]); - } - return bytes; -} -#endif - -int curl_progress_callback_func(void *clientp, - double dltotal, - double dlnow, - double ultotal, - double ulnow) -{ - int max = (int)(dltotal / 1024.0); - int cur = (int)(dlnow / 1024.0); - sendProgress(cur, max); - - if(gCancelled) - return(1); - - return(0); -} +LLMacUpdater* LLMacUpdater::sInstance = NULL; -int parse_args(int argc, char **argv) +LLMacUpdater::LLMacUpdater(): + mUpdateURL (NULL), + mProductName (NULL), + mBundleID (NULL), + mDmgFile (NULL), + mMarkerPath (NULL) { - int j; - - for (j = 1; j < argc; j++) - { - if ((!strcmp(argv[j], "-url")) && (++j < argc)) - { - gUpdateURL = argv[j]; - } - else if ((!strcmp(argv[j], "-name")) && (++j < argc)) - { - gProductName = argv[j]; - } - else if ((!strcmp(argv[j], "-bundleid")) && (++j < argc)) - { - gBundleID = argv[j]; - } - else if ((!strcmp(argv[j], "-dmg")) && (++j < argc)) - { - gDmgFile = argv[j]; - } - else if ((!strcmp(argv[j], "-marker")) && (++j < argc)) - { - gMarkerPath = argv[j];; - } - } - - return 0; + sInstance = this; } -int main(int argc, char **argv) +void LLMacUpdater::doUpdate() { - // We assume that all the logs we're looking for reside on the current drive + // We assume that all the logs we're looking for reside on the current drive gDirUtilp->initAppDirs("SecondLife"); - + LLError::initForApplication( gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - + // Rename current log file to ".old" std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "updater.log.old"); std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "updater.log"); LLFile::rename(log_file.c_str(), old_log_file.c_str()); - + // Set the log file to updater.log LLError::logToFile(log_file); - - ///////////////////////////////////////// - // - // Process command line arguments - // - gUpdateURL = NULL; - gProductName = NULL; - gBundleID = NULL; - gDmgFile = NULL; - gMarkerPath = NULL; - parse_args(argc, argv); - if ((gUpdateURL == NULL) && (gDmgFile == NULL)) + + if ((mUpdateURL == NULL) && (mDmgFile == NULL)) { llinfos << "Usage: mac_updater -url <url> | -dmg <dmg file> [-name <product_name>] [-program <program_name>]" << llendl; exit(1); } else { - llinfos << "Update url is: " << gUpdateURL << llendl; - if (gProductName) + llinfos << "Update url is: " << mUpdateURL << llendl; + if (mProductName) { - llinfos << "Product name is: " << gProductName << llendl; + llinfos << "Product name is: " << *mProductName << llendl; } else { - gProductName = "Second Life"; + mProductName = new std::string("Second Life"); } - if (gBundleID) + if (mBundleID) { - llinfos << "Bundle ID is: " << gBundleID << llendl; + llinfos << "Bundle ID is: " << *mBundleID << llendl; } else { - gBundleID = "com.secondlife.indra.viewer"; + mBundleID = new std::string("com.secondlife.indra.viewer"); } } - llinfos << "Starting " << gProductName << " Updater" << llendl; - - // Real UI... - OSStatus err; - IBNibRef nib = NULL; - - err = CreateNibReference(CFSTR("AutoUpdater"), &nib); - - char windowTitle[MAX_PATH]; /* Flawfinder: ignore */ - snprintf(windowTitle, sizeof(windowTitle), "%s Updater", gProductName); - CFStringRef windowTitleRef = NULL; - windowTitleRef = CFStringCreateWithCString(NULL, windowTitle, kCFStringEncodingUTF8); - - if(err == noErr) - { - err = CreateWindowFromNib(nib, CFSTR("Updater"), &gWindow); - } - - if (err == noErr) - { - err = SetWindowTitleWithCFString(gWindow, windowTitleRef); - } - CFRelease(windowTitleRef); - - if(err == noErr) - { - // Set up an event handler for the window. - EventTypeSpec handlerEvents[] = - { - { kEventClassCommand, kEventCommandProcess }, - { kEventClassCustom, kEventCustomProgress }, - { kEventClassCustom, kEventCustomDone } - }; - InstallStandardEventHandler(GetWindowEventTarget(gWindow)); - InstallWindowEventHandler( - gWindow, - NewEventHandlerUPP(dialogHandler), - GetEventTypeCount (handlerEvents), - handlerEvents, - 0, - &gEventHandler); - } - - if(err == noErr) - { - ShowWindow(gWindow); - SelectWindow(gWindow); - } - - if(err == noErr) - { - pthread_create(&updatethread, - NULL, - &updatethreadproc, - NULL); - - } - - if(err == noErr) - { - RunAppModalLoopForWindow(gWindow); - } - + llinfos << "Starting " << *mProductName << " Updater" << llendl; + + pthread_create(&updatethread, + NULL, + &sUpdatethreadproc, + NULL); + + void *threadresult; - + pthread_join(updatethread, &threadresult); - - if(!gCancelled && (gFailure != noErr)) + + if(gCancelled || gFailure) { - // Something went wrong. Since we always just tell the user to download a new version, we don't really care what. - AlertStdCFStringAlertParamRec params; - SInt16 retval_mac = 1; - DialogRef alert = NULL; - OSStatus err; - - params.version = kStdCFStringAlertVersionOne; - params.movable = false; - params.helpButton = false; - params.defaultText = (CFStringRef)kAlertDefaultOKText; - params.cancelText = 0; - params.otherText = 0; - params.defaultButton = 1; - params.cancelButton = 0; - params.position = kWindowDefaultPosition; - params.flags = 0; - - err = CreateStandardAlert( - kAlertStopAlert, - CFSTR("Error"), - CFSTR("An error occurred while updating Second Life. Please download the latest version from www.secondlife.com."), - ¶ms, - &alert); - - if(err == noErr) - { - err = RunStandardAlert( - alert, - NULL, - &retval_mac); - } - - if(gMarkerPath != 0) + sendStopAlert(); + + if(mMarkerPath != 0) { // Create a install fail marker that can be used by the viewer to // detect install problems. - std::ofstream stream(gMarkerPath); + std::ofstream stream(mMarkerPath->c_str()); if(stream) stream << -1; } exit(-1); } else { exit(0); } - - if(gWindow != NULL) - { - DisposeWindow(gWindow); - } - - if(nib != NULL) - { - DisposeNibReference(nib); - } - return 0; + return; } -bool isDirWritable(FSRef &dir) +//SPATTERS TODO this should be moved to lldir_mac.cpp +const std::string LLMacUpdater::walkParents( unsigned int depth, const std::string& childpath ) { - bool result = false; - - // Test for a writable directory by creating a directory, then deleting it again. - // This is kinda lame, but will pretty much always give the right answer. - - OSStatus err = noErr; - char temp[PATH_MAX] = ""; /* Flawfinder: ignore */ + boost::filesystem::path fullpath(childpath.c_str()); + + while (depth >= 0 && fullpath.has_parent_path()) + { + fullpath = boost::filesystem::path(fullpath.parent_path()); + --depth; + } + + return fullpath.string(); +} - err = FSRefMakePath(&dir, (UInt8*)temp, sizeof(temp)); +//#if 0 +//size_t curl_download_callback(void *data, size_t size, size_t nmemb, +// void *user_data) +//{ +// S32 bytes = size * nmemb; +// char *cdata = (char *) data; +// for (int i =0; i < bytes; i += 1) +// { +// gServerResponse.append(cdata[i]); +// } +// return bytes; +//} +//#endif - if(err == noErr) - { - strncat(temp, "/.test_XXXXXX", (sizeof(temp) - strlen(temp)) - 1); - - if(mkdtemp(temp) != NULL) - { - // We were able to make the directory. This means the directory is writable. - result = true; - - // Clean up. - rmdir(temp); - } - } - -#if 0 - // This seemed like a good idea, but won't tell us if we're on a volume mounted read-only. - UInt8 perm; - err = FSGetUserPrivilegesPermissions(&targetParentRef, &perm, NULL); - if(err == noErr) - { - if(perm & kioACUserNoMakeChangesMask) - { - // Parent directory isn't writable. - llinfos << "Target parent directory not writable." << llendl; - err = -1; - replacingTarget = false; - } - } -#endif +int curl_progress_callback_func(void *clientp, + double dltotal, + double dlnow, + double ultotal, + double ulnow) +{ + int max = (int)(dltotal / 1024.0); + int cur = (int)(dlnow / 1024.0); + setProgress(cur, max); + + if(gCancelled) + return(1); - return result; + return(0); } -static std::string HFSUniStr255_to_utf8str(const HFSUniStr255* src) +bool LLMacUpdater::isApplication(const std::string& app_str) { - llutf16string string16((U16*)&(src->unicode), src->length); - std::string result = utf16str_to_utf8str(string16); - return result; + return !(bool) app_str.compare( app_str.length()-4, 4, ".app"); } - -int restoreObject(const char* aside, const char* target, const char* path, const char* object) + +// Search through the directory specified by 'parent' for an item that appears to be a Second Life viewer. +bool LLMacUpdater::findAppBundleOnDiskImage(const boost::filesystem::path& dir_path, + boost::filesystem::path& path_found) { - char source[PATH_MAX] = ""; /* Flawfinder: ignore */ - char dest[PATH_MAX] = ""; /* Flawfinder: ignore */ - snprintf(source, sizeof(source), "%s/%s/%s", aside, path, object); - snprintf(dest, sizeof(dest), "%s/%s", target, path); - FSRef sourceRef; - FSRef destRef; - OSStatus err; - err = FSPathMakeRef((UInt8 *)source, &sourceRef, NULL); - if(err != noErr) return false; - err = FSPathMakeRef((UInt8 *)dest, &destRef, NULL); - if(err != noErr) return false; - - llinfos << "Copying " << source << " to " << dest << llendl; - - err = FSCopyObjectSync( - &sourceRef, - &destRef, - NULL, - NULL, - kFSFileOperationOverwrite); - - if(err != noErr) return false; - return true; + if ( !exists( dir_path ) ) return false; + + boost::filesystem::directory_iterator end_itr; + + for ( boost::filesystem::directory_iterator itr( dir_path ); + itr != end_itr; + ++itr ) + { + if ( boost::filesystem::is_directory(itr->status()) ) + { + std::string dir_name = itr->string(); + if ( isApplication(dir_name) ) + { + if(isFSRefViewerBundle(dir_name)) + { + llinfos << dir_name << " is the one" << llendl; + + path_found = itr->path(); + return true; + } + } + } + } + return false; } -// Replace any mention of "Second Life" with the product name. -void filterFile(const char* filename) +bool LLMacUpdater::verifyDirectory(const boost::filesystem::path* directory, bool isParent) { - char temp[PATH_MAX] = ""; /* Flawfinder: ignore */ - // First copy the target's version, so we can run it through sed. - snprintf(temp, sizeof(temp), "cp '%s' '%s.tmp'", filename, filename); - system(temp); /* Flawfinder: ignore */ - - // Now run it through sed. - snprintf(temp, sizeof(temp), - "sed 's/Second Life/%s/g' '%s.tmp' > '%s'", gProductName, filename, filename); - system(temp); /* Flawfinder: ignore */ + bool replacingTarget; + std::string app_str = directory->string(); + + if (boost::filesystem::is_directory(*directory)) + { + // This is fine, just means we're not replacing anything. + replacingTarget = true; + } + else + { + replacingTarget = isParent; + } + + //Check that the directory is writeable. + if(!isDirWritable(app_str)) + { + // Parent directory isn't writable. + llinfos << "Target directory not writable." << llendl; + replacingTarget = false; + } + return replacingTarget; } - -static bool isFSRefViewerBundle(FSRef *targetRef) + +bool LLMacUpdater::getViewerDir(boost::filesystem::path &app_dir) { - bool result = false; - CFURLRef targetURL = NULL; - CFBundleRef targetBundle = NULL; - CFStringRef targetBundleID = NULL; - CFStringRef sourceBundleID = NULL; - - targetURL = CFURLCreateFromFSRef(NULL, targetRef); - - if(targetURL == NULL) - { - llinfos << "Error creating target URL." << llendl; - } - else - { - targetBundle = CFBundleCreate(NULL, targetURL); - } - - if(targetBundle == NULL) - { - llinfos << "Failed to create target bundle." << llendl; - } - else - { - targetBundleID = CFBundleGetIdentifier(targetBundle); - } - - if(targetBundleID == NULL) - { - llinfos << "Couldn't retrieve target bundle ID." << llendl; - } - else - { - sourceBundleID = CFStringCreateWithCString(NULL, gBundleID, kCFStringEncodingUTF8); - if(CFStringCompare(sourceBundleID, targetBundleID, 0) == kCFCompareEqualTo) - { - // This is the bundle we're looking for. - result = true; - } - else - { - llinfos << "Target bundle ID mismatch." << llendl; - } - } - - // Don't release targetBundleID -- since we don't retain it, it's released when targetBundle is released. - if(targetURL != NULL) - CFRelease(targetURL); - if(targetBundle != NULL) - CFRelease(targetBundle); - - return result; + std::string app_dir_str; + + //Walk up 6 levels from the App Updater's installation point. + app_dir_str = walkParents( 6, *mApplicationPath ); + + app_dir = boost::filesystem::path(app_dir_str); + + std::string app_str(app_dir.string()); + + //Check to see that the directory's name ends in .app Lame but it's the best thing we have to go on. + //If it's not there, we're going to default to /Applications/VIEWERNAME + if (!isApplication(app_str)) + { + llinfos << "Target search failed, defaulting to /Applications/" << *mProductName << ".app." << llendl; + std::string newpath = std::string("/Applications/") + mProductName->c_str(); + app_dir = boost::filesystem::path(newpath); + } + return verifyDirectory(&app_dir); } -// Search through the directory specified by 'parent' for an item that appears to be a Second Life viewer. -static OSErr findAppBundleOnDiskImage(FSRef *parent, FSRef *app) +bool LLMacUpdater::downloadDMG(const std::string& dmgName, boost::filesystem::path* temp_dir) { - FSIterator iterator; - bool found = false; + LLFILE *downloadFile = NULL; + char temp[PATH_MAX] = ""; /* Flawfinder: ignore */ - OSErr err = FSOpenIterator( parent, kFSIterateFlat, &iterator ); - if(!err) + chdir(temp_dir->string().c_str()); + + snprintf(temp, sizeof(temp), "SecondLife.dmg"); + + downloadFile = LLFile::fopen(temp, "wb"); /* Flawfinder: ignore */ + if(downloadFile == NULL) + { + return false; + } + + bool success = false; + + CURL *curl = curl_easy_init(); + + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback); + curl_easy_setopt(curl, CURLOPT_FILE, downloadFile); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); + curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &curl_progress_callback_func); + curl_easy_setopt(curl, CURLOPT_URL, mUpdateURL); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + + sendProgress(0, 1, std::string("Downloading...")); + + CURLcode result = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + + if(gCancelled) + { + llinfos << "User cancel, bailing out."<< llendl; + goto close_file; + } + + if(result != CURLE_OK) + { + llinfos << "Error " << result << " while downloading disk image."<< llendl; + goto close_file; + } + + fclose(downloadFile); + downloadFile = NULL; + + success = true; + +close_file: + // Close disk image file if necessary + if(downloadFile != NULL) { - do - { - ItemCount actualObjects = 0; - Boolean containerChanged = false; - FSCatalogInfo info; - FSRef ref; - HFSUniStr255 unicodeName; - err = FSGetCatalogInfoBulk( - iterator, - 1, - &actualObjects, - &containerChanged, - kFSCatInfoNodeFlags, - &info, - &ref, - NULL, - &unicodeName ); - - if(actualObjects == 0) - break; - - if(!err) - { - // Call succeeded and not done with the iteration. - std::string name = HFSUniStr255_to_utf8str(&unicodeName); + llinfos << "Closing download file." << llendl; + + fclose(downloadFile); + downloadFile = NULL; + } - llinfos << "Considering \"" << name << "\"" << llendl; + return success; +} + +bool LLMacUpdater::doMount(const std::string& dmgName, char* deviceNode, const boost::filesystem::path& temp_dir) +{ + char temp[PATH_MAX] = ""; /* Flawfinder: ignore */ + + sendProgress(0, 0, std::string("Mounting image...")); + chdir(temp_dir.string().c_str()); + std::string mnt_dir = temp_dir.string() + std::string("/mnt"); + LLFile::mkdir(mnt_dir.c_str(), 0700); + + // NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder, + // but if our cleanup fails, this makes it much harder for the user to unmount the image. + std::string mountOutput; + boost::format cmdFormat("hdiutil attach %s -mountpoint mnt"); + cmdFormat % dmgName; + FILE* mounter = popen(cmdFormat.str().c_str(), "r"); /* Flawfinder: ignore */ + + if(mounter == NULL) + { + llinfos << "Failed to mount disk image, exiting."<< llendl; + return false; + } + + // We need to scan the output from hdiutil to find the device node it uses to attach the disk image. + // If we don't have this information, we can't detach it later. + while(mounter != NULL) + { + size_t len = fread(temp, 1, sizeof(temp)-1, mounter); + temp[len] = 0; + mountOutput.append(temp); + if(len < sizeof(temp)-1) + { + // End of file or error. + int result = pclose(mounter); + if(result != 0) + { + // NOTE: We used to abort here, but pclose() started returning + // -1, possibly when the size of the DMG passed a certain point + llinfos << "Unexpected result closing pipe: " << result << llendl; + } + mounter = NULL; + } + } + + if(!mountOutput.empty()) + { + const char *s = mountOutput.c_str(); + const char *prefix = "/dev/"; + char *sub = strstr(s, prefix); + + if(sub != NULL) + { + sub += strlen(prefix); /* Flawfinder: ignore */ + sscanf(sub, "%1023s", deviceNode); /* Flawfinder: ignore */ + } + } + + if(deviceNode[0] != 0) + { + llinfos << "Disk image attached on /dev/" << deviceNode << llendl; + } + else + { + llinfos << "Disk image device node not found!" << llendl; + return false; + } + + return true; +} - if(info.nodeFlags & kFSNodeIsDirectoryMask) - { - // This is a directory. See if it's a .app - if(name.find(".app") != std::string::npos) - { - // Looks promising. Check to see if it has the right bundle identifier. - if(isFSRefViewerBundle(&ref)) - { - llinfos << name << " is the one" << llendl; - // This is the one. Return it. - *app = ref; - found = true; - break; - } else { - llinfos << name << " is not the bundle we are looking for; move along" << llendl; - } +bool LLMacUpdater::moveApplication (const boost::filesystem::path& app_dir, + const boost::filesystem::path& temp_dir, + boost::filesystem::path& aside_dir) +{ + try + { + //Grab filename from installdir append to tempdir move set aside_dir to moved path. + std::string install_str = app_dir.parent_path().string(); + std::string temp_str = temp_dir.string(); + std::string app_str = app_dir.filename(); + aside_dir = boost::filesystem::path( boost::filesystem::operator/(temp_dir,app_str) ); + std::cout << "Attempting to move " << app_dir.string() << " to " << aside_dir.string() << std::endl; + + boost::filesystem::rename(app_dir, aside_dir); + } + catch(boost::filesystem::filesystem_error e) + { + llinfos << "Application move failed." << llendl; + return false; + } + return true; +} - } - } - } - } - while(!err); - - llinfos << "closing the iterator" << llendl; - - FSCloseIterator(iterator); - - llinfos << "closed" << llendl; - } - - if(!err && !found) - err = fnfErr; - - return err; +bool LLMacUpdater::doInstall(const boost::filesystem::path& app_dir, + const boost::filesystem::path& temp_dir, + boost::filesystem::path& mount_dir, + bool replacingTarget) +{ + std::string temp_name = temp_dir.string() + std::string("/mnt"); + + llinfos << "Disk image mount point is: " << temp_name << llendl; + + mount_dir = boost::filesystem::path(temp_name.c_str()); + + if (! boost::filesystem::exists ( mount_dir ) ) + { + llinfos << "Couldn't make FSRef to disk image mount point." << llendl; + return false; + } + + sendProgress(0, 0, std::string("Searching for the app bundle...")); + + boost::filesystem::path source_dir; + + if ( !findAppBundleOnDiskImage(mount_dir, source_dir) ) + { + llinfos << "Couldn't find application bundle on mounted disk image." << llendl; + return false; + } + else + { + llinfos << "found the bundle." << llendl; + } + + sendProgress(0, 0, std::string("Preparing to copy files...")); + + // this will hold the name of the destination target + boost::filesystem::path aside_dir; + + if(replacingTarget) + { + + if (! moveApplication (app_dir, temp_dir, aside_dir) ) + { + llwarns << "failed to move aside old version." << llendl; + return false; + } + } + + sendProgress(0, 0, std::string("Copying files...")); + + llinfos << "Starting copy..." << llendl; + // If we were replacingTarget, we've moved the app to a temp directory. + // Otherwise the destination should be empty. + // We have mounted the DMG as a volume so we should be able to just + // move the app from the volume to the destination and everything will just work. + + + // Copy the new version from the disk image to the target location. + + //The installer volume is mounted read-only so we can't move. Instead copy and then unmount. + if (! copyDir(source_dir.string(), app_dir.string()) ) + { + llwarns << "Failed to copy " << source_dir.string() << " to " << app_dir.string() << llendl; + + // Something went wrong during the copy. Attempt to put the old version back and bail. + boost::filesystem::rename(app_dir, aside_dir); + return false; + + } + + // The update has succeeded. Clear the cache directory. + + sendProgress(0, 0, std::string("Clearing cache...")); + + llinfos << "Clearing cache..." << llendl; + + gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), "*.*"); + + llinfos << "Clear complete." << llendl; + + return true; } -void *updatethreadproc(void*) +void* LLMacUpdater::updatethreadproc(void*) { char tempDir[PATH_MAX] = ""; /* Flawfinder: ignore */ - FSRef tempDirRef; char temp[PATH_MAX] = ""; /* Flawfinder: ignore */ // *NOTE: This buffer length is used in a scanf() below. char deviceNode[1024] = ""; /* Flawfinder: ignore */ - LLFILE *downloadFile = NULL; - OSStatus err; - ProcessSerialNumber psn; - char target[PATH_MAX] = ""; /* Flawfinder: ignore */ - FSRef targetRef; - FSRef targetParentRef; - FSVolumeRefNum targetVol; - FSRef trashFolderRef; - Boolean replacingTarget = false; - - memset(&tempDirRef, 0, sizeof(tempDirRef)); - memset(&targetRef, 0, sizeof(targetRef)); - memset(&targetParentRef, 0, sizeof(targetParentRef)); - - try - { - // Attempt to get a reference to the Second Life application bundle containing this updater. - // Any failures during this process will cause us to default to updating /Applications/Second Life.app - { - FSRef myBundle; - - err = GetCurrentProcess(&psn); - if(err == noErr) - { - err = GetProcessBundleLocation(&psn, &myBundle); - } - - if(err == noErr) - { - // Sanity check: Make sure the name of the item referenced by targetRef is "Second Life.app". - FSRefMakePath(&myBundle, (UInt8*)target, sizeof(target)); - - llinfos << "Updater bundle location: " << target << llendl; - } - - // Our bundle should be in Second Life.app/Contents/Resources/AutoUpdater.app - // so we need to go up 3 levels to get the path to the main application bundle. - if(err == noErr) - { - err = FSGetCatalogInfo(&myBundle, kFSCatInfoNone, NULL, NULL, NULL, &targetRef); - } - if(err == noErr) - { - err = FSGetCatalogInfo(&targetRef, kFSCatInfoNone, NULL, NULL, NULL, &targetRef); - } - if(err == noErr) - { - err = FSGetCatalogInfo(&targetRef, kFSCatInfoNone, NULL, NULL, NULL, &targetRef); - } - - // And once more to get the parent of the target - if(err == noErr) - { - err = FSGetCatalogInfo(&targetRef, kFSCatInfoNone, NULL, NULL, NULL, &targetParentRef); - } - - if(err == noErr) - { - FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target)); - llinfos << "Path to target: " << target << llendl; - } - - // Sanity check: make sure the target is a bundle with the right identifier - if(err == noErr) - { - // Assume the worst... - err = -1; - - if(isFSRefViewerBundle(&targetRef)) - { - // This is the bundle we're looking for. - err = noErr; - replacingTarget = true; - } - } - - // Make sure the target's parent directory is writable. - if(err == noErr) - { - if(!isDirWritable(targetParentRef)) - { - // Parent directory isn't writable. - llinfos << "Target parent directory not writable." << llendl; - err = -1; - replacingTarget = false; - } - } - - if(err != noErr) - { - Boolean isDirectory; - llinfos << "Target search failed, defaulting to /Applications/" << gProductName << ".app." << llendl; - - // Set up the parent directory - err = FSPathMakeRef((UInt8*)"/Applications", &targetParentRef, &isDirectory); - if((err != noErr) || (!isDirectory)) - { - // We're so hosed. - llinfos << "Applications directory not found, giving up." << llendl; - throw 0; - } - - snprintf(target, sizeof(target), "/Applications/%s.app", gProductName); - - memset(&targetRef, 0, sizeof(targetRef)); - err = FSPathMakeRef((UInt8*)target, &targetRef, NULL); - if(err == fnfErr) - { - // This is fine, just means we're not replacing anything. - err = noErr; - replacingTarget = false; - } - else - { - replacingTarget = true; - } - - // Make sure the target's parent directory is writable. - if(err == noErr) - { - if(!isDirWritable(targetParentRef)) - { - // Parent directory isn't writable. - llinfos << "Target parent directory not writable." << llendl; - err = -1; - replacingTarget = false; - } - } - - } - - // If we haven't fixed all problems by this point, just bail. - if(err != noErr) - { - llinfos << "Unable to pick a target, giving up." << llendl; - throw 0; - } - } - - // Find the volID of the volume the target resides on - { - FSCatalogInfo info; - err = FSGetCatalogInfo( - &targetParentRef, - kFSCatInfoVolume, - &info, - NULL, - NULL, - NULL); - - if(err != noErr) - throw 0; - - targetVol = info.volume; - } - - // Find the temporary items and trash folders on that volume. - err = FSFindFolder( - targetVol, - kTrashFolderType, - true, - &trashFolderRef); - - if(err != noErr) - throw 0; - -#if 0 // *HACK for DEV-11935 see below for details. - - FSRef tempFolderRef; - - err = FSFindFolder( - targetVol, - kTemporaryFolderType, - true, - &tempFolderRef); - - if(err != noErr) - throw 0; - - err = FSRefMakePath(&tempFolderRef, (UInt8*)temp, sizeof(temp)); + + bool replacingTarget = false; - if(err != noErr) - throw 0; + boost::filesystem::path install_dir; + boost::filesystem::path app_dir; + boost::filesystem::path temp_dir; + boost::filesystem::path mount_dir; + + // Attempt to get a reference to the Second Life application bundle containing this updater. + // Any failures during this process will cause us to default to updating /Applications/Second Life.app -#else - - // *HACK for DEV-11935 the above kTemporaryFolderType query was giving - // back results with path names that seem to be too long to be used as - // mount points. I suspect this incompatibility was introduced in the - // Leopard 10.5.2 update, but I have not verified this. - char const HARDCODED_TMP[] = "/tmp"; - strncpy(temp, HARDCODED_TMP, sizeof(HARDCODED_TMP)); + try + { + replacingTarget = getViewerDir( app_dir ); + install_dir = app_dir.parent_path(); + + if (!mkTempDir(temp_dir)) + { + throw 0; + } + + //In case the dir doesn't exist, try to create it. If create fails, verify it exists. + if (! boost::filesystem::create_directory(install_dir)) + { + + + if(isFSRefViewerBundle(install_dir.parent_path().string())) + { + // This is the bundle we're looking for. + replacingTarget = true; + } + } + + if ( !verifyDirectory(&install_dir, true) ) + { + // We're so hosed. + llinfos << "Applications directory not found, giving up." << llendl; + throw 0; + } -#endif // 0 *HACK for DEV-11935 // Skip downloading the file if the dmg was passed on the command line. std::string dmgName; - if(gDmgFile != NULL) { - dmgName = basename((char *)gDmgFile); - char * dmgDir = dirname((char *)gDmgFile); - strncpy(tempDir, dmgDir, sizeof(tempDir)); - err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL); - if(err != noErr) throw 0; - chdir(tempDir); - goto begin_install; + if(mDmgFile != NULL) { + //Create a string from the mDmgFile then a dir reference to that. + //change to that directory and begin install. + + boost::filesystem::path dmg_path(*mDmgFile); + + dmgName = dmg_path.string(); + std::string* dmgPath = new std::string(dmg_path.parent_path().string()); + if (!isDirWritable(*dmgPath)) throw 0; + + chdir(dmgPath->c_str()); } else { // Continue on to download file. dmgName = "SecondLife.dmg"; - } - - - strncat(temp, "/SecondLifeUpdate_XXXXXX", (sizeof(temp) - strlen(temp)) - 1); - if(mkdtemp(temp) == NULL) - { - throw 0; - } - - strncpy(tempDir, temp, sizeof(tempDir)); - temp[sizeof(tempDir) - 1] = '\0'; - - llinfos << "tempDir is " << tempDir << llendl; - - err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL); - - if(err != noErr) - throw 0; - - chdir(tempDir); - - snprintf(temp, sizeof(temp), "SecondLife.dmg"); - - downloadFile = LLFile::fopen(temp, "wb"); /* Flawfinder: ignore */ - if(downloadFile == NULL) - { - throw 0; - } - - { - CURL *curl = curl_easy_init(); - - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback); - curl_easy_setopt(curl, CURLOPT_FILE, downloadFile); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); - curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &curl_progress_callback_func); - curl_easy_setopt(curl, CURLOPT_URL, gUpdateURL); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - - sendProgress(0, 1, CFSTR("Downloading...")); - - CURLcode result = curl_easy_perform(curl); - - curl_easy_cleanup(curl); - - if(gCancelled) - { - llinfos << "User cancel, bailing out."<< llendl; - throw 0; - } - - if(result != CURLE_OK) - { - llinfos << "Error " << result << " while downloading disk image."<< llendl; - throw 0; - } - - fclose(downloadFile); - downloadFile = NULL; - } - - begin_install: - sendProgress(0, 0, CFSTR("Mounting image...")); - LLFile::mkdir("mnt", 0700); - - // NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder, - // but if our cleanup fails, this makes it much harder for the user to unmount the image. - std::string mountOutput; - boost::format cmdFormat("hdiutil attach %s -mountpoint mnt"); - cmdFormat % dmgName; - FILE* mounter = popen(cmdFormat.str().c_str(), "r"); /* Flawfinder: ignore */ - - if(mounter == NULL) - { - llinfos << "Failed to mount disk image, exiting."<< llendl; - throw 0; - } - - // We need to scan the output from hdiutil to find the device node it uses to attach the disk image. - // If we don't have this information, we can't detach it later. - while(mounter != NULL) - { - size_t len = fread(temp, 1, sizeof(temp)-1, mounter); - temp[len] = 0; - mountOutput.append(temp); - if(len < sizeof(temp)-1) - { - // End of file or error. - int result = pclose(mounter); - if(result != 0) - { - // NOTE: We used to abort here, but pclose() started returning - // -1, possibly when the size of the DMG passed a certain point - llinfos << "Unexpected result closing pipe: " << result << llendl; - } - mounter = NULL; - } - } - - if(!mountOutput.empty()) - { - const char *s = mountOutput.c_str(); - const char *prefix = "/dev/"; - char *sub = strstr(s, prefix); - - if(sub != NULL) - { - sub += strlen(prefix); /* Flawfinder: ignore */ - sscanf(sub, "%1023s", deviceNode); /* Flawfinder: ignore */ - } - } - - if(deviceNode[0] != 0) - { - llinfos << "Disk image attached on /dev/" << deviceNode << llendl; - } - else - { - llinfos << "Disk image device node not found!" << llendl; - throw 0; - } - - // Get an FSRef to the new application on the disk image - FSRef sourceRef; - FSRef mountRef; - snprintf(temp, sizeof(temp), "%s/mnt", tempDir); - - llinfos << "Disk image mount point is: " << temp << llendl; + + + if (!downloadDMG(dmgName, &temp_dir)) + { + throw 0; + } + } + + if (!doMount(dmgName, deviceNode, temp_dir)) + { + throw 0; + } + + if (!doInstall( app_dir, temp_dir, mount_dir, replacingTarget )) + { + throw 0; + } - err = FSPathMakeRef((UInt8 *)temp, &mountRef, NULL); - if(err != noErr) - { - llinfos << "Couldn't make FSRef to disk image mount point." << llendl; - throw 0; - } - - sendProgress(0, 0, CFSTR("Searching for the app bundle...")); - err = findAppBundleOnDiskImage(&mountRef, &sourceRef); - if(err != noErr) - { - llinfos << "Couldn't find application bundle on mounted disk image." << llendl; - throw 0; - } - else - { - llinfos << "found the bundle." << llendl; - } - - sendProgress(0, 0, CFSTR("Preparing to copy files...")); - - FSRef asideRef; - char aside[MAX_PATH]; /* Flawfinder: ignore */ - - // this will hold the name of the destination target - CFStringRef appNameRef; - - if(replacingTarget) - { - // Get the name of the target we're replacing - HFSUniStr255 appNameUniStr; - err = FSGetCatalogInfo(&targetRef, 0, NULL, &appNameUniStr, NULL, NULL); - if(err != noErr) - throw 0; - appNameRef = FSCreateStringFromHFSUniStr(NULL, &appNameUniStr); - - // Move aside old version (into work directory) - err = FSMoveObject(&targetRef, &tempDirRef, &asideRef); - if(err != noErr) - { - llwarns << "failed to move aside old version (error code " << - err << ")" << llendl; - throw 0; - } - - // Grab the path for later use. - err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside)); - } - else - { - // Construct the name of the target based on the product name - char appName[MAX_PATH]; /* Flawfinder: ignore */ - snprintf(appName, sizeof(appName), "%s.app", gProductName); - appNameRef = CFStringCreateWithCString(NULL, appName, kCFStringEncodingUTF8); - } - - sendProgress(0, 0, CFSTR("Copying files...")); - - llinfos << "Starting copy..." << llendl; - - // Copy the new version from the disk image to the target location. - err = FSCopyObjectSync( - &sourceRef, - &targetParentRef, - appNameRef, - &targetRef, - kFSFileOperationDefaultOptions); - - // Grab the path for later use. - err = FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target)); - if(err != noErr) - throw 0; - - llinfos << "Copy complete. Target = " << target << llendl; - - if(err != noErr) - { - // Something went wrong during the copy. Attempt to put the old version back and bail. - (void)FSDeleteObject(&targetRef); - if(replacingTarget) - { - (void)FSMoveObject(&asideRef, &targetParentRef, NULL); - } - throw 0; - } - else - { - // The update has succeeded. Clear the cache directory. - - sendProgress(0, 0, CFSTR("Clearing cache...")); - - llinfos << "Clearing cache..." << llendl; - - gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), "*.*"); - - llinfos << "Clear complete." << llendl; - - } } catch(...) { if(!gCancelled) - if(gFailure == noErr) - gFailure = -1; + gFailure = true; } // Failures from here on out are all non-fatal and not reported. - sendProgress(0, 3, CFSTR("Cleaning up...")); + sendProgress(0, 3, std::string("Cleaning up...")); - // Close disk image file if necessary - if(downloadFile != NULL) - { - llinfos << "Closing download file." << llendl; - - fclose(downloadFile); - downloadFile = NULL; - } - - sendProgress(1, 3); + setProgress(1, 3); // Unmount image if(deviceNode[0] != 0) { @@ -1223,35 +597,61 @@ void *updatethreadproc(void*) system(temp); /* Flawfinder: ignore */ } - sendProgress(2, 3); + setProgress(2, 3); + std::string *trash_str=getUserTrashFolder(); // Move work directory to the trash if(tempDir[0] != 0) { llinfos << "Moving work directory to the trash." << llendl; - - FSRef trashRef; - OSStatus err = FSMoveObjectToTrashSync(&tempDirRef, &trashRef, 0); - if(err != noErr) { - llwarns << "failed to move files to trash, (error code " << - err << ")" << llendl; - } + + try + { + boost::filesystem::path trash_dir(*trash_str); + boost::filesystem::rename(mount_dir, trash_dir); + } + catch(boost::filesystem::filesystem_error e) + { + llwarns << "Failed to move " << mount_dir.string() << " to " << *trash_str << llendl; + return (NULL); + } } - if(!gCancelled && !gFailure && (target[0] != 0)) + std::string app_name_str = app_dir.string(); + + if(!gCancelled && !gFailure && !app_name_str.empty()) { + //SPATTERS todo is there no better way to do this than system calls? llinfos << "Touching application bundle." << llendl; + + std::stringstream touch_str; - snprintf(temp, sizeof(temp), "touch '%s'", target); - system(temp); /* Flawfinder: ignore */ + touch_str << "touch '" << app_name_str << "'"; + + system(touch_str.str().c_str()); /* Flawfinder: ignore */ llinfos << "Launching updated application." << llendl; + + std::stringstream open_str; + + open_str << "open '" << app_name_str << "'"; - snprintf(temp, sizeof(temp), "open '%s'", target); - system(temp); /* Flawfinder: ignore */ + system(open_str.str().c_str()); /* Flawfinder: ignore */ } sendDone(); - return(NULL); + return (NULL); } + +//static +void* LLMacUpdater::sUpdatethreadproc(void* vptr) +{ + if (!sInstance) + { + llerrs << "LLMacUpdater not instantiated before use. Aborting." << llendl; + return (NULL); + } + return sInstance->updatethreadproc(vptr); +} + diff --git a/indra/mac_updater/mac_updater.h b/indra/mac_updater/mac_updater.h new file mode 100644 index 0000000000..756f46f996 --- /dev/null +++ b/indra/mac_updater/mac_updater.h @@ -0,0 +1,89 @@ +/** + * @file mac_updater.h + * @brief + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include <iostream> +#include <pthread.h> +#include <boost/filesystem.hpp> + +#ifndef LL_MAC_UPDATER_H +#define LL_MAC_UPDATER_H +extern bool gCancelled; +extern bool gFailure; + +void *updatethreadproc(void*); +std::string* walkParents( unsigned int depth, std::string* childpath ); +std::string* getUserTrashFolder(); + +void setProgress(int cur, int max); +void setProgressText(const std::string& str); +void sendProgress(int cur, int max, std::string str); +void sendDone(); +void sendStopAlert(); + +bool isFSRefViewerBundle(const std::string& targetURL); +bool isDirWritable(const std::string& dir_name); +bool mkTempDir(boost::filesystem::path& temp_dir); +bool copyDir(const std::string& src_dir, const std::string& dest_dir); + +int oldmain(); + +class LLMacUpdater +{ +public: + LLMacUpdater(); + void doUpdate(); + const std::string walkParents( unsigned int depth, const std::string& childpath ); + bool isApplication(const std::string& app_str); + void filterFile(const char* filename); + + bool findAppBundleOnDiskImage(const boost::filesystem::path& dir_path, + boost::filesystem::path& path_found); + + bool verifyDirectory(const boost::filesystem::path* directory, bool isParent=false); + bool getViewerDir(boost::filesystem::path &app_dir); + bool downloadDMG(const std::string& dmgName, boost::filesystem::path* temp_dir); + bool doMount(const std::string& dmgName, char* deviceNode, const boost::filesystem::path& temp_dir); + bool moveApplication (const boost::filesystem::path& app_dir, + const boost::filesystem::path& temp_dir, + boost::filesystem::path& aside_dir); + bool doInstall(const boost::filesystem::path& app_dir, + const boost::filesystem::path& temp_dir, + boost::filesystem::path& mount_dir, + bool replacingTarget); + void* updatethreadproc(void*); + static void* sUpdatethreadproc(void*); + +public: + std::string *mUpdateURL; + std::string *mProductName; + std::string *mBundleID; + std::string *mDmgFile; + std::string *mMarkerPath; + std::string *mApplicationPath; + static LLMacUpdater *sInstance; + +}; +#endif
\ No newline at end of file diff --git a/indra/mac_updater/main.m b/indra/mac_updater/main.m new file mode 100644 index 0000000000..aa3776a87d --- /dev/null +++ b/indra/mac_updater/main.m @@ -0,0 +1,34 @@ +/** + * @file main.m + * @brief + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#import <Cocoa/Cocoa.h> + +int main(int argc, char *argv[]) +{ + int retVal = NSApplicationMain(argc, (const char **)argv); + + return retVal; +} |