summaryrefslogtreecommitdiff
path: root/indra/mac_updater
diff options
context:
space:
mode:
Diffstat (limited to 'indra/mac_updater')
-rwxr-xr-xindra/mac_updater/AutoUpdater.nibbin0 -> 5251 bytes
-rw-r--r--indra/mac_updater/AutoUpdater.nib/classes.nib4
-rw-r--r--indra/mac_updater/AutoUpdater.nib/info.nib14
-rw-r--r--indra/mac_updater/AutoUpdater.nib/objects.xib56
-rw-r--r--indra/mac_updater/AutoUpdater.xib520
-rw-r--r--indra/mac_updater/CMakeLists.txt22
-rw-r--r--indra/mac_updater/MacUpdater-Info.plist30
-rw-r--r--indra/mac_updater/MacUpdaterAppDelegate.h60
-rw-r--r--indra/mac_updater/MacUpdaterAppDelegate.mm265
-rw-r--r--indra/mac_updater/mac_updater.cpp1594
-rw-r--r--indra/mac_updater/mac_updater.h91
-rw-r--r--indra/mac_updater/main.m34
12 files changed, 1524 insertions, 1166 deletions
diff --git a/indra/mac_updater/AutoUpdater.nib b/indra/mac_updater/AutoUpdater.nib
new file mode 100755
index 0000000000..03883e2b86
--- /dev/null
+++ b/indra/mac_updater/AutoUpdater.nib
Binary files differ
diff --git a/indra/mac_updater/AutoUpdater.nib/classes.nib b/indra/mac_updater/AutoUpdater.nib/classes.nib
deleted file mode 100644
index ea58db1189..0000000000
--- a/indra/mac_updater/AutoUpdater.nib/classes.nib
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-IBClasses = ();
-IBVersion = 1;
-}
diff --git a/indra/mac_updater/AutoUpdater.nib/info.nib b/indra/mac_updater/AutoUpdater.nib/info.nib
deleted file mode 100644
index a49a92385b..0000000000
--- a/indra/mac_updater/AutoUpdater.nib/info.nib
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>IBDocumentLocation</key>
- <string>103 138 356 240 0 0 1280 1002 </string>
- <key>IBFramework Version</key>
- <string>362.0</string>
- <key>IBSystem Version</key>
- <string>7D24</string>
- <key>targetFramework</key>
- <string>IBCarbonFramework</string>
-</dict>
-</plist>
diff --git a/indra/mac_updater/AutoUpdater.nib/objects.xib b/indra/mac_updater/AutoUpdater.nib/objects.xib
deleted file mode 100644
index 310411b711..0000000000
--- a/indra/mac_updater/AutoUpdater.nib/objects.xib
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" standalone="yes"?>
-<object class="NSIBObjectData">
- <string name="targetFramework">IBCarbonFramework</string>
- <object name="rootObject" class="NSCustomObject" id="1">
- <string name="customClass">NSApplication</string>
- </object>
- <array count="5" name="allObjects">
- <object class="IBCarbonWindow" id="166">
- <string name="windowRect">405 222 533 663 </string>
- <string name="title">Second Life Updater</string>
- <object name="rootControl" class="IBCarbonRootControl" id="167">
- <string name="bounds">0 0 128 441 </string>
- <array count="3" name="subviews">
- <object class="IBCarbonStaticText" id="181">
- <string name="bounds">20 20 44 421 </string>
- <ostype name="controlSignature">what</ostype>
- <string name="title">Initializing…</string>
- </object>
- <object class="IBCarbonButton" id="183">
- <string name="bounds">88 351 108 421 </string>
- <string name="title">Cancel</string>
- <ostype name="command">not!</ostype>
- <int name="buttonType">2</int>
- </object>
- <object class="IBCarbonProgressBar" id="193">
- <string name="bounds">51 19 70 422 </string>
- <ostype name="controlSignature">prog</ostype>
- <int name="initialValue">50</int>
- </object>
- </array>
- </object>
- <boolean name="isResizable">FALSE</boolean>
- <int name="carbonWindowClass">2</int>
- <int name="themeBrush">3</int>
- <int name="windowPosition">7</int>
- </object>
- <reference idRef="167"/>
- <reference idRef="181"/>
- <reference idRef="183"/>
- <reference idRef="193"/>
- </array>
- <array count="5" name="allParents">
- <reference idRef="1"/>
- <reference idRef="166"/>
- <reference idRef="167"/>
- <reference idRef="167"/>
- <reference idRef="167"/>
- </array>
- <dictionary count="2" name="nameTable">
- <string>File&apos;s Owner</string>
- <reference idRef="1"/>
- <string>Updater</string>
- <reference idRef="166"/>
- </dictionary>
- <unsigned_int name="nextObjectID">194</unsigned_int>
-</object>
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..7382e912bf 100644
--- a/indra/mac_updater/CMakeLists.txt
+++ b/indra/mac_updater/CMakeLists.txt
@@ -18,10 +18,14 @@ include_directories(
)
set(mac_updater_SOURCE_FILES
+ 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,19 @@ 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}
+ ${BOOST_FILESYSTEM_LIBRARY}
+ ${IOKIT_LIBRARY}
${CURL_LIBRARIES}
${CARES_LIBRARIES}
${LLCOMMON_LIBRARIES}
@@ -62,10 +72,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..c051214bb8
--- /dev/null
+++ b/indra/mac_updater/MacUpdaterAppDelegate.h
@@ -0,0 +1,60 @@
+/**
+ * @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
+
+
diff --git a/indra/mac_updater/MacUpdaterAppDelegate.mm b/indra/mac_updater/MacUpdaterAppDelegate.mm
new file mode 100644
index 0000000000..b6f95eef7e
--- /dev/null
+++ b/indra/mac_updater/MacUpdaterAppDelegate.mm
@@ -0,0 +1,265 @@
+/**
+ * @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 copyDir(const std::string& src_dir, const std::string& dest_dir)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ 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]);
+ }
+ [pool release];
+
+ 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)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *fullPath = [NSString stringWithCString:dir_name.c_str()
+ encoding:[NSString defaultCStringEncoding]];
+
+ NSFileManager *fm = [NSFileManager defaultManager];
+ bool result = [fm isWritableFileAtPath:fullPath];
+ [pool release];
+
+ return result;
+}
+
+std::string* getUserTrashFolder()
+{
+ std::string *result;
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSString *trash_str=[NSHomeDirectory() stringByAppendingPathComponent:@".Trash"];
+
+ result = NSToString( trash_str );
+
+ [pool release];
+ return result;
+
+}
+
+bool isFSRefViewerBundle(const std::string& targetURL)
+{
+ bool result = false;
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ 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;
+ }
+
+ [pool release];
+
+ 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..fc9565d63d 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,566 @@
#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);
+LLMacUpdater* LLMacUpdater::sInstance = NULL;
- }
-
- return(result);
-}
-
-OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
+LLMacUpdater::LLMacUpdater():
+ mUpdateURL (NULL),
+ mProductName (NULL),
+ mBundleID (NULL),
+ mDmgFile (NULL),
+ mMarkerPath (NULL)
{
- 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);
+ sInstance = this;
}
-#if 0
-size_t curl_download_callback(void *data, size_t size, size_t nmemb,
- void *user_data)
+void LLMacUpdater::doUpdate()
{
- 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);
-}
-
-int parse_args(int argc, char **argv)
-{
- 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;
-}
-
-int main(int argc, char **argv)
-{
- // 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."),
- &params,
- &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( signed 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 ( !boost::filesystem::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->path().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;
+ 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);
+
+ //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_dir_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);
+}
- targetURL = CFURLCreateFromFSRef(NULL, targetRef);
+bool LLMacUpdater::downloadDMG(const std::string& dmgName, boost::filesystem::path* temp_dir)
+{
+ LLFILE *downloadFile = NULL;
+ char temp[PATH_MAX] = ""; /* Flawfinder: ignore */
- 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
+ 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)
{
- 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;
- }
+ llinfos << "Closing download file." << llendl;
+
+ fclose(downloadFile);
+ downloadFile = NULL;
}
-
- // 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;
-}
-// 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)
+ return success;
+}
+
+bool LLMacUpdater::doMount(const std::string& dmgName, char* deviceNode, const boost::filesystem::path& temp_dir)
{
- FSIterator iterator;
- bool found = false;
-
- OSErr err = FSOpenIterator( parent, kFSIterateFlat, &iterator );
- if(!err)
- {
- 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);
+ 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;
+}
- llinfos << "Considering \"" << name << "\"" << 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().string();
+ 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;
+}
- 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::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;
+}
- }
- }
- }
- }
- while(!err);
-
- llinfos << "closing the iterator" << llendl;
-
- FSCloseIterator(iterator);
-
- llinfos << "closed" << llendl;
- }
-
- if(!err && !found)
- err = fnfErr;
-
- return err;
+bool mkTempDir(boost::filesystem::path& temp_dir)
+{
+ char temp_str[PATH_MAX] = "/tmp/SecondLifeUpdate_XXXXXX";
+
+ if(mkdtemp(temp_str) == NULL)
+ {
+ return false;
+ }
+
+ temp_dir = boost::filesystem::path(temp_str);
+
+ 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));
-
- if(err != noErr)
- throw 0;
+
+ bool replacingTarget = false;
-#else
+ 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
- // *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));
-
-#endif // 0 *HACK for DEV-11935
+ try
+ {
+ replacingTarget = getViewerDir( app_dir );
+
+ 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(app_dir))
+ {
+
+
+ if(isFSRefViewerBundle(app_dir.string()))
+ {
+ // This is the bundle we're looking for.
+ replacingTarget = true;
+ }
+ else
+ {
+ throw 0;
+ }
+ }
+
+ if ( !verifyDirectory(&app_dir, true) )
+ {
+ // We're so hosed.
+ llinfos << "Applications directory not found, giving up." << llendl;
+ throw 0;
+ }
// 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 ( !boost::filesystem::exists( dmg_path.parent_path() ) ) {
+ llinfos << "Path " << *dmgPath << " is not writeable. Aborting." << llendl;
+ 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 +613,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..f65b481cb6
--- /dev/null
+++ b/indra/mac_updater/mac_updater.h
@@ -0,0 +1,91 @@
+/**
+ * @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( signed 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( signed 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
+
+
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;
+}