diff --git a/.hgtags b/.hgtags
index 92ddad7884eee9d710328d5fc7168290dc6ea0e0..23ac1b88ca54d9b4ce68c59cb243484db0c8cd9b 100644
--- a/.hgtags
+++ b/.hgtags
@@ -72,35 +72,35 @@ b53a0576eec80614d7767ed72b40ed67aeff27c9 DRTVWR-38_2.5.2-release
 461c8c65b5c799ddfe365422f9be9c0095d91e7d 2.6.0-beta1-tip
 9e4641f4a7870c0f565a25a2971368d5a29516a1 2.6.0-beta2
 9e4641f4a7870c0f565a25a2971368d5a29516a1 DRTVWR-41_2.6.0-beta2
+42f32494bac475d0737799346f6831558ae8bf5d 2.6.0-release
+42f32494bac475d0737799346f6831558ae8bf5d DRTVWR-39_2.6.0-release
 c5bdef3aaa2744626aef3c217ce29e1900d357b3 2.6.1-beta1
 c5bdef3aaa2744626aef3c217ce29e1900d357b3 2.6.1-start
 c5bdef3aaa2744626aef3c217ce29e1900d357b3 DRTVWR-43_2.6.1-beta1
+c9182ed77d427c759cfacf49a7b71a2e20d522aa 2.6.1-release
+c9182ed77d427c759cfacf49a7b71a2e20d522aa DRTVWR-42_2.6.1-release
 56b2778c743c2a964d82e1caf11084d76a87de2c 2.6.2-start
 d1203046bb653b763f835b04d184646949d8dd5c 2.6.2-beta1
 d1203046bb653b763f835b04d184646949d8dd5c DRTVWR-45_2.6.2-beta1
-42f32494bac475d0737799346f6831558ae8bf5d 2.6.0-release
-42f32494bac475d0737799346f6831558ae8bf5d DRTVWR-39_2.6.0-release
-c9182ed77d427c759cfacf49a7b71a2e20d522aa 2.6.1-release
-c9182ed77d427c759cfacf49a7b71a2e20d522aa DRTVWR-42_2.6.1-release
+214180ad5714ce8392b82bbebcc92f4babd98300 2.6.2-release
+214180ad5714ce8392b82bbebcc92f4babd98300 DRTVWR-44_2.6.2-release
 52b2263ab28f0976c689fd0b76c55a9eb027cdbf end-of-develop.py
 ec32f1045e7c2644015245df3a9933620aa194b8 2.6.3-start
 d7fcefabdf32bb61a9ea6d6037c1bb26190a85bc 2.6.3-beta1
 d7fcefabdf32bb61a9ea6d6037c1bb26190a85bc DRTVWR-47_2.6.3-beta1
 0630e977504af5ea320c58d33cae4e1ddee793e9 2.6.3-beta2
 0630e977504af5ea320c58d33cae4e1ddee793e9 DRTVWR-48_2.6.3-beta2
+8f2da1701c81a62352df2b8d413d27fb2cade9a6 2.6.3-release
+8f2da1701c81a62352df2b8d413d27fb2cade9a6 DRTVWR-46_2.6.3-release
 3178e311da3a8739a85363665006ea3c4610cad4 dons-headless-hackathon-work
-214180ad5714ce8392b82bbebcc92f4babd98300 2.6.2-release
-214180ad5714ce8392b82bbebcc92f4babd98300 DRTVWR-44_2.6.2-release
 7db558aaa7c176f2022b3e9cfe38ac72f6d1fccd 2.6.5-beta1
 7db558aaa7c176f2022b3e9cfe38ac72f6d1fccd DRTVWR-50_2.6.5-beta1
-8f2da1701c81a62352df2b8d413d27fb2cade9a6 2.6.3-release
-8f2da1701c81a62352df2b8d413d27fb2cade9a6 DRTVWR-46_2.6.3-release
 800cefce8d364ffdd2f383cbecb91294da3ea424 2.6.6-start
 bb1075286b3b147b1dae2e3d6b2d56f04ff03f35 2.6.6-beta1
 bb1075286b3b147b1dae2e3d6b2d56f04ff03f35 DRTVWR-52_2.6.6-beta1
-5e349dbe9cc84ea5795af8aeb6d473a0af9d4953 2.6.8-start
 dac76a711da5f1489a01c1fa62ec97d99c25736d 2.6.6-release
 dac76a711da5f1489a01c1fa62ec97d99c25736d DRTVWR-51_2.6.6-release
+5e349dbe9cc84ea5795af8aeb6d473a0af9d4953 2.6.8-start
 beafa8a9bd1d1b670b7523d865204dc4a4b38eef 2.6.8-beta1
 beafa8a9bd1d1b670b7523d865204dc4a4b38eef DRTVWR-55_2.6.8-beta1
 be2000b946f8cb3de5f44b2d419287d4c48ec4eb 2.6.8-release
@@ -119,50 +119,50 @@ e67da2c6e3125966dd49eef98b36317afac1fcfe 2.6.9-start
 9f79a6ed8fdcd2f3dac33ea6b3236eeb278dccfe 2.7.2-start
 e0dc8b741eaa27dcdfbc9e956bb2579b954d15eb 2.7.2-beta1
 e0dc8b741eaa27dcdfbc9e956bb2579b954d15eb DRTVWR-63_2.7.2-beta1
-6a3e7e403bd19e45fdfc2fcc716867af3ab80861 2.7.3-start
 fe3a8e7973072ea62043c08b19b66626c1a720eb 2.7.1-release
 fe3a8e7973072ea62043c08b19b66626c1a720eb 2.7.2-release
 fe3a8e7973072ea62043c08b19b66626c1a720eb DRTVWR-60_2.7.1-release
 fe3a8e7973072ea62043c08b19b66626c1a720eb DRTVWR-62_2.7.2-release
+6a3e7e403bd19e45fdfc2fcc716867af3ab80861 2.7.3-start
 6af10678de4736222b2c3f7e010e984fb5b327de 2.7.4-start
 be963a4eef635542f9617d7f5fd22ba48fb71958 2.7.4-beta1
 be963a4eef635542f9617d7f5fd22ba48fb71958 DRTVWR-67_2.7.4-beta1
+057f319dd8eccdf63a54d99686c68cdcb31b6abc 2.7.4-release
+057f319dd8eccdf63a54d99686c68cdcb31b6abc DRTVWR-66_2.7.4-release
 19a498fa62570f352d7d246f17e3c81cc1d82d8b 2.7.5-start
 09984bfa6cae17e0f72d02b75c1b7393c65eecfc 2.7.5-beta1
 09984bfa6cae17e0f72d02b75c1b7393c65eecfc DRTVWR-69_2.7.5-beta1
+6866d9df6efbd441c66451debd376d21211de39c 2.7.5-release
+6866d9df6efbd441c66451debd376d21211de39c DRTVWR-68_2.7.5-release
 e1ed60913230dd64269a7f7fc52cbc6004f6d52c 2.8.0-beta1
 e1ed60913230dd64269a7f7fc52cbc6004f6d52c 2.8.0-start
 e1ed60913230dd64269a7f7fc52cbc6004f6d52c DRTVWR-71_2.8.0-beta1
-057f319dd8eccdf63a54d99686c68cdcb31b6abc 2.7.4-release
-057f319dd8eccdf63a54d99686c68cdcb31b6abc DRTVWR-66_2.7.4-release
-6866d9df6efbd441c66451debd376d21211de39c 2.7.5-release
-6866d9df6efbd441c66451debd376d21211de39c DRTVWR-68_2.7.5-release
+493d9127ee50e84ba08a736a65a23ca86f7a5b01 2.8.0-release
+493d9127ee50e84ba08a736a65a23ca86f7a5b01 DRTVWR-70_2.8.0-release
 502f6a5deca9365ddae57db4f1e30172668e171e 2.8.1-start
 2c7e459e0c883f8e406b932e41e60097e9ee077e 2.8.1-beta1
 2c7e459e0c883f8e406b932e41e60097e9ee077e DRTVWR-73_2.8.1-beta1
-493d9127ee50e84ba08a736a65a23ca86f7a5b01 2.8.0-release
-493d9127ee50e84ba08a736a65a23ca86f7a5b01 DRTVWR-70_2.8.0-release
-54bc7823ad4e3a436fef79710f685a7372bbf795 2.8.2-start
-ac0f1a132d35c02a58861d37cca75b0429ac9137 2.8.3-start
 29e93d7e19991011bd12b5748142b11a5dcb4370 2.8.1-release
 29e93d7e19991011bd12b5748142b11a5dcb4370 DRTVWR-72_2.8.1-release
 4780e3bd2b3042f91be3426151f28c30d199bb3b 2.8.1-hotfix
 4780e3bd2b3042f91be3426151f28c30d199bb3b DRTVWR-76_2.8.1-hotfix
+54bc7823ad4e3a436fef79710f685a7372bbf795 2.8.2-start
+ac0f1a132d35c02a58861d37cca75b0429ac9137 2.8.3-start
 599677276b227357140dda35bea4a2c18e2e67b5 2.8.3-beta1
 599677276b227357140dda35bea4a2c18e2e67b5 DRTVWR-75_2.8.3-beta1
+fb85792b84bf28428889c4cc966469d92e5dac4c 2.8.3-release
+fb85792b84bf28428889c4cc966469d92e5dac4c DRTVWR-74_2.8.3-release
 6b678ea52f90d5c14181661dcd2546e25bde483e 3.0.0-start
 b0be6ce3adfef3a014a2389d360539f8a86c5439 3.0.0-beta1
 b0be6ce3adfef3a014a2389d360539f8a86c5439 DRTVWR-78_3.0.0-beta1
-fb85792b84bf28428889c4cc966469d92e5dac4c 2.8.3-release
-fb85792b84bf28428889c4cc966469d92e5dac4c DRTVWR-74_2.8.3-release
+1778f26b6d0ae762dec3ca37140f66620f2485d9 3.0.0-release
+1778f26b6d0ae762dec3ca37140f66620f2485d9 DRTVWR-77_3.0.0-release
 82a2079ffcb57ecb1b3849cb41376b443e1eb912 3.0.1-start
 364fd63517fbacbbcb9129d096187171ba8c9e48 3.0.1-beta1
 364fd63517fbacbbcb9129d096187171ba8c9e48 DRTVWR-81_3.0.1-beta1
 f2412ecd6740803ea9452f1d17fd872e263a0df7 3.0.2-start
 42784bf50fa01974bada2a1af3892ee09c93fcda 3.0.2-beta1
 42784bf50fa01974bada2a1af3892ee09c93fcda DRTVWR-83_3.0.2-beta1
-1778f26b6d0ae762dec3ca37140f66620f2485d9 3.0.0-release
-1778f26b6d0ae762dec3ca37140f66620f2485d9 DRTVWR-77_3.0.0-release
 e5c9af2d7980a99a71650be3a0cf7b2b3c3b897e 3.0.2-beta2
 e5c9af2d7980a99a71650be3a0cf7b2b3c3b897e DRTVWR-86_3.0.2-beta2
 b95ddac176ac944efdc85cbee94ac2e1eab44c79 3.0.3-start
@@ -170,9 +170,9 @@ b95ddac176ac944efdc85cbee94ac2e1eab44c79 3.0.3-start
 6694f3f062aa45f64ab391d25a3eb3d5eb1b0871 DRTVWR-85_3.0.3-beta1
 61aa7974df089e8621fe9a4c69bcdefdb3cc208a 3.0.3-beta2
 61aa7974df089e8621fe9a4c69bcdefdb3cc208a DRTVWR-89_3.0.3-beta2
-586907287be581817b2422b5137971b22d54ea48 3.0.4-start
 0496d2f74043cf4e6058e76ac3db03d44cff42ce 3.0.3-release
 0496d2f74043cf4e6058e76ac3db03d44cff42ce DRTVWR-84_3.0.3-release
+586907287be581817b2422b5137971b22d54ea48 3.0.4-start
 92a3aa04775438226399b19deee12ac3b5a62838 3.0.5-start
 c7282e59f374ee904bd793c3c444455e3399b0c5 3.1.0-start
 2657fa785bbfac115852c41bd0adaff74c2ad5da 3.1.0-beta1
@@ -193,11 +193,11 @@ e440cd1dfbd128d7d5467019e497f7f803640ad6 DRTVWR-95_3.2.0-beta1
 c4911ec8cd81e676dfd2af438b3e065407a94a7a 3.2.1-start
 9e390d76807fa70d356b8716fb83b8ce42a629ef 3.2.1-beta1
 9e390d76807fa70d356b8716fb83b8ce42a629ef DRTVWR-100_3.2.1-beta1
+a8c7030d6845186fac7c188be4323a0e887b4184 3.2.1-release
+a8c7030d6845186fac7c188be4323a0e887b4184 DRTVWR-99_3.2.1-release
 40b46edba007d15d0059c80864b708b99c1da368 3.2.2-start
 523df3e67378541498d516d52af4402176a26bac 3.2.2-beta1
 523df3e67378541498d516d52af4402176a26bac DRTVWR-102_3.2.2-beta1
-a8c7030d6845186fac7c188be4323a0e887b4184 3.2.1-release
-a8c7030d6845186fac7c188be4323a0e887b4184 DRTVWR-99_3.2.1-release
 80f3e30d8aa4d8f674a48bd742aaa6d8e9eae0b5 3.2.3-start
 3fe994349fae64fc40874bb59db387131eb35a41 3.2.4-beta1
 3fe994349fae64fc40874bb59db387131eb35a41 3.2.4-start
@@ -248,57 +248,67 @@ bb9932a7a5fd00edf52d95f354e3b37ae6a942db DRTVWR-156
 6414ecdabc5d89515b08d1f872cf923ed3a5523a DRTVWR-148
 2a3965b3ad202df7ea25d2be689291bb14a1280e DRTVWR-155
 24a7281bef42bd4430ceb25db8b195449c2c7de3 DRTVWR-153
+a716684aa7c07c440b1de5815b8a1f3dd3fd8bfb DRTVWR-159
+9a78ac13f047056f788c4734dd91aebfe30970e3 DRTVWR-157
 5910f8063a7e1ddddf504c2f35ca831cc5e8f469 DRTVWR-160
 f0a174c2adb4bc39b16722a61d7eeb4f2a1d4843 3.3.3-beta1
 f0a174c2adb4bc39b16722a61d7eeb4f2a1d4843 DRTVWR-144
-2d6c0634b11e6f3df11002b8510a72a0433da00a DRTVWR-164
+089e5c84b2dece68f2b016c842ef9b5de4786842 DRTVWR-161
 600f3b3920d94de805ac6dc8bb6def9c069dd360 DRTVWR-162
+c08e2ac17a99973b2a94477659220b99b8847ae2 DRTVWR-163
+2d6c0634b11e6f3df11002b8510a72a0433da00a DRTVWR-164
 80b5e5e9775966d3839331ffa7a16a60f9d7c930 DRTVWR-165
 fdcc08a4f20ae9bb060f4693c8980d216534efdf 3.3.3-beta2
 af5f3e43e6e4424b1da19d9e16f6b853a7b822ed DRTVWR-169
 4b3c68199a86cabaa5d9466d7b0f7e141e901d7a 3.3.3-beta3
 6428242e124b523813bfaf4c45b3d422f0298c81 3.3.3-release
-a716684aa7c07c440b1de5815b8a1f3dd3fd8bfb DRTVWR-159
-9a78ac13f047056f788c4734dd91aebfe30970e3 DRTVWR-157
-089e5c84b2dece68f2b016c842ef9b5de4786842 DRTVWR-161
-c08e2ac17a99973b2a94477659220b99b8847ae2 DRTVWR-163
 b9d0170b62eb1c7c3adaa37a0b13a833e5e659f9 DRTVWR-171
 050e48759337249130f684b4a21080b683f61732 DRTVWR-168
 09ef7fd1b0781f33b8a3a9af6236b7bcb4831910 DRTVWR-170
 f87bfbe0b62d26f451d02a47c80ebef6b9168fc2 DRTVWR-158
 f91d003091a61937a044652c4c674447f7dcbb7a 3.3.4-beta1
+005dfe5c4c377207d065fb27858d2eb0b53b143a DRTVWR-167
 bce218b2b45b730b22cc51e4807aa8b571cadef3 DRTVWR-173
 cbea6356ce9cb0c313b6777f10c5c14783264fcc DRTVWR-174
 82b5330bc8b17d0d4b598832e9c5a92e90075682 3.3.4-beta2
 57d221de3df94f90b55204313c2cef044a3c0ae2 DRTVWR-176
 eb539c65e6ee26eea2bf373af2d0f4b52dc91289 DRTVWR-177
 a8057e1b9a1246b434a27405be35e030f7d28b0c 3.3.4-beta3
+888768f162d2c0a8de1dcc5fb9a08bd8bd120a6b DRTVWR-175
 4281aa899fb2cedb7a9ca7ce91c5c29d4aa69594 DRTVWR-180
 5c08e1d8edd871807153603b690e3ee9dbb548aa DRTVWR-183
 6c75f220b103db1420919c8b635fe53e2177f318 3.3.4-beta4
 9cd174d3a54d93d409a7c346a15b8bfb40fc58f4 DRTVWR-184
 ab2ffc547c8a8950ff187c4f6c95e5334fab597b 3.3.4-beta5
 28e100d0379a2b0710c57647a28fc5239d3d7b99 3.3.4-release
-005dfe5c4c377207d065fb27858d2eb0b53b143a DRTVWR-167
-888768f162d2c0a8de1dcc5fb9a08bd8bd120a6b DRTVWR-175
 a8b3eca451a9eaab59987efb0ab1c4217e3f2dcc DRTVWR-182
 1f27cdfdc54246484f8afbbe42ce48e954175cbd 3.4.0-beta1
-9ee9387789701d597130f879d9011a4958753862 DRTVWR-189
+81f6b745ef27f5915fd07f988fdec9944f2bb73e DRTVWR-186
 47f0d08ba7ade0a3905074009067c6d3df7e16ae DRTVWR-190
+cc953f00956be52cc64c30637bbeec310eea603f DRTVWR-181
+c04e68e1b0034fd0a20815ae24c77e5f8428e822 DRTVWR-188
+9ee9387789701d597130f879d9011a4958753862 DRTVWR-189
 421126293dcbde918e0da027ca0ab9deb5b4fbf2 DRTVWR-192
+4b2c52aecb7a75de31dbb12d9f5b9a251d8707be DRTVWR-191
 33a2fc7a910ae29ff8b4850316ed7fbff9f64d33 DRTVWR-195
 e9732c739c8a72a590216951505ea9c76a526a84 DRTVWR-193
+78ca0bbf43a92e8914d4cfa87d69a6717ef7d4cf DRTVWR-194
 7602f61c804a512764e349c034c02ddabeefebc4 DRTVWR-196
 ae5c83dd61d2d37c45f1d5b8bf2b036d87599f1b DRTVWR-198
 507bdfbd6bf844a511c1ffeda4baa80016ed1346 DRTVWR-197
 b1dbb1a83f48f93f6f878cff9e52d2cb635e145c 3.4.0-beta2
 37402e2b19af970d51b0a814d79892cc5647532b DRTVWR-200
 182a9bf30e81070361bb020a78003b1cf398e79c 3.4.0-beta3
+248f4acd92a706c79e842bc83d80baa7369c0c2e DRTVWR-203
 6dfb0fba782c9233dd95f24ec48146db0d3f210b DRTVWR-199
 7c9102fb998885621919f2474a002c35b583539b 3.3.4-release2
 7649a3dff5ec22d3727377e5f02efd0f421e4cb5 DRTVWR-201
 84fb70dfe3444e75a44fb4bee43e2fc8221cebdd 3.4.0-beta4
+de3be913f68813a9bac7d1c671fef96d1159bcd6 DRTVWR-202
 573e863be2f26d3687161def4b9fea9b7038dda8 3.4.0-beta5
+34dbbe2b00afe90352d3acf8290eb10ab90d1c8b oz-build-test-tag
+6ee71714935ffcd159db3d4f5800c1929aac54e1 DRTVWR-205
+7b22c612fc756e0ea63b10b163e81d107f85dbf8 DRTVWR-206
 8c9085066c78ed5f6c9379dc054c82a6fcdb1851 DRTVWR-207
 351eea5f9dc192fc5ddea3b02958de97677a0a12 3.3.4-release3
 af7b28e75bd5a629cd9e0dc46fb3f1757626f493 DRTVWR-212
@@ -309,6 +319,7 @@ ceed0b65a69f1eac20d523e0203320a32f9a3f3c DRTVWR-215
 97977c67245f52db20eb15f1918cc0f24778cabc 3.4.0-release
 5adb2b8f96c3cac88ad7c7d996d707f1b29df336 3.4.1-beta1
 b3f74858a1c8720c82d0978f3877a3fc8ba459ec 3.4.1-beta1a
+b61afe175b829c149d369524a4e974dfda99facf DRTVWR-219
 2b779f233ee6f38c89cb921650c773a96e63da92 DRTVWR-220
 0b9d95f4bfb6867cbf56eaec51633b0da2f1262d DRTVWR-221
 e6e553761829dc0270eaaa712b7cb0622535b076 3.4.1-beta3
@@ -333,17 +344,6 @@ baf97f06ae17223614c5e31aa42e71d87cff07fe DRTVWR-236
 b2f21e3442542283a80e7eaebae9f833e5a927b6 DRTVWR-237
 3f9be82de642d468c5fc272cb9d96b46b5498402 3.4.1-beta12
 e59ffd3fe0838ae6b09b242a6e9df71761b88f41 3.4.1-release
-81f6b745ef27f5915fd07f988fdec9944f2bb73e DRTVWR-186
-cc953f00956be52cc64c30637bbeec310eea603f DRTVWR-181
-c04e68e1b0034fd0a20815ae24c77e5f8428e822 DRTVWR-188
-4b2c52aecb7a75de31dbb12d9f5b9a251d8707be DRTVWR-191
-78ca0bbf43a92e8914d4cfa87d69a6717ef7d4cf DRTVWR-194
-248f4acd92a706c79e842bc83d80baa7369c0c2e DRTVWR-203
-de3be913f68813a9bac7d1c671fef96d1159bcd6 DRTVWR-202
-34dbbe2b00afe90352d3acf8290eb10ab90d1c8b oz-build-test-tag
-6ee71714935ffcd159db3d4f5800c1929aac54e1 DRTVWR-205
-7b22c612fc756e0ea63b10b163e81d107f85dbf8 DRTVWR-206
-b61afe175b829c149d369524a4e974dfda99facf DRTVWR-219
 32896d5e920ca9a29256ff3b747c2e99752aa5ae DRTVWR-217
 704bbae7b182a1f2811a47a054e680522966f54a 3.4.2-beta1
 288539fc0408ed4b69a99665de33bbbc2c3c08fe DRTVWR-216
@@ -417,3 +417,4 @@ b23419a2748483c98f3b84b630468a21c88feba5 DRTVWR-292
 0a5d409161ef2a89b28c9a741051dd2dedc707d6 DRTVWR-297
 852b69ef0b5fe6b13b69cc2217282cc64de6afab 3.4.5-beta5
 a49c715243a36a8a380504d14cb7416b3039c956 3.4.5-release
+e6b8a92acffd693cd1459e4212e3dff1050acf67 DRTVWR-278
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt
index 477192387171c70cb7c7ce62562a51c1b57847a5..ae69d0b843e9da400190e42b6271490e27c1dc2d 100644
--- a/indra/CMakeLists.txt
+++ b/indra/CMakeLists.txt
@@ -73,8 +73,7 @@ if (VIEWER)
     add_dependencies(viewer linux-crash-logger-strip-target)
   elseif (DARWIN)
     add_subdirectory(${VIEWER_PREFIX}mac_crash_logger)
-    add_subdirectory(${VIEWER_PREFIX}mac_updater)
-    add_dependencies(viewer mac-updater mac-crash-logger)
+    add_dependencies(viewer mac-crash-logger)
   elseif (WINDOWS)
     add_subdirectory(${VIEWER_PREFIX}win_crash_logger)
     # cmake EXISTS requires an absolute path, see indra/cmake/Variables.cmake
diff --git a/indra/mac_updater/AutoUpdater.nib/classes.nib b/indra/mac_updater/AutoUpdater.nib/classes.nib
deleted file mode 100644
index ea58db1189aa4aa4b8112941abcc5b3013db1001..0000000000000000000000000000000000000000
Binary files a/indra/mac_updater/AutoUpdater.nib/classes.nib and /dev/null differ
diff --git a/indra/mac_updater/AutoUpdater.nib/info.nib b/indra/mac_updater/AutoUpdater.nib/info.nib
deleted file mode 100644
index a49a92385bb55b21f4c5b736ecaadc09127d04ae..0000000000000000000000000000000000000000
Binary files a/indra/mac_updater/AutoUpdater.nib/info.nib and /dev/null differ
diff --git a/indra/mac_updater/AutoUpdater.nib/objects.xib b/indra/mac_updater/AutoUpdater.nib/objects.xib
deleted file mode 100644
index 310411b7113c96e28f07f317d9e22b0fcf531ef0..0000000000000000000000000000000000000000
--- 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/CMakeLists.txt b/indra/mac_updater/CMakeLists.txt
deleted file mode 100644
index 00dcedecaa90560272fabefa6d52e599db9aaff9..0000000000000000000000000000000000000000
--- a/indra/mac_updater/CMakeLists.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- cmake -*-
-
-project(mac_updater)
-
-include(00-Common)
-include(OpenSSL)
-include(CURL)
-include(CARes)
-include(LLCommon)
-include(LLVFS)
-include(Linking)
-
-include_directories(
-    ${LLCOMMON_INCLUDE_DIRS}
-    ${LLVFS_INCLUDE_DIRS}
-    ${CURL_INCLUDE_DIRS}
-    ${CARES_INCLUDE_DIRS}
-    )
-
-set(mac_updater_SOURCE_FILES
-    mac_updater.cpp
-    )
-
-set(mac_updater_HEADER_FILES
-    CMakeLists.txt
-    )
-
-set_source_files_properties(${mac_updater_HEADER_FILES}
-                            PROPERTIES HEADER_FILE_ONLY TRUE)
-
-list(APPEND mac_updater_SOURCE_FILES ${mac_updater_HEADER_FILES})
-
-
-set(mac_updater_RESOURCE_FILES
-  AutoUpdater.nib/
-  )
-set_source_files_properties(
-  ${mac_updater_RESOURCE_FILES}
-  PROPERTIES
-  HEADER_FILE_ONLY TRUE
-  )
-SOURCE_GROUP("Resources" FILES ${mac_updater_RESOURCE_FILES})
-list(APPEND mac_updater_SOURCE_FILES ${mac_updater_RESOURCE_FILES})
-
-add_executable(mac-updater
-  MACOSX_BUNDLE
-  ${mac_updater_SOURCE_FILES})
-
-set_target_properties(mac-updater
-  PROPERTIES
-  MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist
-  )
-
-target_link_libraries(mac-updater
-    ${LLVFS_LIBRARIES}
-    ${OPENSSL_LIBRARIES}
-    ${CRYPTO_LIBRARIES}
-    ${CURL_LIBRARIES}
-    ${CARES_LIBRARIES}
-    ${LLCOMMON_LIBRARIES}
-    )
-
-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
-  )
-
-ll_deploy_sharedlibs_command(mac-updater) 
diff --git a/indra/mac_updater/Info.plist b/indra/mac_updater/Info.plist
deleted file mode 100644
index bb27fddb03bc8002d3253b548c27382663de7826..0000000000000000000000000000000000000000
--- a/indra/mac_updater/Info.plist
+++ /dev/null
@@ -1,26 +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>CFBundleDevelopmentRegion</key>
-	<string>English</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>
-</dict>
-</plist>
diff --git a/indra/mac_updater/mac_updater.cpp b/indra/mac_updater/mac_updater.cpp
deleted file mode 100644
index aa45c5d23f25e685ba6f2a352e53cfa13aec0836..0000000000000000000000000000000000000000
--- a/indra/mac_updater/mac_updater.cpp
+++ /dev/null
@@ -1,1257 +0,0 @@
-/** 
- * @file mac_updater.cpp
- * @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 "linden_common.h"
-
-#include <boost/format.hpp>
-
-#include <libgen.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <curl/curl.h>
-#include <pthread.h>
-
-#include "llerror.h"
-#include "lltimer.h"
-#include "lldir.h"
-#include "llfile.h"
-
-#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*);
-
-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);
-}
-
-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
-	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))
-	{
-		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 << "Product name is: " << gProductName << llendl;
-		}
-		else
-		{
-			gProductName = "Second Life";
-		}
-		if (gBundleID)
-		{
-			llinfos << "Bundle ID is: " << gBundleID << llendl;
-		}
-		else
-		{
-			gBundleID = "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);
-	}
-
-	void *threadresult;
-
-	pthread_join(updatethread, &threadresult);
-
-	if(!gCancelled && (gFailure != noErr))
-	{
-		// 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)
-		{
-			// Create a install fail marker that can be used by the viewer to
-			// detect install problems.
-			std::ofstream stream(gMarkerPath);
-			if(stream) stream << -1;
-		}
-		exit(-1);
-	} else {
-		exit(0);
-	}
-
-	if(gWindow != NULL)
-	{
-		DisposeWindow(gWindow);
-	}
-	
-	if(nib != NULL)
-	{
-		DisposeNibReference(nib);
-	}
-	
-	return 0;
-}
-
-bool isDirWritable(FSRef &dir)
-{
-	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 */
-
-	err = FSRefMakePath(&dir, (UInt8*)temp, sizeof(temp));
-
-	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
-
-	return result;
-}
-
-static std::string HFSUniStr255_to_utf8str(const HFSUniStr255* src)
-{
-	llutf16string string16((U16*)&(src->unicode), src->length);
-	std::string result = utf16str_to_utf8str(string16);
-	return result;
-}
-
-int restoreObject(const char* aside, const char* target, const char* path, const char* object)
-{
-	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;
-}
-
-// Replace any mention of "Second Life" with the product name.
-void filterFile(const char* filename)
-{
-	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 */
-}
-
-static bool isFSRefViewerBundle(FSRef *targetRef)
-{
-	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;
-}
-
-// 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)
-{
-	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);
-
-				llinfos << "Considering \"" << name << "\"" << llendl;
-
-				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;
-						}
-
-					}
-				}
-			}
-		}
-		while(!err);
-		
-		llinfos << "closing the iterator" << llendl;
-		
-		FSCloseIterator(iterator);
-		
-		llinfos << "closed" << llendl;
-	}
-	
-	if(!err && !found)
-		err = fnfErr;
-		
-	return err;
-}
-
-void *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;
-
-#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));
-
-#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;
-		} 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;
-
-		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;
-	}
-
-	// Failures from here on out are all non-fatal and not reported.
-	sendProgress(0, 3, CFSTR("Cleaning up..."));
-
-	// Close disk image file if necessary
-	if(downloadFile != NULL)
-	{
-		llinfos << "Closing download file." << llendl;
-
-		fclose(downloadFile);
-		downloadFile = NULL;
-	}
-
-	sendProgress(1, 3);
-	// Unmount image
-	if(deviceNode[0] != 0)
-	{
-		llinfos << "Detaching disk image." << llendl;
-
-		snprintf(temp, sizeof(temp), "hdiutil detach '%s'", deviceNode);		
-		system(temp);		/* Flawfinder: ignore */
-	}
-
-	sendProgress(2, 3);
-
-	// 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;
-		}
-	}
-	
-	if(!gCancelled  && !gFailure && (target[0] != 0))
-	{
-		llinfos << "Touching application bundle." << llendl;
-
-		snprintf(temp, sizeof(temp), "touch '%s'", target);		
-		system(temp);		/* Flawfinder: ignore */
-
-		llinfos << "Launching updated application." << llendl;
-
-		snprintf(temp, sizeof(temp), "open '%s'", target);		
-		system(temp);		/* Flawfinder: ignore */
-	}
-
-	sendDone();
-	
-	return(NULL);
-}
diff --git a/indra/mac_updater/mac_updater.h b/indra/mac_updater/mac_updater.h
deleted file mode 100644
index f65b481cb68b44e6de08f5b58617ce5460203a28..0000000000000000000000000000000000000000
--- a/indra/mac_updater/mac_updater.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/** 
- * @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/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 002c826a30d2428f68ae5867744df75d835ee09f..0b21d6c8fb995a42378391fc38e0be1746d52746 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -2000,7 +2000,7 @@ if (DARWIN)
       generate_viewer_version
     )
 
-  add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit mac-updater mac-crash-logger)
+  add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit mac-crash-logger)
   
   if (ENABLE_SIGNING)
       set(SIGNING_SETTING "--signature=${SIGNING_IDENTITY}")
@@ -2056,12 +2056,11 @@ if (PACKAGE)
     # *TODO: Generate these search dirs in the cmake files related to each binary.
     list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/llplugin/slplugin/${CMAKE_CFG_INTDIR}")
     list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/mac_crash_logger/${CMAKE_CFG_INTDIR}")
-    list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/mac_updater/${CMAKE_CFG_INTDIR}")
     list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/media_plugins/gstreamer010/${CMAKE_CFG_INTDIR}")
     list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/media_plugins/quicktime/${CMAKE_CFG_INTDIR}")
     list(APPEND SYMBOL_SEARCH_DIRS "${CMAKE_BINARY_DIR}/media_plugins/webkit/${CMAKE_CFG_INTDIR}")
     set(VIEWER_SYMBOL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/secondlife-symbols-darwin.tar.bz2")
-    set(VIEWER_EXE_GLOBS "'Second Life' SLPlugin mac-updater mac-crash-logger")
+    set(VIEWER_EXE_GLOBS "'Second Life' SLPlugin mac-crash-logger")
     set(VIEWER_LIB_GLOB "*.dylib")
   endif (DARWIN)
   if (LINUX)
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index b3c0e650ece63f34d4218827ea53e71897da3265..9f06dca17ae095cc068dc7e93498dc8c273dea67 100644
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -635,7 +635,9 @@ def construct(self):
             self.path("../packages/lib/release/libndofdev.dylib", dst="Resources/libndofdev.dylib")
             self.path("../packages/lib/release/libhunspell-1.3.0.dylib", dst="Resources/libhunspell-1.3.0.dylib")
 
-            self.path("../viewer_components/updater/scripts/darwin/update_install", "MacOS/update_install")
+            if self.prefix(dst="MacOS"):
+                self.path2basename("../viewer_components/updater/scripts/darwin", "*.py")
+                self.end_prefix()
 
             # most everything goes in the Resources directory
             if self.prefix(src="", dst="Resources"):
@@ -731,7 +733,6 @@ def path_optional(src, dst):
                 
                 # our apps
                 for app_bld_dir, app in (("mac_crash_logger", "mac-crash-logger.app"),
-                                         ("mac_updater", "mac-updater.app"),
                                          # plugin launcher
                                          (os.path.join("llplugin", "slplugin"), "SLPlugin.app"),
                                          ):
@@ -777,7 +778,7 @@ def path_optional(src, dst):
     def copy_finish(self):
         # Force executable permissions to be set for scripts
         # see CHOP-223 and http://mercurial.selenic.com/bts/issue1802
-        for script in 'Contents/MacOS/update_install',:
+        for script in 'Contents/MacOS/update_install.py',:
             self.run_command("chmod +x %r" % os.path.join(self.get_dst_prefix(), script))
 
     def package_finish(self):
diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp
index 324b051b217d4ad6542fdbae7708b7eb862bcf01..c1e57122eeda4c386ac82c0483dbc1374d8d3587 100644
--- a/indra/viewer_components/updater/llupdaterservice.cpp
+++ b/indra/viewer_components/updater/llupdaterservice.cpp
@@ -65,6 +65,8 @@ namespace
 	{
 #ifdef LL_WINDOWS
 		std::string scriptFile = "update_install.bat";
+#elif LL_DARWIN
+		std::string scriptFile = "update_install.py";
 #else
 		std::string scriptFile = "update_install";
 #endif
@@ -76,6 +78,8 @@ namespace
 #ifdef LL_WINDOWS
 		return LL_COPY_INSTALL_SCRIPT_TO_TEMP;
 #else
+		// This is important on Mac because update_install.py looks at its own
+		// script pathname to discover the viewer app bundle to update.
 		return LL_RUN_INSTALL_SCRIPT_IN_PLACE;
 #endif
 	};
diff --git a/indra/viewer_components/updater/scripts/darwin/janitor.py b/indra/viewer_components/updater/scripts/darwin/janitor.py
new file mode 100644
index 0000000000000000000000000000000000000000..cdf33df731b93baeebdc50aba63274c9a9c1f7c3
--- /dev/null
+++ b/indra/viewer_components/updater/scripts/darwin/janitor.py
@@ -0,0 +1,133 @@
+#!/usr/bin/python
+"""\
+@file   janitor.py
+@author Nat Goodspeed
+@date   2011-09-14
+@brief  Janitor class to clean up arbitrary resources
+
+2013-01-04 cloned from vita because it's exactly what update_install.py needs.
+
+$LicenseInfo:firstyear=2011&license=viewerlgpl$
+Copyright (c) 2011, Linden Research, Inc.
+$/LicenseInfo$
+"""
+
+import sys
+import functools
+import itertools
+
+class Janitor(object):
+    """
+    Usage:
+
+    Basic:
+    self.janitor = Janitor(sys.stdout) # report cleanup actions on stdout
+    ...
+    self.janitor.later(os.remove, some_temp_file)
+    self.janitor.later(os.remove, some_other_file)
+    ...
+    self.janitor.cleanup()          # perform cleanup actions
+
+    Context Manager:
+    with Janitor() as janitor:      # clean up quietly
+        ...
+        janitor.later(shutil.rmtree, some_temp_directory)
+        ...
+    # exiting 'with' block performs cleanup
+
+    Test Class:
+    class TestMySoftware(unittest.TestCase, Janitor):
+        def __init__(self):
+            Janitor.__init__(self)  # quiet cleanup
+            ...
+
+        def setUp(self):
+            ...
+            self.later(os.rename, saved_file, original_location)
+            ...
+
+        def tearDown(self):
+            Janitor.tearDown(self)  # calls cleanup()
+            ...
+            # Or, if you have no other tearDown() logic for
+            # TestMySoftware, you can omit the TestMySoftware.tearDown()
+            # def entirely and let it inherit Janitor.tearDown().
+    """
+    def __init__(self, stream=None):
+        """
+        If you pass stream= (e.g.) sys.stdout or sys.stderr, Janitor will
+        report its cleanup operations as it performs them. If you don't, it
+        will perform them quietly -- unless one or more of the actions throws
+        an exception, in which case you'll get output on stderr.
+        """
+        self.stream   = stream
+        self.cleanups = []
+
+    def later(self, func, *args, **kwds):
+        """
+        Pass the callable you want to call at cleanup() time, plus any
+        positional or keyword args you want to pass it.
+        """
+        # Get a name string for 'func'
+        try:
+            # A free function has a __name__
+            name = func.__name__
+        except AttributeError:
+            try:
+                # A class object (even builtin objects like ints!) support
+                # __class__.__name__
+                name = func.__class__.__name__
+            except AttributeError:
+                # Shrug! Just use repr() to get a string describing this func.
+                name = repr(func)
+        # Construct a description of this operation in Python syntax from
+        # args, kwds.
+        desc = "%s(%s)" % \
+               (name, ", ".join(itertools.chain((repr(a) for a in args),
+                                                ("%s=%r" % (k, v) for (k, v) in kwds.iteritems()))))
+        # Use functools.partial() to bind passed args and keywords to the
+        # passed func so we get a nullary callable that does what caller
+        # wants.
+        bound = functools.partial(func, *args, **kwds)
+        self.cleanups.append((desc, bound))
+
+    def cleanup(self):
+        """
+        Perform all the actions saved with later() calls.
+        """
+        # Typically one allocates resource A, then allocates resource B that
+        # depends on it. In such a scenario it's appropriate to delete B
+        # before A -- so perform cleanup actions in reverse order. (This is
+        # the same strategy used by atexit().)
+        while self.cleanups:
+            # Until our list is empty, pop the last pair.
+            desc, bound = self.cleanups.pop(-1)
+
+            # If requested, report the action.
+            if self.stream is not None:
+                print >>self.stream, desc
+
+            try:
+                # Call the bound callable
+                bound()
+            except Exception, err:
+                # This is cleanup. Report the problem but continue.
+                print >>(self.stream or sys.stderr), "Calling %s\nraised  %s: %s" % \
+                      (desc, err.__class__.__name__, err)
+
+    def tearDown(self):
+        """
+        If a unittest.TestCase subclass (or a nose test class) adds Janitor as
+        one of its base classes, and has no other tearDown() logic, let it
+        inherit Janitor.tearDown().
+        """
+        self.cleanup()
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, tb):
+        # Perform cleanup no matter how we exit this 'with' statement
+        self.cleanup()
+        # Propagate any exception from the 'with' statement, don't swallow it
+        return False
diff --git a/indra/viewer_components/updater/scripts/darwin/messageframe.py b/indra/viewer_components/updater/scripts/darwin/messageframe.py
new file mode 100644
index 0000000000000000000000000000000000000000..8f58848882a069bf4caeed0c34c41df11f36dcda
--- /dev/null
+++ b/indra/viewer_components/updater/scripts/darwin/messageframe.py
@@ -0,0 +1,66 @@
+#!/usr/bin/python
+"""\
+@file   messageframe.py
+@author Nat Goodspeed
+@date   2013-01-03
+@brief  Define MessageFrame class for popping up messages from a command-line
+        script.
+
+$LicenseInfo:firstyear=2013&license=viewerlgpl$
+Copyright (c) 2013, Linden Research, Inc.
+$/LicenseInfo$
+"""
+
+import Tkinter as tk
+import os
+
+# Tricky way to obtain the filename of the main script (default title string)
+import __main__
+
+# This class is intended for displaying messages from a command-line script.
+# Getting the base class right took a bit of trial and error.
+# If you derive from tk.Frame, the destroy() method doesn't actually close it.
+# If you derive from tk.Toplevel, it pops up a separate Tk frame too. destroy()
+# closes this frame, but not that one.
+# Deriving from tk.Tk appears to do the right thing.
+class MessageFrame(tk.Tk):
+    def __init__(self, text="", title=os.path.splitext(os.path.basename(__main__.__file__))[0],
+                 width=320, height=120):
+        tk.Tk.__init__(self)
+        self.grid()
+        self.title(title)
+        self.var = tk.StringVar()
+        self.var.set(text)
+        self.msg = tk.Label(self, textvariable=self.var)
+        self.msg.grid()
+        # from http://stackoverflow.com/questions/3352918/how-to-center-a-window-on-the-screen-in-tkinter :
+        self.update_idletasks()
+
+        # The constants below are to adjust for typical overhead from the
+        # frame borders.
+        xp = (self.winfo_screenwidth()  / 2) - (width  / 2) - 8
+        yp = (self.winfo_screenheight() / 2) - (height / 2) - 20
+        self.geometry('{0}x{1}+{2}+{3}'.format(width, height, xp, yp))
+        self.update()
+
+    def set(self, text):
+        self.var.set(text)
+        self.update()
+
+if __name__ == "__main__":
+    # When run as a script, just test the MessageFrame.
+    import sys
+    import time
+
+    frame = MessageFrame("something in the way she moves....")
+    time.sleep(3)
+    frame.set("smaller")
+    time.sleep(3)
+    frame.set("""this has
+several
+lines""")
+    time.sleep(3)
+    frame.destroy()
+    print "Destroyed!"
+    sys.stdout.flush()
+    time.sleep(3)
diff --git a/indra/viewer_components/updater/scripts/darwin/update_install b/indra/viewer_components/updater/scripts/darwin/update_install
deleted file mode 100644
index e7f36dc5a360faee7dbc2a4d4c5e6f9b80130f33..0000000000000000000000000000000000000000
--- a/indra/viewer_components/updater/scripts/darwin/update_install
+++ /dev/null
@@ -1,10 +0,0 @@
-#! /bin/bash
-
-#
-# The first argument contains the path to the installer app.  The second a path
-# to a marker file which should be created if the installer fails.q
-#
-
-cd "$(dirname "$0")"
-(../Resources/mac-updater.app/Contents/MacOS/mac-updater -dmg "$1" -name "Second Life Viewer"; if [ $? -ne 0 ]; then echo $3 >> "$2"; fi;) &
-exit 0
diff --git a/indra/viewer_components/updater/scripts/darwin/update_install.py b/indra/viewer_components/updater/scripts/darwin/update_install.py
new file mode 100755
index 0000000000000000000000000000000000000000..2fc6fcdb29e1b04a33feb56014cc96af7b84d131
--- /dev/null
+++ b/indra/viewer_components/updater/scripts/darwin/update_install.py
@@ -0,0 +1,373 @@
+#!/usr/bin/python
+"""\
+@file   update_install.py
+@author Nat Goodspeed
+@date   2012-12-20
+@brief  Update the containing Second Life application bundle to the version in
+        the specified disk image file.
+
+        This Python implementation is derived from the previous mac-updater
+        application, a funky mix of C++, classic C and Objective-C.
+
+$LicenseInfo:firstyear=2012&license=viewerlgpl$
+Copyright (c) 2012, Linden Research, Inc.
+$/LicenseInfo$
+"""
+
+import os
+import sys
+import cgitb
+import errno
+import glob
+import plistlib
+import re
+import shutil
+import subprocess
+import tempfile
+import time
+from janitor import Janitor
+from messageframe import MessageFrame
+import Tkinter, tkMessageBox
+
+TITLE = "Second Life Viewer Updater"
+# Magic bundle identifier used by all Second Life viewer bundles
+BUNDLE_IDENTIFIER = "com.secondlife.indra.viewer"
+
+# Global handle to the MessageFrame so we can update message
+FRAME = None
+# Global handle to logfile, once it's open
+LOGF  = None
+
+# ****************************************************************************
+#   Logging and messaging
+#
+#   This script is normally run implicitly by the old viewer to update to the
+#   new viewer. Its UI consists of a MessageFrame and possibly a Tk error box.
+#   Log details to updater.log -- especially uncaught exceptions!
+# ****************************************************************************
+def log(message):
+    """write message only to LOGF (also called by status() and fail())"""
+    # If we don't even have LOGF open yet, at least write to Console log
+    logf = LOGF or sys.stderr
+    logf.writelines((time.strftime("%Y-%m-%dT%H:%M:%SZ ", time.gmtime()), message, '\n'))
+    logf.flush()
+
+def status(message):
+    """display and log normal progress message"""
+    log(message)
+
+    global FRAME
+    if not FRAME:
+        FRAME = MessageFrame(message, TITLE)
+    else:
+        FRAME.set(message)
+
+def fail(message):
+    """log message, produce error box, then terminate with nonzero rc"""
+    log(message)
+
+    # If we haven't yet called status() (we don't yet have a FRAME), perform a
+    # bit of trickery to bypass the spurious "main window" that Tkinter would
+    # otherwise pop up if the first call is showerror().
+    if not FRAME:
+        root = Tkinter.Tk()
+        root.withdraw()
+
+    # If we do have a LOGF available, mention it in the error box.
+    if LOGF:
+        message = "%s\n(Updater log in %s)" % (message, LOGF.name)
+
+    # We explicitly specify the WARNING icon because, at least on the Tkinter
+    # bundled with the system-default Python 2.7 on Mac OS X 10.7.4, the
+    # ERROR, QUESTION and INFO icons are all the silly Tk rocket ship. At
+    # least WARNING has an exclamation in a yellow triangle, even though
+    # overlaid by a smaller image of the rocket ship.
+    tkMessageBox.showerror(TITLE,
+"""An error occurred while updating Second Life:
+%s
+Please download the latest viewer from www.secondlife.com.""" % message,
+                           icon=tkMessageBox.WARNING)
+    sys.exit(1)
+
+def exception(err):
+    """call fail() with an exception instance"""
+    fail("%s exception: %s" % (err.__class__.__name__, str(err)))
+
+def excepthook(type, value, traceback):
+    """
+    Store this hook function into sys.excepthook until we have a logfile.
+    """
+    # At least in older Python versions, it could be tricky to produce a
+    # string from 'type' and 'value'. For instance, an OSError exception would
+    # pass type=OSError and value=some_tuple. Empirically, this funky
+    # expression seems to work.
+    exception(type(*value))
+sys.excepthook = excepthook
+
+class ExceptHook(object):
+    """
+    Store an instance of this class into sys.excepthook once we have a logfile
+    open.
+    """
+    def __init__(self, logfile):
+        # There's no magic to the cgitb.enable() function -- it merely stores
+        # an instance of cgitb.Hook into sys.excepthook, passing enable()'s
+        # params into Hook.__init__(). Sadly, enable() doesn't forward all its
+        # params using (*args, **kwds) syntax -- another story. But the point
+        # is that all the goodness is in the cgitb.Hook class. Capture an
+        # instance.
+        self.hook = cgitb.Hook(file=logfile, format="text")
+
+    def __call__(self, type, value, traceback):
+        # produce nice text traceback to logfile
+        self.hook(type, value, traceback)
+        # Now display an error box.
+        excepthook(type, value, traceback)
+
+def write_marker(markerfile, markertext):
+    log("writing %r to %s" % (markertext, markerfile))
+    try:
+        with open(markerfile, "w") as markerf:
+            markerf.write(markertext)
+    except IOError, err:
+        # write_marker() is invoked by fail(), and fail() is invoked by other
+        # error-handling functions. If we try to invoke any of those, we'll
+        # get infinite recursion. If for any reason we can't write markerfile,
+        # try to log it -- otherwise shrug.
+        log("%s exception: %s" % (err.__class__.__name__, err))
+
+# ****************************************************************************
+#   Main script logic
+# ****************************************************************************
+def main(dmgfile, markerfile, markertext):
+    # Should we fail, we're supposed to write 'markertext' to 'markerfile'.
+    # Wrap the fail() function so we do that.
+    global fail
+    oldfail = fail
+    def fail(message):
+        write_marker(markerfile, markertext)
+        oldfail(message)
+
+    try:
+        # Starting with the Cocoafied viewer, we'll find viewer logs in
+        # ~/Library/Application Support/$CFBundleIdentifier/logs rather than in
+        # ~/Library/Application Support/SecondLife/logs as before. This could be
+        # obnoxious -- but we Happen To Know that markerfile is a path specified
+        # within the viewer's logs directory. Use that.
+        logsdir = os.path.dirname(markerfile)
+
+        # Move the old updater.log file out of the way
+        logname = os.path.join(logsdir, "updater.log")
+        try:
+            os.rename(logname, logname + ".old")
+        except OSError, err:
+            # Nonexistence is okay. Anything else, not so much.
+            if err.errno != errno.ENOENT:
+                raise
+
+        # Open new updater.log.
+        global LOGF
+        LOGF = open(logname, "w")
+
+        # Now that LOGF is in fact open for business, use it to log any further
+        # uncaught exceptions.
+        sys.excepthook = ExceptHook(LOGF)
+
+        # log how this script was invoked
+        log(' '.join(repr(arg) for arg in sys.argv))
+
+        # prepare for other cleanup
+        with Janitor(LOGF) as janitor:
+
+            # Try to derive the name of the running viewer app bundle from our
+            # own pathname. (Hopefully the old viewer won't copy this script
+            # to a temp dir before running!)
+            # Somewhat peculiarly, this script is currently packaged in
+            # Appname.app/Contents/MacOS with the viewer executable. But even
+            # if we decide to move it to Appname.app/Contents/Resources, we'll
+            # still find Appname.app two levels up from dirname(__file__).
+            appdir = os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                                  os.pardir, os.pardir))
+            if not appdir.endswith(".app"):
+                # This can happen if either this script has been copied before
+                # being executed, or if it's in an unexpected place in the app
+                # bundle.
+                fail(appdir + " is not an application directory")
+
+            # We need to install into appdir's parent directory -- can we?
+            installdir = os.path.abspath(os.path.join(appdir, os.pardir))
+            if not os.access(installdir, os.W_OK):
+                fail("Can't modify " + installdir)
+
+            # invent a temporary directory
+            tempdir = tempfile.mkdtemp()
+            log("created " + tempdir)
+            # clean it up when we leave
+            janitor.later(shutil.rmtree, tempdir)
+
+            status("Mounting image...")
+
+            mntdir = os.path.join(tempdir, "mnt")
+            log("mkdir " + mntdir)
+            os.mkdir(mntdir)
+            command = ["hdiutil", "attach", dmgfile, "-mountpoint", mntdir]
+            log(' '.join(command))
+            # Instantiating subprocess.Popen launches a child process with the
+            # specified command line. stdout=PIPE passes a pipe to its stdout.
+            hdiutil = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=LOGF)
+            # Popen.communicate() reads that pipe until the child process
+            # terminates, returning (stdout, stderr) output. Select just stdout.
+            hdiutil_out = hdiutil.communicate()[0]
+            if hdiutil.returncode != 0:
+                fail("Couldn't mount " + dmgfile)
+            # hdiutil should report the devnode. Find that.
+            found = re.search(r"/dev/[^ ]*\b", hdiutil_out)
+            if not found:
+                # If we don't spot the devnode, log it and continue -- we only
+                # use it to detach it. Don't fail the whole update if we can't
+                # clean up properly.
+                log("Couldn't spot devnode in hdiutil output:\n" + hdiutil_out)
+            else:
+                # If we do spot the devnode, detach it when done.
+                janitor.later(subprocess.call, ["hdiutil", "detach", found.group(0)],
+                              stdout=LOGF, stderr=subprocess.STDOUT)
+
+            status("Searching for app bundle...")
+
+            for candidate in glob.glob(os.path.join(mntdir, "*.app")):
+                log("Considering " + candidate)
+                try:
+                    # By convention, a valid Mac app bundle has a
+                    # Contents/Info.plist file containing at least
+                    # CFBundleIdentifier.
+                    CFBundleIdentifier = \
+                        plistlib.readPlist(os.path.join(candidate, "Contents",
+                                                        "Info.plist"))["CFBundleIdentifier"]
+                except Exception, err:
+                    # might be IOError, xml.parsers.expat.ExpatError, KeyError
+                    # Any of these means it's not a valid app bundle. Instead
+                    # of aborting, just skip this candidate and continue.
+                    log("%s not a valid app bundle: %s: %s" %
+                        (candidate, err.__class__.__name__, err))
+                    continue
+
+                if CFBundleIdentifier == BUNDLE_IDENTIFIER:
+                    break
+
+                log("unrecognized CFBundleIdentifier: " + CFBundleIdentifier)
+
+            else:
+                fail("Could not find Second Life viewer in " + dmgfile)
+
+            # Here 'candidate' is the new viewer to install
+            log("Found " + candidate)
+
+            # This logic was changed to make Mac updates behave more like
+            # Windows. Most of the time, the user doesn't change the name of
+            # the app bundle on our .dmg installer (e.g. "Second Life Beta
+            # Viewer.app"). Most of the time, the version manager directs a
+            # given viewer to update to another .dmg containing an app bundle
+            # with THE SAME name. In that case, everything behaves as usual.
+
+            # The case that was changed is when the version manager offers (or
+            # mandates) an update to a .dmg containing a different app bundle
+            # name. This can happen, for instance, to a user who's downloaded
+            # a "project beta" viewer, and the project subsequently publishes
+            # a Release Candidate viewer. Say the project beta's app bundle
+            # name is something like "Second Life Beta Neato.app". Anyone
+            # launching that viewer will be offered an update to the
+            # corresponding Release Candidate viewer -- which will be built as
+            # a release viewer, with app bundle name "Second Life Viewer.app".
+
+            # On Windows, we run the NSIS installer, which will update/replace
+            # the embedded install directory name, e.g. Second Life Viewer.
+            # But the Mac installer used to locate the app bundle name in the
+            # mounted .dmg file, then ignore that name, copying its contents
+            # into the app bundle directory of the running viewer. That is,
+            # we'd install the Release Candidate from the .dmg's "Second
+            # Life.app" into "/Applications/Second Life Beta Neato.app". This
+            # is undesired behavior.
+
+            # Instead, having found the app bundle name on the mounted .dmg,
+            # we try to install that app bundle name into the parent directory
+            # of the running app bundle.
+
+            # Are we installing a different app bundle name? If so, call it
+            # out, both in the log and for the user -- this is an odd case.
+            # (Presumably they've already agreed to a similar notification in
+            # the viewer before the viewer launched this script, but still.)
+            bundlename = os.path.basename(candidate)
+            if os.path.basename(appdir) == bundlename:
+                # updating the running app bundle, which we KNOW exists
+                appexists = True
+            else:
+                # installing some other app bundle
+                newapp = os.path.join(installdir, bundlename)
+                appexists = os.path.exists(newapp)
+                message = "Note: %s %s %s" % \
+                          (appdir, "updating" if appexists else "installing new", newapp)
+                status(message)
+                # okay, we have no further need of the name of the running app
+                # bundle.
+                appdir = newapp
+
+            status("Preparing to copy files...")
+
+            if appexists:
+                # move old viewer to temp location in case copy from .dmg fails
+                aside = os.path.join(tempdir, os.path.basename(appdir))
+                log("mv %r %r" % (appdir, aside))
+                # Use shutil.move() instead of os.rename(). move() first tries
+                # os.rename(), but falls back to shutil.copytree() if the dest is
+                # on a different filesystem.
+                shutil.move(appdir, aside)
+
+            status("Copying files...")
+
+            # shutil.copytree()'s target must not already exist. But we just
+            # moved appdir out of the way.
+            log("cp -p %r %r" % (candidate, appdir))
+            try:
+                # The viewer app bundle does include internal symlinks. Keep them
+                # as symlinks.
+                shutil.copytree(candidate, appdir, symlinks=True)
+            except Exception, err:
+                # copy failed -- try to restore previous viewer before crumping
+                type, value, traceback = sys.exc_info()
+                if appexists:
+                    log("exception response: mv %r %r" % (aside, appdir))
+                    shutil.move(aside, appdir)
+                # let our previously-set sys.excepthook handle this
+                raise type, value, traceback
+
+            status("Cleaning up...")
+
+            log("touch " + appdir)
+            os.utime(appdir, None)      # set to current time
+
+            command = ["open", appdir]
+            log(' '.join(command))
+            subprocess.check_call(command, stdout=LOGF, stderr=subprocess.STDOUT)
+
+    except Exception, err:
+        # Because we carefully set sys.excepthook -- and even modify it to log
+        # the problem once we have our log file open -- you might think we
+        # could just let exceptions propagate. But when we do that, on
+        # exception in this block, we FIRST restore the no-side-effects fail()
+        # and THEN implicitly call sys.excepthook(), which calls the (no-side-
+        # effects) fail(). Explicitly call sys.excepthook() BEFORE restoring
+        # fail(). Only then do we get the enriched fail() behavior.
+        sys.excepthook(*sys.exc_info())
+
+    finally:
+        # When we leave main() -- for whatever reason -- reset fail() the way
+        # it was before, because the bound markerfile, markertext params
+        # passed to this main() call are no longer applicable.
+        fail = oldfail
+
+if __name__ == "__main__":
+    # We expect this script to be invoked with:
+    # - the pathname to the .dmg we intend to install;
+    # - the pathname to an update-error marker file to create on failure;
+    # - the content to write into the marker file.
+    main(*sys.argv[1:])