diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d2a4c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +teleport.xcodeproj/xcuserdata +teleport.xcodeproj/project.xcworkspace/xcuserdata +teleport.xcodeproj/project.xcworkspace/xcshareddata diff --git a/Base.lproj/teleportPref.xib b/Base.lproj/teleportPref.xib new file mode 100644 index 0000000..8f403a6 --- /dev/null +++ b/Base.lproj/teleportPref.xib @@ -0,0 +1,758 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + © 2003-2015 abyssoft +Based on an idea by Dave Sag and Jeremy Quinn. +Special thanks to Manu02, Julien and Paul. + + + + + + + + + + + + + + + + + + + + + + + + + + + + https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=jul%40mac%2ecom&item_name=teleport&no_shipping=0&no_note=0&tax=0&currency_code=USD&charset=UTF%2d8&charset=UTF%2d8 + + + + + + + + + + + + + prefs.allowControl + prefs.sharePasteboard + prefs.requireKey + prefs.delayedSwitch + prefs.showStatusItem + prefs.limitPasteboardSize + prefs.maxPasterboardSize + active + prefs.autocheckVersion + prefs.rejectAuthenticationRequests + prefs.enableEncryption + prefs.switchKeyTag + prefs.copyFiles + prefs.requirePasteboardKey + prefs.pasteboardKeyTag + prefs.trustRequestBehavior + prefs.wakeOnLAN + prefs.switchDelay + localHost.supportDragNDrop + prefs.maxPasteboardSize + prefs.hideControlBezel + prefs.switchWithDoubleTap + prefs.playSwitchSound + + + + + + + + numberOfSelectedRows + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Czech.lproj/InfoPlist.strings b/Czech.lproj/InfoPlist.strings new file mode 100644 index 0000000..8eaceaf Binary files /dev/null and b/Czech.lproj/InfoPlist.strings differ diff --git a/Czech.lproj/Localizable.strings b/Czech.lproj/Localizable.strings new file mode 100644 index 0000000..04e31ce Binary files /dev/null and b/Czech.lproj/Localizable.strings differ diff --git a/Czech.lproj/MainMenuApp.nib/classes.nib b/Czech.lproj/MainMenuApp.nib/classes.nib new file mode 100644 index 0000000..8eab51a --- /dev/null +++ b/Czech.lproj/MainMenuApp.nib/classes.nib @@ -0,0 +1,38 @@ + + + + + IBClasses + + + CLASS + TPPrefPaneAppController + LANGUAGE + ObjC + OUTLETS + + window + NSWindow + + SUPERCLASS + NSObject + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Czech.lproj/MainMenuApp.nib/info.nib b/Czech.lproj/MainMenuApp.nib/info.nib new file mode 100644 index 0000000..21ddea8 --- /dev/null +++ b/Czech.lproj/MainMenuApp.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 629 + IBLastKnownRelativeProjectPath + ../teleport.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 265 + + IBSystem Version + 9A569 + targetFramework + IBCocoaFramework + + diff --git a/Czech.lproj/MainMenuApp.nib/keyedobjects.nib b/Czech.lproj/MainMenuApp.nib/keyedobjects.nib new file mode 100644 index 0000000..0d5ee09 Binary files /dev/null and b/Czech.lproj/MainMenuApp.nib/keyedobjects.nib differ diff --git a/Czech.lproj/teleportPref.xib b/Czech.lproj/teleportPref.xib new file mode 100644 index 0000000..0f19a51 --- /dev/null +++ b/Czech.lproj/teleportPref.xib @@ -0,0 +1,5920 @@ + + + + 1050 + 9C31 + 629 + 949.26 + 352.00 + + YES + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + TPPreferencePane + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{516, 450}, {595, 429}} + 1081606144 + PDwgZG8gbm90IGxvY2FsaXplID4+A + NSWindow + + View + + + + 256 + + YES + + + 274 + + YES + + + 268 + {{176, 387}, {130, 18}} + + + YES + + 604110336 + 0 + U2TDrWxldCB0ZW50byBNYWM + + LucidaGrande + 1.300000e+01 + 1044 + + + 1211912703 + 2 + + NSSwitch + + + + 200 + 25 + + + + + 265 + {{545, 379}, {32, 32}} + + + YES + + 67239424 + 134217728 + Q28gamXigKY + + + 139215103 + 130 + + NSImage + icon-about + + + + + + 200 + 25 + + + + + 268 + {{18, 387}, {141, 18}} + + + YES + + 67239424 + 0 + Aktivovat teleport + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 274 + + YES + + + 256 + + YES + + + 274 + {{20, 17}, {511, 296}} + + + TPLayoutView + NSView + + + + 290 + {{17, 1}, {405, 14}} + + + YES + + 67239424 + 4325376 + UHJvIG92bMOhZMOhbsOtIHJvem3DrXN0xJt0ZSBzZMOtbGVuw6kgTWFjeSB6IGhvcm7DrSBsacWhdHkg +b2tvbG8gc3bDqWhvIG1vbml0b3J1Lg + + + 1.100000e+01 + 3100 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2OQA + + + + 6 + + controlTextColor + + 3 + MAA + + + + + + {{10, 33}, {549, 319}} + + + + + {{13, 10}, {569, 365}} + + + + YES + + layout + + Um96bG/FvmVuw60 + + + + + + + 256 + + YES + + + 301 + + YES + + + 292 + {{182, 101}, {306, 14}} + + YES + + 67239424 + 272760832 + T3puYcSNZW7DvSBwb8SNw610YcSNIG9kc3RyYW7DrXRlIGtsw6F2ZXNvdSBCYWNrc3BhY2UuA + + + + + + + + + -2147483380 + {{179, 250}, {167, 32}} + + YES + + 67239424 + 134217728 + WnZvbGl0IGNlcnRpZmlrw6F0Li4uA + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{179, 250}, {140, 32}} + + YES + + 67239424 + 134217728 + Wm9icmF6aXQgY2VydGlmaWvDoXQ + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{183, 286}, {135, 18}} + + YES + + 67239424 + 0 + UG92b2xpdCDFoWlmcm92w6Fuw60 + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{23, 287}, {148, 17}} + + YES + + 67239424 + 71303168 + xaBpZnJvdsOhbsOtOg + + + + + + + + + 292 + {{185, 23}, {299, 58}} + + YES + 3 + 1 + + YES + + -2080244224 + 0 + RG90w6F6YXQgc2UsIHpkYSBqZSBzdHJvaiBkxa92xJtyeWhvZG7DvQ + + + 1211912703 + 0 + + NSRadioButton + + + + + + 200 + 25 + + + 67239424 + 0 + T2Rtw610bm91dCwgcG9rdWQgbmVuw60gdiBzZXpuYW11A + + 1 + + 1211912703 + 0 + + + + 200 + 25 + + + 67239424 + 0 + Automaticky poskytnout + + 2 + + 1211912703 + 0 + + + + 400 + 75 + + + {299, 18} + {4, 2} + 1143480320 + NSActionCell + + 67239424 + 0 + Radio + + 1211912703 + 0 + + 400 + 75 + + + + + 3 + MQA + + + + + + 274 + + YES + + + 2304 + + YES + + + 256 + {333, 104} + + YES + + + 256 + {333, 17} + + + + + + 256 + {{-26, 0}, {16, 17}} + + + + YES + + name + 1.490000e+02 + 4.000000e+01 + 1.000000e+03 + + 75628032 + 0 + TsOhemV2A + + + 3 + MC4zMzMzMzI5OQA + + + 6 + + headerTextColor + + + + + 337772096 + 133120 + Text Cell + + + + 6 + + controlBackgroundColor + + + + + 1 + YES + + + + address + 1.160000e+02 + 8.000000e+00 + 1.000000e+03 + + 75628032 + 0 + Adresa + + + + + + 337772096 + 133120 + + + + + + + 1 + YES + + + + certificate + 5.900000e+01 + 5.857568e+01 + 1.000000e+03 + + 67239424 + 0 + Q2VydGlmaWvDoXQ + + + 6 + + headerColor + + + + + + 67239424 + 134348800 + VWthxb4 + + + -2034876161 + 36 + + + 400 + 75 + + + + + 3.000000e+00 + 2.000000e+00 + + + 6 + + gridColor + + 3 + MC41AA + + + 1.700000e+01 + 448823296 + 1 + 15 + 0 + YES + + + {{1, 17}, {333, 104}} + + + + + 4 + + + + 256 + {{-100, -100}, {15, 180}} + + + _doScroller: + 9.222222e-01 + + + + 256 + {{-100, -100}, {463, 15}} + + 1 + + + 9.904762e-01 + + + + 2304 + + YES + + + {{1, 0}, {333, 17}} + + + + + 4 + + + + {{185, 116}, {335, 122}} + + + 2 + + + + + + QSAAAEEgAABBmAAAQZgAAA + + + + 292 + {{14, 64}, {157, 17}} + + YES + + 67239424 + 71303168 + UG/FvmFkYXZlayBrIG92bMOhZMOhbsOtOg + + + + + + + + + 268 + {{8, 221}, {163, 17}} + + YES + + 67239424 + 71303168 + RMWvdsSbcnlob2Ruw6kgc3Ryb2plOg + + + + + + + + {549, 319} + + + + + {{10, 33}, {549, 319}} + + WmFiZXpwZcSNZW7DrQ + + + + + options + + + 256 + + YES + + + 301 + + YES + + + 268 + {{211, 132}, {146, 18}} + + YES + + 67239424 + 0 + SmUtbGkgbWVuxaHDrSBuZcW+A + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{102, 181}, {69, 17}} + + YES + + 67239424 + 71303168 + UMWZZW5vc3k6A + + + + + + + + + 268 + {{55, 17}, {116, 17}} + + YES + + 67239424 + 71303168 + Aktualizace: + + + + + + + + + 268 + {{102, 287}, {69, 17}} + + YES + + 67239424 + 71303168 + UMWZZXDDrW7DoW7DrTo + + + + + + + + + 268 + {{183, 74}, {183, 18}} + + YES + + 67239424 + 0 + Wm9icmF6aXQgc3RhdiB2IGxpxaF0xJs + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 180}, {224, 18}} + + YES + + -2080244224 + 0 + VMOhaG5pICYgUHVzxaUgbWV6aSBNYWN5A + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{211, 108}, {134, 18}} + + YES + + 67239424 + 0 + UMWZaSBzdGlza3Uga2zDoXZlc3k6A + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 156}, {174, 18}} + + YES + + -2080244224 + 0 + U2TDrWxldCBzY2hyw6Fua3U + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 286}, {174, 18}} + + YES + + 67239424 + 0 + UG91emUgcMWZaSBzdGlza3Uga2zDoXZlc3k + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{346, 103}, {129, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + 4oylIEFsdA + + 1048576 + 2147483647 + 1 + + + NSMenuCheckmark + + + + NSMenuMixedState + + _popUpItemAction: + 19 + + + YES + + + OtherViews + + + YES + + + 4oyYIEphYmxrbw + + 1048576 + 2147483647 + + + _popUpItemAction: + 20 + + + + + + 4oyDIENvbnRyb2w + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + 4oenIFNoaWZ0A + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + 4oeqIENhcHMgTG9jaw + + 1048576 + 2147483647 + + + _popUpItemAction: + 16 + + + + + 1 + 3 + YES + YES + 1 + + + + + 268 + {{294, 249}, {52, 28}} + + YES + + 67239424 + 272629760 + S3LDoXRrb3U + + + + + + + + + 268 + {{183, 214}, {214, 18}} + + YES + + 67239424 + 0 + UG9rdXNpdCBzZSBwcm9idWRpdCBzcMOtY8OtIE1hY3k + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 262}, {146, 18}} + + YES + + 67239424 + 0 + S prodlevou + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 238}, {204, 18}} + + YES + + 67239424 + 0 + UMWZaSBkdm9qZG90ZWt1A + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{363, 131}, {58, 22}} + + YES + + -1804468671 + 71304192 + + + + YES + + YES + allowsFloats + attributedStringForZero + decimalSeparator + formatterBehavior + groupingSeparator + locale + maximum + minimum + negativeFormat + positiveFormat + textAttributesForNegativeValues + usesGroupingSeparator + + + YES + + + 0 + + YES + + YES + + + YES + + + + wqA + + , + + + + + 0 + 2 + NO + YES + 2 + AABAAAAAAAAAAAAAAAAAAA + + + 0 + 0 + NO + NO + 2 + AAAAAAAAAAAAAAAAAAAAAA + + + 0K + + YES + + YES + + + YES + + + + + + + + + + + + + + + NaN + + + + + + + + NO + NO + YES + + velikost + + YES + + 6 + + textBackgroundColor + + + + 6 + + textColor + + + + + + + 268 + {{360, 281}, {129, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + 16 + + + YES + + + OtherViews + + + YES + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 20 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 19 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + + 4 + 3 + YES + YES + 1 + + + + + 268 + {{183, 50}, {279, 18}} + + YES + + 67239424 + 0 + Wm9icmF6aXQgYmV6ZWwgcMWZaSBvdmzDoWTDoW7DrSBqaW7DqWhvIE1hY2E + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{381, 7}, {111, 32}} + + YES + + 67239424 + 134217728 + T3bEm8WZaXQgdGXEjw + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{102, 75}, {69, 17}} + + YES + + 67239424 + 71303168 + Stav: + + + + + + + + + 268 + {{351, 261}, {83, 15}} + + YES + + 67239424 + 131072 + + + + + Helvetica + 1.200000e+01 + 16 + + + 1.000000e+00 + 1.000000e-01 + 5.000000e-01 + 0.000000e+00 + 0 + 1 + NO + NO + + + + + 268 + {{439, 249}, {51, 28}} + + YES + + 67239424 + 272629760 + Dlouhou + + + + + + + + + 268 + {{183, 16}, {198, 18}} + + YES + + 67239424 + 0 + T3bEm8WZaXQgcMWZaSBwxZlpaGzDocWhZW7DrQ + + + 1211912703 + 2 + + + + 200 + 25 + + + + {549, 319} + + + + + {{10, 33}, {549, 319}} + + TW/Fvm5vc3RpA + + + + + + + 0 + YES + YES + + + {595, 438} + + + + + + {595, 429} + + + + {{0, 0}, {1680, 1028}} + {224.664, 32} + {3.40282e+38, 3.40282e+38} + + + 1 + 2 + {{194, 375}, {295, 307}} + 1886912512 + + TPClosingWindow + + View + + + + 256 + + YES + + + 256 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + CorePasteboardFlavorType 0x504E4766 + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{20, 159}, {255, 128}} + + + YES + + 537001472 + 33554432 + + + icon + + 0 + 0 + 0 + NO + + YES + + + + 256 + {{17, 129}, {261, 22}} + + + YES + + 67239424 + 138412032 + teleport + + LucidaGrande-Bold + 1.800000e+01 + 16 + + + + + + + + + 256 + {{17, 113}, {261, 14}} + + + YES + + 67239424 + 138412032 + PHZlcnNpb24+A + + + + + + + + + 256 + {{12, 43}, {274, 39}} + + + YES + + 67239424 + 4194304 + wqkgMjAwMy0yMDA4IGFieXNzb2Z0ClDFr3ZvZG7DrSBtecWhbGVua2E6IERhdmUgU2FnIGEgSmVyZW15 +IFF1aW5uLgpadmzDocWhdG7DrSBwb2TEm2tvdsOhbsOtOiBNYW51MDIsIEp1bGllbiBhIFBhdWwuA + + + 1.000000e+01 + 2843 + + + + + + + + + 258 + {{12, 8}, {59, 17}} + + + YES + + 69336577 + 272629760 + 4p6kIHdlYg + + + 1.100000e+01 + 16 + + http://teleport.abyssoft.com + + YES + + + + + + + 258 + {{119, 8}, {62, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIGRpc2t1c2U + + http://www.abyssoft.com/software/teleport/forums/ + + + + + + + + 258 + {{218, 8}, {601, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIHDFmWlzcMSbanRlA + + aHR0cHM6Ly93d3cucGF5cGFsLmNvbS9jZ2ktYmluL3dlYnNjcj9jbWQ9X3hjbGljayZidXNpbmVzcz1q +dWwlNDBtYWMlMmVjb20maXRlbV9uYW1lPXRlbGVwb3J0Jm5vX3NoaXBwaW5nPTAmbm9fbm90ZT0wJnRh +eD0wJmN1cnJlbmN5X2NvZGU9VVNEJmNoYXJzZXQ9VVRGJTJkOCZjaGFyc2V0PVVURiUyZDg + + + + + + + {295, 307} + + + + {{0, 0}, {1680, 1028}} + {213, 129} + {3.40282e+38, 3.40282e+38} + + + + YES + prefs.allowControl + prefs.sharePasteboard + prefs.requireKey + prefs.delayedSwitch + prefs.showStatusItem + prefs.limitPasteboardSize + prefs.maxPasterboardSize + active + prefs.autocheckVersion + prefs.rejectAuthenticationRequests + prefs.enableEncryption + prefs.switchKeyTag + prefs.copyFiles + prefs.requirePasteboardKey + prefs.pasteboardKeyTag + prefs.trustRequestBehavior + prefs.wakeOnLAN + prefs.switchDelay + localHost.supportDragNDrop + prefs.maxPasteboardSize + prefs.hideControlBezel + prefs.switchWithDoubleTap + + YES + + + + + YES + numberOfSelectedRows + + NSTableView + + + + + + YES + + + _window + + + + 26 + + + + layoutView + + + + 145 + + + + allowControlCheckbox + + + + 156 + + + + aboutWindow + + + + 170 + + + + delegate + + + + 171 + + + + showAboutSheet: + + + + 173 + + + + activationCheckbox + + + + 175 + + + + versionTextField + + + + 183 + + + + + + + + 184 + + + + + + + + 200 + + + + view + + + + 202 + + + + sharePasteboardCheckbox + + + + 205 + + + + requireKeyCheckbox + + + + 206 + + + + _firstKeyView + + + + 232 + + + + _initialKeyView + + + + 233 + + + + _lastKeyView + + + + 234 + + + + dataSource + + + + 258 + + + + + + + + 259 + + + + trustedHostsTableView + + + + 260 + + + + statusCheckbox + + + + 266 + + + + delayedSwitchCheckbox + + + + 272 + + + + content + + + + 298 + + + + value: selection.prefs.allowControl + + + + + + + value + selection.prefs.allowControl + 2 + + + 320 + + + + value: selection.prefs.sharePasteboard + + + + + + + + selection.prefs.sharePasteboard + 2 + + + 321 + + + + value: selection.prefs.requireKey + + + + + + + + selection.prefs.requireKey + 2 + + + 322 + + + + value: selection.prefs.delayedSwitch + + + + + + + + selection.prefs.delayedSwitch + 2 + + + 323 + + + + value: selection.prefs.showStatusItem + + + + + + + + selection.prefs.showStatusItem + 2 + + + 324 + + + + value: selection.prefs.limitPasteboardSize + + + + + + + + selection.prefs.limitPasteboardSize + 2 + + + 329 + + + + enabled: selection.prefs.sharePasteboard + + + + + + + enabled + + 2 + + + 330 + + + + + + + + + + + + + 2 + + + 331 + + + + checkVersion: + + + + 337 + + + + value: selection.active + + + + + + + + selection.active + 2 + + + 342 + + + + enabled: selection.active + + + + + + + + + 2 + + + 343 + + + + + + + + + + + + + 2 + + + 344 + + + + enabled2: selection.active + + + + + + + enabled2 + + + YES + + YES + NSMultipleValuesPlaceholder + NSNoSelectionPlaceholder + NSNotApplicablePlaceholder + NSNullPlaceholder + + + YES + + + + + + + + 2 + + + 345 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 346 + + + + enabled3: selection.prefs.limitPasteboardSize + + + + + + + enabled3 + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 347 + + + + + + + + + + + + + 2 + + + 348 + + + + + + + + + + + + + 2 + + + 349 + + + + + + + + + + + + + 2 + + + 350 + + + + + + + + + + + + + 2 + + + 352 + + + + value: selection.prefs.autocheckVersion + + + + + + + + selection.prefs.autocheckVersion + 2 + + + 355 + + + + + + + + + + + + + 2 + + + 356 + + + + + + + + + + + + + 2 + + + 383 + + + + value: selection.prefs.enableEncryption + + + + + + + + selection.prefs.enableEncryption + 2 + + + 384 + + + + enabled: selection.prefs.enableEncryption + + + + + + + + + 2 + + + 386 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 387 + + + + enabled: selection.prefs.requireKey + + + + + + + + + 2 + + + 399 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 400 + + + + selectedTag: selection.prefs.switchKeyTag + + + + + + + selectedTag + selection.prefs.switchKeyTag + 2 + + + 401 + + + + + + + + + + + + + 2 + + + 403 + + + + value: selection.prefs.copyFiles + + + + + + + + selection.prefs.copyFiles + 2 + + + 405 + + + + + + + + 407 + + + + + + + + + + + + + 2 + + + 419 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 420 + + + + value: selection.prefs.requirePasteboardKey + + + + + + + + selection.prefs.requirePasteboardKey + 2 + + + 421 + + + + enabled: selection.prefs.requirePasteboardKey + + + + + + + + + 2 + + + 422 + + + + enabled2: selection.prefs.sharePasteboard + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 423 + + + + enabled3: selection.active + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 424 + + + + selectedTag: selection.prefs.pasteboardKeyTag + + + + + + + + selection.prefs.pasteboardKeyTag + 2 + + + 425 + + + + + + + + + + + + + 2 + + + 431 + + + + + + + + + + + + + 2 + + + 432 + + + + selectedTag: selection.prefs.trustRequestBehavior + + + + + + + + selection.prefs.trustRequestBehavior + 2 + + + 442 + + + + + + + + + + + + + 2 + + + 445 + + + + value: selection.prefs.wakeOnLAN + + + + + + + + selection.prefs.wakeOnLAN + 2 + + + 446 + + + + value: selection.prefs.switchDelay + + + + + + + + selection.prefs.switchDelay + 2 + + + 450 + + + + enabled: selection.prefs.delayedSwitch + + + + + + + + + 2 + + + 451 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 452 + + + + enabled2: selection.localHost.supportDragNDrop + + + + + + + + selection.localHost.supportDragNDrop + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 453 + + + + + + + + + + + + + 2 + + + 454 + + + + value: selection.prefs.maxPasteboardSize + + + + + + + + selection.prefs.maxPasteboardSize + 2 + + + 455 + + + + showCertificateButton + + + + 300281 + + + + chooseCertificateButton + + + + 300282 + + + + showCertificateViewer: + + + + 300283 + + + + showCertificateChooser: + + + + 300284 + + + + + + + + + + + + + 2 + + + 300293 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 300294 + + + + value: selection.prefs.hideControlBezel + + + + + + + + selection.prefs.hideControlBezel + + NSValueTransformerName + NSNegateBoolean + + 2 + + + 300300 + + + + + + + + + + + + + 2 + + + 300301 + + + + value: selection.prefs.switchWithDoubleTap + + + + + + + + selection.prefs.switchWithDoubleTap + 2 + + + 300305 + + + + + + + + + + + + + 2 + + + 300306 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + -3 + + + Application + + + 12 + + + YES + + + + PrefPane + + + 6 + + + YES + + + + + + 201 + + + YES + + + + + + + + + 136 + + + YES + + + + + + 172 + + + YES + + + + + + 174 + + + YES + + + + + + 161 + + + YES + + + + About + + + 162 + + + YES + + + + + + + + + + + + 164 + + + YES + + + + + + 165 + + + YES + + + + + + 166 + + + YES + + + + + + 168 + + + YES + + + + + + 291 + + + YES + + + + + + 292 + + + YES + + + + + + 293 + + + YES + + + + + + 297 + + + PrefPaneController + + + 406 + + + TrustedHostsTable + + + 100136 + + + + + 100172 + + + + + 100174 + + + + + 100164 + + + + + 100165 + + + + + 100166 + + + + + 100168 + + + + + 100291 + + + + + 100292 + + + + + 100293 + + + + + 186 + + + YES + + + + + + + + 357 + + + YES + + + + + + 358 + + + YES + + + + + + 300315 + + + YES + + + + + + + + + + + + + + 362 + + + YES + + + + + + 100362 + + + + + 440 + + + YES + + + + + + 100440 + + + + + 246 + + + YES + + + + + + + + + 247 + + + YES + + + + + + + + 248 + + + YES + + + + + + 100248 + + + + + 249 + + + YES + + + + + + 100249 + + + + + 360 + + + YES + + + + + + 361 + + + + + 100246 + + + + + 200246 + + + + + 300246 + + + + + 434 + + + YES + + + + + + + + + 437 + + + + + 438 + + + + + 439 + + + + + 100434 + + + + + 300297 + + + YES + + + + + + 300298 + + + + + 381 + + + YES + + + + + + 100381 + + + + + 385 + + + YES + + + + + + 100385 + + + + + 300279 + + + YES + + + + + + 300280 + + + + + 441 + + + YES + + + + + + 100441 + + + + + 188 + + + YES + + + + + + 190 + + + YES + + + + + + 300316 + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + 353 + + + YES + + + + + + 100353 + + + + + 449 + + + YES + + + + + + 100449 + + + + + 447 + + + YES + + + + + + 100447 + + + + + 368 + + + YES + + + + + + 100368 + + + + + 336 + + + YES + + + + + + 100336 + + + + + 300295 + + + YES + + + + + + 300296 + + + + + 389 + + + YES + + + + + + 100389 + + + YES + + + + + + 390 + + + YES + + + + + + + + + + 395 + + + + + 394 + + + + + 393 + + + + + 392 + + + + + 391 + + + + + 326 + + + YES + + + + + + 100326 + + + YES + + + + + + 334 + + + + + 300302 + + + YES + + + + + + 300303 + + + + + 271 + + + YES + + + + + + 100271 + + + + + 443 + + + YES + + + + + + 100443 + + + + + 448 + + + YES + + + + + + 100448 + + + + + 412 + + + YES + + + + + + 100412 + + + YES + + + + + + 413 + + + YES + + + + + + + + + + 418 + + + + + 417 + + + + + 416 + + + + + 415 + + + + + 414 + + + + + 194 + + + YES + + + + + + 100194 + + + + + 191 + + + YES + + + + + + 100191 + + + + + 411 + + + YES + + + + + + 100411 + + + + + 402 + + + YES + + + + + + 100402 + + + + + 264 + + + YES + + + + + + 100264 + + + + + 366 + + + YES + + + + + + 100366 + + + + + 367 + + + YES + + + + + + 100367 + + + + + 363 + + + YES + + + + + + 100363 + + + + + 325 + + + YES + + + + + + 100325 + + + + + 187 + + + YES + + + + + + 189 + + + YES + + + + + + + 152 + + + YES + + + + + + 100152 + + + + + 137 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + -3.ImportedFromIB2 + 100246.IBShouldRemoveOnLegacySave + 100248.IBShouldRemoveOnLegacySave + 100249.IBShouldRemoveOnLegacySave + 12.IBPluginDependency + 12.IBWindowTemplateEditedContentRect + 12.ImportedFromIB2 + 12.editorWindowContentRectSynchronizationRect + 12.windowTemplate.hasMaxSize + 12.windowTemplate.hasMinSize + 12.windowTemplate.maxSize + 12.windowTemplate.minSize + 136.IBAttributePlaceholdersKey + 136.IBPluginDependency + 136.ImportedFromIB2 + 137.IBAttributePlaceholdersKey + 137.IBPluginDependency + 137.ImportedFromIB2 + 152.IBPluginDependency + 152.ImportedFromIB2 + 161.IBPluginDependency + 161.IBWindowTemplateEditedContentRect + 161.ImportedFromIB2 + 161.editorWindowContentRectSynchronizationRect + 161.windowTemplate.hasMaxSize + 161.windowTemplate.hasMinSize + 161.windowTemplate.maxSize + 161.windowTemplate.minSize + 162.IBPluginDependency + 162.ImportedFromIB2 + 164.IBPluginDependency + 164.ImportedFromIB2 + 165.IBPluginDependency + 165.ImportedFromIB2 + 166.CustomClassName + 166.IBPluginDependency + 166.ImportedFromIB2 + 168.IBPluginDependency + 168.ImportedFromIB2 + 172.IBAttributePlaceholdersKey + 172.IBPluginDependency + 172.ImportedFromIB2 + 174.IBAttributePlaceholdersKey + 174.IBPluginDependency + 174.ImportedFromIB2 + 186.IBPluginDependency + 186.ImportedFromIB2 + 187.IBPluginDependency + 187.ImportedFromIB2 + 188.IBPluginDependency + 188.ImportedFromIB2 + 189.IBPluginDependency + 189.ImportedFromIB2 + 190.IBPluginDependency + 190.ImportedFromIB2 + 191.IBPluginDependency + 191.ImportedFromIB2 + 194.IBPluginDependency + 194.ImportedFromIB2 + 200246.IBShouldRemoveOnLegacySave + 201.IBPluginDependency + 201.ImportedFromIB2 + 246.IBPluginDependency + 246.ImportedFromIB2 + 247.CustomClassName + 247.IBPluginDependency + 247.ImportedFromIB2 + 248.IBPluginDependency + 248.ImportedFromIB2 + 249.IBPluginDependency + 249.ImportedFromIB2 + 264.IBPluginDependency + 264.ImportedFromIB2 + 271.IBPluginDependency + 271.ImportedFromIB2 + 291.CustomClassName + 291.IBPluginDependency + 291.ImportedFromIB2 + 292.CustomClassName + 292.IBPluginDependency + 292.ImportedFromIB2 + 293.CustomClassName + 293.IBPluginDependency + 293.ImportedFromIB2 + 297.IBPluginDependency + 297.ImportedFromIB2 + 300246.IBShouldRemoveOnLegacySave + 300279.IBPluginDependency + 300279.ImportedFromIB2 + 300295.IBPluginDependency + 300295.ImportedFromIB2 + 300297.IBPluginDependency + 300297.ImportedFromIB2 + 300302.IBAttributePlaceholdersKey + 300302.IBPluginDependency + 300302.ImportedFromIB2 + 325.IBPluginDependency + 325.ImportedFromIB2 + 326.IBPluginDependency + 326.ImportedFromIB2 + 334.IBPluginDependency + 334.ImportedFromIB2 + 336.IBPluginDependency + 336.ImportedFromIB2 + 353.IBPluginDependency + 353.ImportedFromIB2 + 357.IBPluginDependency + 357.ImportedFromIB2 + 358.IBPluginDependency + 358.ImportedFromIB2 + 360.IBPluginDependency + 360.ImportedFromIB2 + 361.IBPluginDependency + 361.ImportedFromIB2 + 362.IBPluginDependency + 362.ImportedFromIB2 + 363.IBPluginDependency + 363.ImportedFromIB2 + 366.IBPluginDependency + 366.ImportedFromIB2 + 367.IBPluginDependency + 367.ImportedFromIB2 + 368.IBPluginDependency + 368.ImportedFromIB2 + 381.IBPluginDependency + 381.ImportedFromIB2 + 385.IBPluginDependency + 385.ImportedFromIB2 + 389.IBPluginDependency + 389.ImportedFromIB2 + 390.IBPluginDependency + 390.ImportedFromIB2 + 390.editorWindowContentRectSynchronizationRect + 391.IBPluginDependency + 391.ImportedFromIB2 + 392.IBPluginDependency + 392.ImportedFromIB2 + 393.IBPluginDependency + 393.ImportedFromIB2 + 394.IBPluginDependency + 394.ImportedFromIB2 + 395.IBPluginDependency + 395.ImportedFromIB2 + 402.IBPluginDependency + 402.ImportedFromIB2 + 406.IBPluginDependency + 406.ImportedFromIB2 + 411.IBPluginDependency + 411.ImportedFromIB2 + 412.IBPluginDependency + 412.ImportedFromIB2 + 413.IBPluginDependency + 413.ImportedFromIB2 + 413.editorWindowContentRectSynchronizationRect + 414.IBPluginDependency + 414.ImportedFromIB2 + 415.IBPluginDependency + 415.ImportedFromIB2 + 416.IBPluginDependency + 416.ImportedFromIB2 + 417.IBPluginDependency + 417.ImportedFromIB2 + 418.IBPluginDependency + 418.ImportedFromIB2 + 434.IBPluginDependency + 434.ImportedFromIB2 + 437.IBPluginDependency + 437.ImportedFromIB2 + 438.IBPluginDependency + 438.ImportedFromIB2 + 439.IBPluginDependency + 439.ImportedFromIB2 + 440.IBPluginDependency + 440.ImportedFromIB2 + 441.IBPluginDependency + 441.ImportedFromIB2 + 443.IBAttributePlaceholdersKey + 443.IBPluginDependency + 443.ImportedFromIB2 + 447.IBPluginDependency + 447.ImportedFromIB2 + 448.IBPluginDependency + 448.ImportedFromIB2 + 449.IBPluginDependency + 449.ImportedFromIB2 + 6.IBPluginDependency + 6.ImportedFromIB2 + + + YES + + + + + + + + + {{346, 184}, {595, 429}} + + + + + {3.40282e+38, 3.40282e+38} + {224.664, 10} + + ToolTip + + + + WmHFoWtydG51dMOtbSB1bW/Fvm7DrXRlIG9zdGF0bsOtbSB1xb5pdmF0ZWzFr20gb3Zsw6FkYXQgdsOh +xaEgTWFjLg + + + + + + + + + + VG90byBqZSBvYmxhc3QsIHZlIGt0ZXLDqSB1csSNdWpldGUgcm96bcOtc3TEm27DrSBqZWRub3RsaXbD +vWNoIHBsb2NoLiBQxZlldMOhaG7Em3RlIG92bMOhZGFuw6kgcG/EjcOtdGHEjWUgbmEgenZvbGVuw6kg +bcOtc3RvLg + + + + + + + + {{66, 227}, {295, 307}} + + + + + + {213, 107} + + + + + + + TPVersionTextField + + + + + + + + + + Q28gamUgdGVsZXBvcnTigKY + + + + + + + + + + WmHFoWtydG51dMOtbSBha3RpdnVqZXRlIHRlbGVwb3J0Lg + + + + + + + + + + + + + + + + + + + + + + + + TPTableView + + + + + + + + + + + TPLinkTextField + + + + + + + + + + + + + + + + + + + + + + + SyBkdm9qZG90ZWt1IGRvY2jDoXrDrSwga2R5xb4gc2UgZG90a25ldGUga3Vyem9yZW0gb2tyYWplIG9i +cmF6b3ZreSwgdnLDoXTDrXRlIHNlIHpwxJt0IGEgZG90a25ldGUgc2UgcG9kcnVow6kuA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{766, 444}, {157, 103}} + + + + + + + + + + + + + + + + + + + + + {{675, 264}, {157, 103}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + UG91emUgbmEgcG/EjcOtdGHEjcOtY2ggcMWZaXBvamVuw71jaCBrIHPDrXRpIEV0aGVybmV0IHNlIHph +cG51dG91IHZvbGJvdSAiUHJvYnVkaXQgcHJvIHDFmcOtc3R1cCBhZG1pbmlzdHLDoXRvcmEgcMWZZXMg +RXRoZXJuZXQiIHphxaFrcnRudXRvdSB2IG92bMOhZGFjw61tIHBhbmVsdSDDmnNwb3JhIGVuZXJnaWUu +A + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 300316 + + + + YES + + + NSTextField + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBProjectSource + TPVersionTextField.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBUserSource + + + + + NSObject + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTableView.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPHotBorder.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTransfersManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPConnectionsManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPUDPSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection_Private.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSecureSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPDaemonManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPPreferencesManager.h + + + + + + + : + id + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPClosingWindow.h + + + + + + + YES + + YES + + + YES + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + scroll: + setScaleFactor: + + + YES + + + + + + + + + + + TPLayoutView.h + + + + NSPreferencePane + + + YES + + YES + addOther: + changeTabPanel: + + closeAddOther: + closeTrustedCertificate: + confirmAddOther: + deleteTrustedHost: + + showCertificate: + showTrustedHosts: + + + YES + + + + + + + + + + + + + + YES + + YES + + + + + + + addOtherAddressField + addOtherHeightField + addOtherNameField + addOtherSheet + addOtherWidthField + + certificateView + certificateWindow + + + + + + + + + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPLinkTextField.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + + + + + + + + + YES + + + + + + + + + + + + YES + + YES + + + + + + + + + + + + + + + YES + + + + + + + + + + + + + + + + + TPPreferencePane.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + 0 + ../../teleport.xcodeproj + 3 + + YnBsaXN0MDDUAAEAAgADAAQABQAGAAkAClgkdmVyc2lvblQkdG9wWSRhcmNoaXZlclgkb2JqZWN0cxIA +AYag0QAHAAhdSUIub2JqZWN0ZGF0YYABXxAPTlNLZXllZEFyY2hpdmVyrxEEBwALAAwAMQA1ADYAPAA9 +AEEARQCfAKcAtgC/AAsAwADBAMIAxgDHAMsA0ADRANYA1wDcAOYA7wDwAQoBCwETARQBFwEbARwBHwEg +ASQBKAEpAS4BLwE0ATwBRAFFAU0BTgFPAVQBdgF3AXgBhwGIAY4BlwGYAZsBoAGzAbQBtwG9Ac8B0AHY +AdkB3gHkAecB7wHwAfEB9AH4AgECAgIHAggCCwIQAhECEwIWAh8CIAImAicCLgI2AjcCPQI+AkMCRAJH +AlMCVAJYAl0CXgJhAmQCaQJwAnECegJ7AnwCgQKKAosClwKYAp0CngKfAqICpQKpAqoCrQKyArkCwALB +AskCygLMAs0C0gLaAtsC4gLjAuUC5gLrAvIC8wL6AvsC/QL+AwMDDQMOAw8DFgALAxcDHwMgAyUDJgMn +AywDNAM1Az4DPwNAA0UDTANNA1UDVgNXA1wDXQNiA3gDeQCrA3oDfQOEA4gDjwOfA6MDqAOxA7IDugO7 +A70DvgPAA8EDxwPPA9AD0QPUA9oD3wPjA+8D9wP4BAAEAQQIBAkEEQQSBBkEGgQiBCMEPQQ+BEQETARN +BFAEUQRTBFwEXQRlBGYEZwEjBGgEbQRuBHEEeQR9BH4EgQSMBI0EjgSRBJkEmgSeBJ8EoASjBKoEqwSy +BLMEugS7BMIEwwO9BMQExQTMBM0E0gTWBO8E9gT3BP8FAAUHBQgFDwUQBRcFGAUfBSAFJwUoBS8FMAU3 +BTgFQAVBBUgFSQVRBVIFWQVaBXAFcgWFBYoFiwWPBZAFlAWVBZYFmAWbBaMFrQWVBa4FuAWVBbkFwwWV +BcQFzgWVBc8F0QXVBdgF3wXgBegF6QXwBfEF+QX6BgEGAgYKBgsGEgYTBhsGHAYjBiQGMQZTBnAGcQZy +BnMGdAZ1BnYGdwZ4BnkGegZ7BnwGhwaKBosGkAaRBpUGmAabBp8GoAahBqUBbAaoAQgGrQauBrEBHwa0 +BrgGuQa8Br0GwgbDBsgGyQbQBtEG3AbeBucFlQbrBu0G9QWVBv4FlQcHBZUHEAWVBxkHIAchBykHKgcx +BzIHOQc6B0EHQgdWB1gHXAddB2AHYwdqB2sHcgdzB3oHeweDBMMDvQeEB4UHhweIAAsHiQeKB4sHjAeP +B5AHlQeWB5sHowenAXcHqAeqB6wHsAexB7YHwQfCB8MHxQfPB9sH5QfmB+cH6AfpB+oH6wfsB+0H9wf7 +B/wH/wgCCAoICwgSCBMIFwgYCCAIIQgpCCoILwg6CDsIPAhGCEcISghLCFUIVgheCF8IYAhqCGsIcwh0 +CHUACweJCHYHiwh3CHgIfQiCCIMIiAiJCI4IkwiYCLEIsgizCLQItQi2CLcIuAi5CLoIuwi8CL0Ivgi/ +CMAIwQjCCMMIxAjFCMYIxwjJCM4IzwjUCNUI2gjbCOUI5gjnCOgI6wjyCPMI9Aj1CPwI/Qj+CQUJDAkN +CQ4JGQkaCRsJJwkoCSkJKgkrCSwJMwk6CTsJPAlDCUoJSwlMCVMJWglbCVwJYwlkCWUJZgltCXYJggmJ +CZIJngmnCagJqQm1CbwJvQm+CcUJxgnHCc4JzwnQCdkJ5QnsCe0J7gn1CfwJ/Qn+CgUKBgoPChsKIgop +CioKKwoyCjMKNAo7CjwKRQpGClIKWwpcCmgKbwp2Cn0Kfgp/CoYKjwqbCqIKqQqwCrcKuAq5CsAKxwrO +Cs8K1grfCuAK4QrtCvQK9Qr2Cv0K/gsGCwcLCAsOCw8LEAsXCxgLHwsgCykLNQs8Cz0LPgtBC0YLRwtM +C00LUgtTC1gLWQteC18L4QvkC+UL5wxpDOwNbw1wDXENcg1zDXQNdQ12DXcNeA15DXoNew18DX0Nfg1/ +DYANgQ2CDYMNhA2FDYYNhw2IDYkNig2LDYwNjQ2ODY8NkA2RDZINkw2UDZUNlg2XDZgNmQ2aDZsNnA2d +DZ4Nnw2gDaENog2jDaQNpQ2mDacNqA2pDaoNqw2sDa0Nrg2vDbANsQ2yDbMNtA21DbYNtw24DbkNug27 +DbwNvQ2+Db8NwA3BDcINww3EDcUNxg3HDcgNyQ3KDcsNzA3NDc4Nzw3QDdEN0g3TDdQN1Q3WDdcN2A3Z +DdoN2w3cDd0N3g3fDeAN4Q3iDeMN5A3lDeYN5w3oDekN6g3rA3wN7A3tDe4N9g3+DtgPsgg1D7MPtA+1 +D7YPtw+4D7kPug+7D7wPvQ++D78PwA/BD8IPww/ED8UPxg/HD8gPyQ/KD8sPzA/ND84Pzw/QD9EP0g/T +D9QP1Q/WD9cP2A/ZD9oP2w/cD90P3gHsD98P4A/hD+IP4w/kD+UP5g/nD+gP6Q/qD+sBBw/sD+0P7g/v +D/AP8Q/yD/MP9A/1D/YP9w/4D/kP+g/7D/wP/Q/+D/8QABABEAIQAxAEEAUQBhAHEAgQCRAKEAsQDBAN +EA4QDxAQEBEQEhATEBQQFRAWEBcQGBAZEBoQGxAcEB0QHhAfECAQIRAiECMQJBAlECYQJxAoECkQKhAr +ECwQLRAuEC8QMBAxEDIQMxA0EDUQNhA3EDgQORA6EDsQPBA9ED4QPxBAEEEQQhBDEEQQRRBGEEcQSBBJ +EEoQSxBMEE0QThBPEFAQURBSEFMQVBBVEFYQVxBYEFkQWhBbEFwQXRBeEF8QYBBhEGIQYxBkEGUQZhBn +A/QQaBBpEGoCUBBrEGwQbRBuEG8QcBBxEHIQcxB0EHUQdhB3EHgQeRB6EHsQfBB9EH4QfxCAEIEQghCD +EIQQhxCKEI1VJG51bGzfEBIADQAOAA8AEAARABIAEwAUABUAFgAXABgAGQAaABsAHAAdAB4AHwAgACEA +IgAjACQAJQAmACcAKAApACoAKwAsAC0ALgAvADBWTlNSb290ViRjbGFzc11OU09iamVjdHNLZXlzXxAP +TlNDbGFzc2VzVmFsdWVzXxAZTlNBY2Nlc3NpYmlsaXR5T2lkc1ZhbHVlc11OU0Nvbm5lY3Rpb25zW05T +TmFtZXNLZXlzW05TRnJhbWV3b3JrXU5TQ2xhc3Nlc0tleXNaTlNPaWRzS2V5c11OU05hbWVzVmFsdWVz +XxAZTlNBY2Nlc3NpYmlsaXR5Q29ubmVjdG9yc11OU0ZvbnRNYW5hZ2VyXxAQTlNWaXNpYmxlV2luZG93 +c18QD05TT2JqZWN0c1ZhbHVlc18QF05TQWNjZXNzaWJpbGl0eU9pZHNLZXlzWU5TTmV4dE9pZFxOU09p +ZHNWYWx1ZXOAAoEEBoECoYEDKYEEBYAIgQKmgAWBAyiBAyqBAqeBBAOAAIAGgQKlgQQEEgAElSSBAyvS +AA4AMgAzADRbTlNDbGFzc05hbWWABIADXxAQVFBQcmVmZXJlbmNlUGFuZdIANwA4ADkAOlgkY2xhc3Nl +c1okY2xhc3NuYW1logA6ADteTlNDdXN0b21PYmplY3RYTlNPYmplY3RfEBBJQkNvY29hRnJhbWV3b3Jr +0gAOAD4APwBAWk5TLm9iamVjdHOAB6DSADcAOABCAEOjAEMARAA7XE5TTXV0YWJsZVNldFVOU1NldNIA +DgA+AEYAR4BirxBXAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0AXgBf +AGAAYQBiAGMAZABlAGYAZwBoAGkAagBrAGwAbQBuAG8AcABxAHIAcwB0AHUAdgB3AHgAeQB6AHsAfAB9 +AH4AfwCAAIEAggCDAIQAhQCGAIcAiACJAIoAiwCMAI0AjgCPAJAAkQCSAJMAlACVAJYAlwCYAJkAmgCb +AJwAnQCegAmAE4AVgBeAJ4ApgDCAZ4BtgHqAgoCJgJCAnICigKiAqoEBq4EBrYEBtoEB7YEB7oEB8IEB +8oEB84ECDYECD4ECEYECE4ECGIECHIECH4ECIIECI4ECLIECLYECMIECMYECNIECNYECOIECPIECPYEC +P4ECQIECQoECRoECSYECTIECT4ECUYECVIECVYECWIECWoECXIECXYECYIECY4ECZYECaIECa4ECbIEC +bYECcIECcYECc4ECdIECdYECdoECeYECeoECe4ECfYECfoECgoEChYECh4ECjYECj4ECkYECk4ECl4EC +mYECm4ECnYECn9QADgCgAKEAogCjAB8ApQCmXU5TRGVzdGluYXRpb25YTlNTb3VyY2VXTlNMYWJlbIAS +gAKACoAR2ACoAA4AqQCqAKsAMgCsAK0ArgCvALAAsQCyALMAtACuXxAPTlNOZXh0UmVzcG9uZGVyV05T +RnJhbWVYTlN2RmxhZ3NYTlNXaW5kb3dbTlNFeHRlbnNpb25bTlNTdXBlcnZpZXeAC4AQgAwRARKADYAO +gA+AC9cAqAAOAKkAtwCqAKsArQC4ALkAugC7ALwAsgC4Wk5TU3Vidmlld3OAs4C8gLuAtREBAIANgLNf +EBZ7ezIwLCAxN30sIHs1MTEsIDI5Nn19XFRQTGF5b3V0Vmlld1ZOU1ZpZXfSADcAOADDAMSkAMQAwQDF +ADtcTlNDdXN0b21WaWV3W05TUmVzcG9uZGVyWGRlbGVnYXRl0gA3ADgAyADJowDJAMoAO18QFE5TTmli +T3V0bGV0Q29ubmVjdG9yXk5TTmliQ29ubmVjdG9y1AAOAKAAoQCiAKMApQAfAM+AEoAKgAKAFF1fZmly +c3RLZXlWaWV31AAOAKAAoQCiAKMApQAfANWAEoAKgAKAFlxfbGFzdEtleVZpZXfUAA4AoAChAKIAowDZ +AB8A24ASgBiAAoAm1wCoAA4AqQDdAKoA3gCtAN8A4ADhAOIA4wDkAN9WTlNDZWxsWU5TRW5hYmxlZIAZ +gCWAGoAbEQEMCYAZ1wCoAA4AtwCqAOcAMgCtAOgArwDqAOsA7AC0AOhbTlNGcmFtZVNpemWBAQCAEIEB +AhEBLYEBn4APgQEAXxAXe3sxODMsIDI4Nn0sIHsxNzQsIDE4fX3dAPEADgDyAPMA9AD1APYA9wD4APkA ++gD7APwA/QD+AP8BAAEBAQIA/wEEAQUA2QEHAQgBCVtOU0NlbGxGbGFnc18QE05TQWx0ZXJuYXRlQ29u +dGVudHNfEBJOU1BlcmlvZGljSW50ZXJ2YWxeTlNCdXR0b25GbGFnczJfEBBOU0FsdGVybmF0ZUltYWdl +XxAPTlNLZXlFcXVpdmFsZW50Wk5TQ29udGVudHNZTlNTdXBwb3J0XU5TQ29udHJvbFZpZXdfEA9OU1Bl +cmlvZGljRGVsYXlcTlNDZWxsRmxhZ3MyXU5TQnV0dG9uRmxhZ3MSBAH+AIAkgCMQGRACgCCAI4AcgB2A +GBDIEAASSDxR/28QGABQAG8AdQB6AGUAIABwAVkAaQAgAHMAdABpAHMAawB1ACAAawBsAOEAdgBlAHMA +edQADgEMAQ0BDgEPARABEQESVk5TU2l6ZVZOU05hbWVYTlNmRmxhZ3OAHyNAKgAAAAAAAIAeEQQUXEx1 +Y2lkYUdyYW5kZdIANwA4ARUBFqIBFgA7Vk5TRm9udNIADgEYARkBGltOU0ltYWdlTmFtZYAigCFYTlNT +d2l0Y2jSADcAOAEdAR6iAR4AO18QE05TQnV0dG9uSW1hZ2VTb3VyY2VQ0gA3ADgBIQEipAEiASMA3QA7 +XE5TQnV0dG9uQ2VsbFxOU0FjdGlvbkNlbGzSADcAOAElASalASYBJwDBAMUAO1hOU0J1dHRvbllOU0Nv +bnRyb2xfEBJyZXF1aXJlS2V5Q2hlY2tib3jUAA4AoAChAKIAowClAB8BLYASgAqAAoAoXxAPX2luaXRp +YWxLZXlWaWV31AAOAKAAoQCiAKMBMQAfATOAEoAqgAKAL9gAqAAOAKkA3QCqAN4AqwCtATUA4AE3ATgA +4wDkALIBNYArgCWALIAtCYANgCvYAKgADgC3AKoA5wCrADIArQE9AK8BPwCxAUAAsgC0AT2AsIAQgLKB +AaOADYAPgLBfEBZ7ezE4LCAzODd9LCB7MTQxLCAxOH193QDxAA4A8gDzAPQA9QD2APcA+AD5APoA+wD8 +AP0A/gD/AQABAQECAP8BSgEFATEBBwEIAQmAJIAjgCCAI4AugB2AKl8QEkFrdGl2b3ZhdCB0ZWxlcG9y +dF8QEmFjdGl2YXRpb25DaGVja2JveNQADgCgAKEAogCjAB8BUgCmgBKAAoAxgBHfEBUAqAFVAA4BVgFX +AVgBWQFaAVsBXAFdAV4AqgDnAN4AMgFfAWAArQFhAWIBYwEIAWQBZQFmAWcBaADkAWoBawFsAW0AvAFu +AOQBcAFxAXIBYwF0AXVfEB9OU0RyYWdnaW5nU291cmNlTWFza0Zvck5vbkxvY2FsWU5TVHZGbGFnc18Q +E05TT3JpZ2luYWxDbGFzc05hbWVcTlNIZWFkZXJWaWV3XxARTlNCYWNrZ3JvdW5kQ29sb3JfEBJOU0Fs +bG93c1R5cGVTZWxlY3RcTlNDb3JuZXJWaWV3XxAXTlNJbnRlcmNlbGxTcGFjaW5nV2lkdGhfEBlOU0Nv +bHVtbkF1dG9yZXNpemluZ1N0eWxlXxAYTlNJbnRlcmNlbGxTcGFjaW5nSGVpZ2h0W05TR3JpZENvbG9y +XxAcTlNEcmFnZ2luZ1NvdXJjZU1hc2tGb3JMb2NhbF5OU1RhYmxlQ29sdW1uc1tOU1Jvd0hlaWdodIA0 +gGYSGsCAAIAzgDaAXwmAOiNACAAAAAAAABABI0AAAAAAAAAAgDUJgDKAYxAPgDSAPiNAMQAAAAAAAFtU +UFRhYmxlVmlld1tOU1RhYmxlVmlld9oAqAAOAKkBeQC3AKoBegF7AK0BfAF9AX4BfwGAAYEBggFSAYQB +fQFSWU5TY3ZGbGFnc1lOU0RvY1ZpZXdZTlNCR0NvbG9yXU5TTmV4dEtleVZpZXeAO4DogOcQBIDmEQkA +gDGATYA7gDFaezMzMywgMTA0fdYAqAAOAKoA5wCtAXcBiQGKALwBiwGJAVKAN4A5gDiAN4Ax2gCoAA4A +qQF5ALcAqgF6AXsArQF8AX0BfgGRAYABkgGCAWcBhAF9AWeAO4DogPCA74A2gE2AO4A2WXszMzMsIDE3 +fdIANwA4AZkBmqQBmgDBAMUAO18QEU5TVGFibGVIZWFkZXJWaWV31QCoAA4AqQCqAK0BfQGdAZ4AvAF9 +gDuAPYA8gDveAKgBoQAOAKkBogFbAaMAtwGkAKoArQGlAXwBpgGnAagBqQGqAQEBagGJAa0BrgCxAacB +sAFjAWNbTlNIU2Nyb2xsZXJYTlNzRmxhZ3NfEBBOU0hlYWRlckNsaXBWaWV3XE5TU2Nyb2xsQW10c1tO +U1ZTY3JvbGxlcl1OU0NvbnRlbnRWaWV3gHyA7YDygPGAOoA3gOVPEBBBIAAAQSAAAEGYAABBmAAAgHyA +6YA0gDRfEBR7ey0yNiwgMH0sIHsxNiwgMTd9fdIANwA4AbUBtqQBtgDBAMUAO11fTlNDb3JuZXJWaWV3 +0gAOAD4ARgG5gGKjAboBuwG8gD+AVIBZ2gG+AA4BvwHAAcEBwgHDAcQBxQF3AOQBxwHIAckBygHLAWwB +zAHNAVJeTlNJc1Jlc2l6ZWFibGVcTlNIZWFkZXJDZWxsXE5TSWRlbnRpZmllcldOU1dpZHRoWk5TRGF0 +YUNlbGxeTlNSZXNpemluZ01hc2taTlNNaW5XaWR0aFpOU01heFdpZHRoCYBTgEGAQCNAYqAAAAAAAIBL +I0BEAAAAAAAAI0CPQAAAAAAAgDFUbmFtZdcA8QAOAVkA9wD4APsB0QHSAdMB1AHVAdYBCAHXW05TVGV4 +dENvbG9yEgSB/gCASoBEgEKAQ4BGZQBOAOEAegBlAHbUAA4BDAENAQ4BDwHbAREB3YAfI0AmAAAAAAAA +gB4RDBzTAA4B3wHgAeEB4gHjXE5TQ29sb3JTcGFjZVdOU1doaXRlgEUQA0swLjMzMzMzMjk5ANIANwA4 +AeUB5qIB5gA7V05TQ29sb3LVAA4B5gHfAegB6QHhAesB7AHtAe5bTlNDb2xvck5hbWVdTlNDYXRhbG9n +TmFtZYBFgEkQBoBIgEdWU3lzdGVtXxAPaGVhZGVyVGV4dENvbG9y0wAOAd8B4AHhAeIB84BFQjAA0gA3 +ADgB9QH2pQH2AfcBIwDdADtfEBFOU1RhYmxlSGVhZGVyQ2VsbF8QD05TVGV4dEZpZWxkQ2VsbNgA8QAO +AVkA9wD4APkA+wHRAfkB+gGEAfwB1gFSAf8CABIUIf5AgFKATYBMgEOAMRIAAggAgFBZVGV4dCBDZWxs +1QAOAeYB3wHoAekB4QIEAewCBQHugEWAT4BOgEdfEBZjb250cm9sQmFja2dyb3VuZENvbG9y0wAOAd8B +4AHhAeICCoBFSzAuNjY2NjY2NjkA1QAOAeYB3wHoAekB4QHrAewCDgHugEWASYBRgEdfEBBjb250cm9s +VGV4dENvbG9y0gA3ADgCEgH3pAH3ASMA3QA70gA3ADgCFAIVogIVADtdTlNUYWJsZUNvbHVtbtoBvgAO +Ab8BwAHBAcIBwwHEAcUBdwDkAccCGQIaAhsCHAFsAh0BzQFSCYBTgFaAVSNAXQAAAAAAAIBYI0AgAAAA +AAAAgDFXYWRkcmVzc9cA8QAOAVkA9wD4APsB0QHSAdMB1AIjAdYBCAHXgEqARIBXgEOARlZBZHJlc2HY +APEADgFZAPcA+AD5APsB0QH5AfoBhAH8AdYBUgH/AgCAUoBNgEyAQ4AxgFDYAA4BvwHAAcEBwgHEAcUB +dwHHAjACMQIyAjMCNAHNAVKAU4BbgFojQE2AAAAAAACAYCNATUmv4AAAAIAxW2NlcnRpZmljYXRl1wDx +AA4BWQD3APgA+wHRAP0B0wI5AjoB1gEIAdeASoBdgFyAQ4BGagBDAGUAcgB0AGkAZgBpAGsA4QB01QAO +AeYB3wHoAekB4QFoAewCQQHugEWAX4BegEdbaGVhZGVyQ29sb3LTAA4B3wHgAeEB4gJGgEVCMQDcAPEA +DgDyAPMA9AD2APcA+AD5APoA+wD8AP0A/gD/AkoCSwD/Ak0B1gFSAlACUQJSgCSAIxBLECSAI4BhgEOA +MREBkBIIAgAAE/////+GtkD/ZABVAGsAYQF+0gA3ADgCVQJWowJWAlcAO15OU011dGFibGVBcnJheVdO +U0FycmF51QAOAeYB3wHoAekB4QJaAewCWwHugEWAZYBkgEdZZ3JpZENvbG9y0wAOAd8B4AHhAeICYIBF +RDAuNQDSADcAOAJiAmOiAmMAO15OU0NsYXNzU3dhcHBlctQADgCgAKEAogCjAmYAHwJogBKAaIACgGzX +AKgADgCpAN0AqgDeAK0A3wDgAmwCbQDjAOQA34AZgCWAaYBqCYAZXxAXe3sxODMsIDE1Nn0sIHsxNzQs +IDE4fX3dAPEADgDyAPMA9AD1APYA9wD4APkA+gD7APwCcgD+AP8BAAEBAQIA/wJ3AQUCZgEHAQgBCRP/ +////hAH+AIAkgCOAIIAjgGuAHYBobxAPAFMAZADtAGwAZQB0ACAAcwBjAGgAcgDhAG4AawB1XxAXc2hh +cmVQYXN0ZWJvYXJkQ2hlY2tib3jUAA4AoAChAKICfQAfAn8CgIB5gAKAboB42ACoAA4AqQDdAKoA3gCr +AK0BNQDgAoQChQKGAOQAsgE1gCuAJYBvgHARAQkJgA2AK18QFnt7NTQ1LCAzNzl9LCB7MzIsIDMyfX3d +APEADgDyAowA8wD0APYA9wD4APkA+gD7APwA/QD+AP8CjwEAApACkQKSAQUCfwEHApUCll1OU05vcm1h +bEltYWdlgCSAI4ByEIKAdoBxgB2AbhIIAAAAEghMQP9mAEMAbwAgAGoAZSAm0wAOADICmQKaApsCnF5O +U1Jlc291cmNlTmFtZYB1gHOAdFdOU0ltYWdlWmljb24tYWJvdXTSADcAOAKgAqGiAqEAO18QEE5TQ3Vz +dG9tUmVzb3VyY2XSAA4CowKkAR9ZTlMuc3RyaW5ngHfSADcAOAKmAqejAqcCqAA7XxAPTlNNdXRhYmxl +U3RyaW5nWE5TU3RyaW5nXxAPc2hvd0Fib3V0U2hlZXQ60gA3ADgCqwKsowKsAMoAO18QFU5TTmliQ29u +dHJvbENvbm5lY3RvctQADgCgAKEAogJ9AB8CsAKxgHmAAoB7gIHXAKgADgCpAN0AqgDeAK0BpwDgArUC +tgDjAOQBp4B8gCWAfYB+CYB81wCoAA4AtwCqAOcAMgCtAroArwK8AOsCvQC0ArqAxIAQgMaA+4APgMRf +EBd7ezE3OSwgMjUwfSwgezE0MCwgMzJ9fdwA8QAOAPIA8wD0APYA9wD4APkA+gD7APwA/QD+AP8BAAFs +AsQCxQEFArABBwKVAsiAJIAjgICAf4AdgHsT/////4aCQP9vEBMAWgBvAGIAcgBhAHoAaQB0ACAAYwBl +AHIAdABpAGYAaQBrAOEAdNIADgKjAqQBH4B3XxAWc2hvd0NlcnRpZmljYXRlVmlld2VyOtQADgCgAKEA +ogJ9AB8C0ALRgHmAAoCDgIjXAKgADgCpAN0AqgDeAK0BpwDgAtUC1gLXAOQBp4B8gCWAhICFE/////+A +AAEMCYB8XxAXe3sxNzksIDI1MH0sIHsxNjcsIDMyfX3cAPEADgDyAPMA9AD2APcA+AD5APoA+wD8AP0A +/gD/AQABbALeAt8BBQLQAQcClQLIgCSAI4CHgIaAHYCDbxAUAFoAdgBvAGwAaQB0ACAAYwBlAHIAdABp +AGYAaQBrAOEAdAAuAC4ALtIADgKjAqQBH4B3XxAXc2hvd0NlcnRpZmljYXRlQ2hvb3NlcjrUAA4AoACh +AKICfQAfAukC6oB5gAKAioCP1wCoAA4AqQDdAKoA3gCtAN8A4ALuAu8A4wDkAN+AGYAlgIuAjAmAGV8Q +FXt7MzgxLCA3fSwgezExMSwgMzJ9fdwA8QAOAPIA8wD0APYA9wD4APkA+gD7APwA/QD+AP8BAAFsAvYC +9wEFAukBBwKVAsiAJIAjgI6AjYAdgIpqAE8AdgEbAVkAaQB0ACAAdABlAQ/SAA4CowKkAR+Ad11jaGVj +a1ZlcnNpb2461AAOAKAAoQCiAKMDAAAfAwKAEoCRgAKAm9oAqAAOAKkBVwDdAKoA3gCrADIArQMEAWQD +BgMHAwgAvADkAwoDCwMEgJSAZoCVgJOAlwmAloCSgJRfEBJUUFZlcnNpb25UZXh0RmllbGRbTlNUZXh0 +RmllbGTXAKgADgC3AKoA5wCrAK0DCgC5AxIAvAMTAwoDFYCWgLyBAbuBAeeAloEB6F8QFnt7MTcsIDEx +M30sIHsyNjEsIDE0fX3YAPEADgFZAPcA+AD5APsB0QD9AfoDGQMaAdYDAAMdAgCAUoCZgJiAQ4CREghA +AACAUFk8dmVyc2lvbj7VAA4B5gHfAegB6QHhAgQB7AMjAe6ARYBPgJqAR1xjb250cm9sQ29sb3JfEBB2 +ZXJzaW9uVGV4dEZpZWxk1AAOAKAAoQCiAKMDKQAfAyuAEoCdgAKAodgAqAAOAKkA3QCqAN4AqwCtATUA +4AMvAzAA4wDkALIBNYArgCWAnoCfCYANgCtfEBd7ezE3NiwgMzg3fSwgezEzMCwgMTh9fd0A8QAOAPIA +8wD0APUA9gD3APgA+QD6APsA/AM2AP4A/wEAAQEBAgD/AzsBBQMpAQcBCAEJEiQB/gCAJIAjgCCAI4Cg +gB2AnW8QEABTAGQA7QBsAGUAdAAgAHQAZQBuAHQAbwAgAE0AYQBjXxAUYWxsb3dDb250cm9sQ2hlY2ti +b3jUAA4AoAChAKIAowNCAB8DRIASgKOAAoCn1wCoAA4AqQDdAKoA3gCtAN8A4ANIA0kA4wDkAN+AGYAl +gKSApQmAGV8QFnt7MTgzLCA3NH0sIHsxODMsIDE4fX3dAPEADgDyAPMA9AD1APYA9wD4APkA+gD7APwA +/QD+AP8BAAEBAQIA/wNSAQUDQgEHAQgBCYAkgCOAIIAjgKaAHYCjbxAVAFoAbwBiAHIAYQB6AGkAdAAg +AHMAdABhAHYAIAB2ACAAbABpAWEAdAEbXnN0YXR1c0NoZWNrYm941AAOAKAAoQCiAKMAHwFSA1uAEoAC +gDGAqVpkYXRhU291cmNl1AAOAKAAoQCiAKMDXwAfA2GAEoCrgAKBAarcA2MADgNkA2UDZgNnA2gDaQNq +A2sDbANtAT0DbwNwA3EDcgNzA3QDdQEBAXIDdgN3XE5TV2luZG93Vmlld1xOU1NjcmVlblJlY3RdTlNX +aW5kb3dUaXRsZVlOU1dURmxhZ3NdTlNXaW5kb3dDbGFzc1xOU1dpbmRvd1JlY3RZTlNNYXhTaXplXxAP +TlNXaW5kb3dCYWNraW5nXxARTlNXaW5kb3dTdHlsZU1hc2tZTlNNaW5TaXplW05TVmlld0NsYXNzgLCB +AamBAaaArRJAeAAAgK6ArIEBqIEBp4CvXxAYe3s1MTYsIDQ1MH0sIHs1OTUsIDQyOX19XxAVPDwgZG8g +bm90IGxvY2FsaXplID4+0gAOAqMCpAN8gHdUVmlld9cAqAAOALcAqgDnAKsArQCyALkDgAC8A4EAsgOD +gA2AvICxgQGkgA2BAaXSAA4APgBGA4aAYqEBNYAr0gAOAD4ARgOKgGKkAykCfwExALiAnYBugCqAs90A +qAAOA5ABVgCpARYAtwCqAKsDkQCtA5IDkwE1A5UDlgEIA5cBBQOZALEAsgDkATUA5AOeXk5TVGFiVmll +d0l0ZW1zXxARTlNEcmF3c0JhY2tncm91bmRfEBZOU0FsbG93VHJ1bmNhdGVkTGFiZWxzXxAVTlNTZWxl +Y3RlZFRhYlZpZXdJdGVtgCuBAaKAvoC9gB2AtIANCYArCYC/0gAOAD4ARgOhgGKhAK6AC9IADgA+AEYD +pYBiogClA6eACoC22ACoAA4AqQDdAKoA3gCrAK0ArgOqA6sDrAOtAOQAsgCugAuAuoC3gLgRASIJgA2A +C18QFHt7MTcsIDF9LCB7NDA1LCAxNH192ADxAA4BWQD3APgA+QD7AdEA/QH6AxkDtQHWA6cDuAIAgFKA +mYC5gEOAthIAQgAAgFBvEEgAUAByAG8AIABvAHYAbADhAGQA4QBuAO0AIAByAG8AegBtAO0AcwB0ARsA +dABlACAAcwBkAO0AbABlAG4A6QAgAE0AYQBjAHkAIAB6ACAAaABvAHIAbgDtACAAbABpAWEAdAB5ACAA +bwBrAG8AbABvACAAcwB2AOkAaABvACAAbQBvAG4AaQB0AG8AcgB1AC7SADcAOAO8Aw6lAw4BJwDBAMUA +O18QFnt7MTAsIDMzfSwgezU0OSwgMzE5fX3SADcAOAO/AMGjAMEAxQA7XxAWe3sxMywgMTB9LCB7NTY5 +LCAzNjV9fdIADgA+AEYDw4BiowOeA8UDxoC/gMOA/tYADgHAAMEDyAHmAKIDyQPKAK4AuAMZA85ZTlNU +YWJWaWV3gMKAwIALgLOAmYDBVmxheW91dGkAUgBvAHoAbABvAX4AZQBuAO3SADcAOAPSA9OiA9MAO11O +U1RhYlZpZXdJdGVt1QAOAMEDyAHmAKIDyQK6ALgDGQPZgMKAxICzgJmA/dUAqAAOAKkAtwCqACsAuQPd +A94AvIAAgLyA/IDF0gAOAD4ARgPhgGKhAaeAfNIADgA+AEYD5YBiqQPmAtACsAPpA+oD6wF9A+0D7oDH +gIOAe4DLgM+A04A7gPOA99cAqAAOAKkA3QCqAN4ArQGnA6oD8gPzA/QA5AGngHyAuoDIgMkRASQJgHxf +EBd7ezE4MiwgMTAxfSwgezMwNiwgMTR9fdgA8QAOAVkA9wD4APkA+wHRAP0B+gMZA/sB1gPmA/4CAIBS +gJmAyoBDgMcSEEIAAIBQbxAvAE8AegBuAGEBDQBlAG4A/QAgAHAAbwENAO0AdABhAQ0AIABvAGQAcwB0 +AHIAYQBuAO0AdABlACAAawBsAOEAdgBlAHMAbwB1ACAAQgBhAGMAawBzAHAAYQBjAGUALtcAqAAOAKkA +3QCqAN4ArQGnAOAEBAQFAOMA5AGngHyAJYDMgM0JgHxfEBd7ezE4MywgMjg2fSwgezEzNSwgMTh9fd0A +8QAOAPIA8wD0APUA9gD3APgA+QD6APsA/AD9AP4A/wEAAQEBAgD/BA4BBQPpAQcBCAEJgCSAI4AggCOA +zoAdgMtvEBEAUABvAHYAbwBsAGkAdAAgAWEAaQBmAHIAbwB2AOEAbgDt1wCoAA4AqQDdAKoA3gCtAacD +qgQVBBYA4wDkAaeAfIC6gNCA0QmAfF8QFnt7MjMsIDI4N30sIHsxNDgsIDE3fX3YAPEADgFZAPcA+AD5 +APsB0QD9AfoDGQQdAQUD6gQgAgCAUoCZgNKAHYDPEgRAAACAUGoBYABpAGYAcgBvAHYA4QBuAO0AOt8Q +EgCoAA4AqQQkBCUEJgFZBCcEKAEWBCkAqgDeBCoArQQrBCwELQGnBC8EMAQxAeIEMgMZBDQBaAEFBDcD +9ADkAWwBpwQ6BDsEPFtOU1Byb3RvQ2VsbFlOU051bVJvd3NeTlNTZWxlY3RlZENlbGxbTlNDZWxsQ2xh +c3NfEBVOU0NlbGxCYWNrZ3JvdW5kQ29sb3JaTlNDZWxsU2l6ZVlOU051bUNvbHNfEBJOU0ludGVyY2Vs +bFNwYWNpbmddTlNNYXRyaXhGbGFnc1dOU0NlbGxzgHyA5IDUgOKA1oCZgOGAX4AdgN8JgHyA4BJEKCAA +gNVfEBZ7ezE4NSwgMjN9LCB7Mjk5LCA1OH190gAOAD4ARgRAgGKjBDIEQgRDgNaA24Dd3QDxAA4A8gDz +APQA9QD2APcA+AD5APoA+wD8AnIA/gRGAQABCARHBEYESQEFA+sBBwEIAQmAJIDagNiA2oDXgB2A028Q +JABEAG8AdADhAHoAYQB0ACAAcwBlACwAIAB6AGQAYQAgAGoAZQAgAHMAdAByAG8AagAgAGQBbwB2ARsA +cgB5AGgAbwBkAG4A/dIADgEYARkET4AigNldTlNSYWRpb0J1dHRvbtIADgKjAqQBH4B33gDxAA4A8gDz +APQA9QD2APcA+AD5APoA+wD8BFQA/QD+BEYBAAEIBEcERgRZAQUD6wEHAQgBCQFsVU5TVGFngCSA2oDY +gNqA3IAdgNNvEB8ATwBkAG0A7QB0AG4AbwB1AHQALAAgAHAAbwBrAHUAZAAgAG4AZQBuAO0AIAB2ACAA +cwBlAHoAbgBhAG0Add4A8QAOAPIA8wD0APUA9gD3APgA+QD6APsA/ARUAP0A/gD/AkoBCARHAP8EYgEF +A+sCUAEIAQkBAYAkgCOA2IAjgN6AHYDTXxAWQXV0b21hdGlja3kgcG9za3l0bm91dFl7Mjk5LCAxOH1W +ezQsIDJ92gDxAA4A8wD0APUA9wD4APoA+wD8AP0A/gJKAQgERwRrAQUCUAEIAQmAJIDYgOOAHVVSYWRp +b9IANwA4BG8EcKUEcAEnAMEAxQA7WE5TTWF0cml40gAOAD4ARgRzgGKlAWMBsAGoAYkBaoA0gOmA7YA3 +gDrSAA4APgBGBHuAYqEBUoAxXxAVe3sxLCAxN30sIHszMzMsIDEwNH190gA3ADgEfwSApASAAMEAxQA7 +Wk5TQ2xpcFZpZXfYAKgEggAOAKkAqgSDAK0EhAF9AX0EhwSIALwEiQF9BItYTlNUYXJnZXRYTlNBY3Rp +b25ZTlNQZXJjZW50gDuAO4DsgOqA64A7Iz/tgtggAAAAXxAZe3stMTAwLCAtMTAwfSwgezE1LCAxODB9 +fVxfZG9TY3JvbGxlcjrSADcAOASPBJClBJABJwDBAMUAO1pOU1Njcm9sbGVy2QCoBIIADgCpAaIAqgSD +AK0EhAF9AX0EhwSVAWwAvASJAX0EmIA7gDuA7IDugOuAOyM/77H7IAAAAF8QGXt7LTEwMCwgLTEwMH0s +IHs0NjMsIDE1fX3SAA4APgBGBJyAYqEBZ4A2XxATe3sxLCAwfSwgezMzMywgMTd9fV8QGHt7MTg1LCAx +MTZ9LCB7MzM1LCAxMjJ9fdIANwA4BKEEoqQEogDBAMUAO1xOU1Njcm9sbFZpZXfXAKgADgCpAN0AqgDe +AK0BpwOqBKYEpwP0AOQBp4B8gLqA9ID1CYB8XxAVe3sxNCwgNjR9LCB7MTU3LCAxN3192ADxAA4BWQD3 +APgA+QD7AdEA/QH6AxkErgEFA+0EIAIAgFKAmYD2gB2A84BQbxAVAFAAbwF+AGEAZABhAHYAZQBrACAA +awAgAG8AdgBsAOEAZADhAG4A7QA61wCoAA4AqQDdAKoA3gCtAacDqgS2BLcA4wDkAaeAfIC6gPiA+QmA +fF8QFXt7OCwgMjIxfSwgezE2MywgMTd9fdgA8QAOAVkA9wD4APkA+wHRAP0B+gMZBL4BBQPuBCACAIBS +gJmA+oAdgPeAUG8QEwBEAW8AdgEbAHIAeQBoAG8AZABuAOkAIABzAHQAcgBvAGoAZQA6Wns1NDksIDMx +OX1rAFoAYQBiAGUAegBwAGUBDQBlAG4A7dYADgHAAMEDyAHmAKIDyQTHAOgAuAMZBMuAwoD/gQEAgLOA +mYEBoVdvcHRpb25z1QCoAA4AqQC3AKoAKwC5BNAE0QC8gACAvIEBoIEBAdIADgA+AEYE1IBioQDfgBnS +AA4APgBGBNiAYq8QFgTZBNoE2wTcA0IE3gTfAmYA2QTiBOME5ATlBOYE5wToBOkC6QTrBOwE7QTugQED +gQEHgQELgQEPgKOBAROBAReAaIAYgQEbgQE5gQE9gQFBgQFFgQFJgQF2gQGHgIqBAYuBAY+BAZeBAZvX +AKgADgCpAN0AqgDeAK0A3wDgBPIE8wDjAOQA34AZgCWBAQSBAQUJgBlfEBd7ezIxMSwgMTMyfSwgezE0 +NiwgMTh9fd0A8QAOAPIA8wD0APUA9gD3APgA+QD6APsA/AD9AP4A/wEAAQEBAgD/BPwBBQTZAQcBCAEJ +gCSAI4AggCOBAQaAHYEBA28QDwBKAGUALQBsAGkAIABtAGUAbgFhAO0AIABuAGUBftcAqAAOAKkA3QCq +AN4ArQDfA6oFAwUEAOMA5ADfgBmAuoEBCIEBCQmAGV8QFnt7MTAyLCAxODF9LCB7NjksIDE3fX3YAPEA +DgFZAPcA+AD5APsB0QD9AfoDGQULAQUE2gQgAgCAUoCZgQEKgB2BAQeAUGgAUAFZAGUAbgBvAHMAeQA6 +1wCoAA4AqQDdAKoA3gCtAN8DqgUTBRQA4wDkAN+AGYC6gQEMgQENCYAZXxAVe3s1NSwgMTd9LCB7MTE2 +LCAxN3192ADxAA4BWQD3APgA+QD7AdEA/QH6AxkFGwEFBNsEIAIAgFKAmYEBDoAdgQELgFBcQWt0dWFs +aXphY2U61wCoAA4AqQDdAKoA3gCtAN8DqgUjBSQA4wDkAN+AGYC6gQEQgQERCYAZXxAWe3sxMDIsIDI4 +N30sIHs2OSwgMTd9fdgA8QAOAVkA9wD4APkA+wHRAP0B+gMZBSsBBQTcBCACAIBSgJmBARKAHYEBD4BQ +agBQAVkAZQBwAO0AbgDhAG4A7QA61wCoAA4AqQDdAKoA3gCtAN8A4AUzBTQA4wDkAN+AGYAlgQEUgQEV +CYAZXxAXe3sxODMsIDE4MH0sIHsyMjQsIDE4fX3dAPEADgDyAPMA9AD1APYA9wD4APkA+gD7APwCcgD+ +AP8BAAEBAQIA/wU9AQUE3gEHAQgBCYAkgCOAIIAjgQEWgB2BARNvEBYAVADhAGgAbgBpACAAJgAgAFAA +dQBzAWUAIABtAGUAegBpACAATQBhAGMAedcAqAAOAKkA3QCqAN4ArQDfAOAFRAVFAOMA5ADfgBmAJYEB +GIEBGQmAGV8QF3t7MjExLCAxMDh9LCB7MTM0LCAxOH193QDxAA4A8gDzAPQA9QD2APcA+AD5APoA+wD8 +AP0A/gD/AQABAQECAP8FTgEFBN8BBwEIAQmAJIAjgCCAI4EBGoAdgQEXbxATAFABWQBpACAAcwB0AGkA +cwBrAHUAIABrAGwA4QB2AGUAcwB5ADrXAKgADgCpAN0AqgDeAK0A3wVUBVUFVgDjAOQA34AZgQE4gQEc +gQEdCYAZXxAXe3szNDYsIDEwM30sIHsxMjksIDI2fX3fEBMA8QVbBVwA8gDzAA4A9AD2APgFXQD5BV4F +XwVgAPoA+wVhAPwFYgVjAOQBbAD/AkoFZgFsBWcBBQVpBOIB4gDkAOQCUAVtBW4FbwFsXxAaTlNNZW51 +SXRlbVJlc3BlY3RBbGlnbm1lbnRfEA9OU0Fycm93UG9zaXRpb25aTlNNZW51SXRlbV8QD05TUHJlZmVy +cmVkRWRnZV8QEk5TVXNlc0l0ZW1Gcm9tTWVudV1OU0FsdGVyc1N0YXRlVk5TTWVudV8QD05TU2VsZWN0 +ZWRJbmRleBP/////hEH+QAmAI4EBN4EBHoAdgQEfgQEbCQkRCACBASASBoJA/9IADgKjAqQBH4B33ASC +AA4FcwV0BXUFdgV3BXgFYQSDBFQFeQVWBXsFfAV9AP8FfwWABYEFbgWDBYQBbFdOU1RpdGxlXxARTlNL +ZXlFcXVpdk1vZE1hc2taTlNLZXlFcXVpdl1OU01uZW1vbmljTG9jWU5TT25JbWFnZVxOU01peGVkSW1h +Z2VXTlNTdGF0ZYEBHYEBJ4EBIRIAEAAAgCMSf////4EBIoEBJIEBIIEBJhAT0wAOBXMFhgWHBYgFiVtO +U01lbnVJdGVtc4EBNoEBKIEBKWUjJQAgAEEAbAB00wAOADICmQKaApsFjoB1gHOBASNfEA9OU01lbnVD +aGVja21hcmvTAA4AMgKZApoCmwWTgHWAc4EBJV8QEE5TTWVudU1peGVkU3RhdGVfEBFfcG9wVXBJdGVt +QWN0aW9uOtIANwA4BZcFXaIFXQA70gAOAqMCpAWagHdaT3RoZXJWaWV3c9IADgA+AEYFnYBipQWeBWkF +oAWhBaKBASqBAR+BAS2BATCBATPbBIIADgVzBXQFdQV2BXcFeAVhBIMEVAVWBXsFpgV9AP8FfwWABYEF +bgWrBayBAR2BASeBASuAI4EBIoEBJIEBIIEBLBAUaCMYACAASgBhAGIAbABrAG/bBIIADgVzBXQFdQV2 +BXcFeAVhBIMEVAVWBXsFsQV9AP8FfwWABYEFbgW2BbeBAR2BASeBAS6AI4EBIoEBJIEBIIEBLxASaSMD +ACAAQwBvAG4AdAByAG8AbNsEggAOBXMFdAV1BXYFdwV4BWEEgwRUBVYFewW8BX0A/wV/BYAFgQVuBcEF +woEBHYEBJ4EBMYAjgQEigQEkgQEggQEyEBFnIecAIABTAGgAaQBmAHTbBIIADgVzBXQFdQV2BXcFeAVh +BIMEVAVWBXsFxwV9AP8FfwWABYEFbgXMBc2BAR2BASeBATSAI4EBIoEBJIEBIIEBNRAQayHqACAAQwBh +AHAAcwAgAEwAbwBjAGvSADcAOAXQBWGiBWEAO9IANwA4BdIF06YF0wXUASIBIwDdADtfEBFOU1BvcFVw +QnV0dG9uQ2VsbF5OU01lbnVJdGVtQ2VsbNIANwA4BdYF16YF1wEmAScAwQDFADtdTlNQb3BVcEJ1dHRv +btcAqAAOAKkA3QCqAN4ArQDfA6oF2wXcAOMA5ADfgBmAuoEBOoEBOwmAGV8QFnt7Mjk0LCAyNDl9LCB7 +NTIsIDI4fX3YAPEADgFZAPcA+AD5APsB0QD9AfoDGQXjAdYE4wXmAgCAUoCZgQE8gEOBATkSEEAAAIBQ +ZwBLAHIA4QB0AGsAbwB11wCoAA4AqQDdAKoA3gCtAN8A4AXsBe0A4wDkAN+AGYAlgQE+gQE/CYAZXxAX +e3sxODMsIDIxNH0sIHsyMTQsIDE4fX3dAPEADgDyAPMA9AD1APYA9wD4APkA+gD7APwA/QD+AP8BAAEB +AQIA/wX2AQUE5AEHAQgBCYAkgCOAIIAjgQFAgB2BAT1vEB4AUABvAGsAdQBzAGkAdAAgAHMAZQAgAHAA +cgBvAGIAdQBkAGkAdAAgAHMAcADtAGMA7QAgAE0AYQBjAHnXAKgADgCpAN0AqgDeAK0A3wDgBf0F/gDj +AOQA34AZgCWBAUKBAUMJgBlfEBd7ezE4MywgMjYyfSwgezE0NiwgMTh9fd0A8QAOAPIA8wD0APUA9gD3 +APgA+QD6APsA/AD9AP4A/wEAAQEBAgD/BgcBBQTlAQcBCAEJgCSAI4AggCOBAUSAHYEBQVtTIHByb2Rs +ZXZvddcAqAAOAKkA3QCqAN4ArQDfAOAGDgYPAOMA5ADfgBmAJYEBRoEBRwmAGV8QF3t7MTgzLCAyMzh9 +LCB7MjA0LCAxOH193QDxAA4A8gDzAPQA9QD2APcA+AD5APoA+wD8AP0A/gD/AQABAQECAP8GGAEFBOYB +BwEIAQmAJIAjgCCAI4EBSIAdgQFFbgBQAVkAaQAgAGQAdgBvAGoAZABvAHQAZQBrAHXXAKgADgCpAN0A +qgDeAK0A3wOqBh8GIADjAOQA34AZgLqBAUqBAUsJgBlfEBZ7ezM2MywgMTMxfSwgezU4LCAyMn192gDx +AA4BWQD4APkGJQD7A5EGJgHRBicB+gYpAQUE5wYsBi0A5AYvBjBbTlNGb3JtYXR0ZXJfEBNOU1BsYWNl +aG9sZGVyU3RyaW5nE/////+Ucf5BgFKBAXKAHYEBSYEBTBIEQAQACYEBcYEBdN8QEQAOBjIGMwY0BjUG +NgY3BjgGOQY6BjsGPAY9Bj4GPwZABkEGQgZDBkQGRQArBkcGSAZJBkoGSwDkBk0AKwZPBlAGUQZRVk5T +Lm5pbFpOUy5kZWNpbWFsVk5TLm5hbltOUy5yb3VuZGluZ1dOUy56ZXJvXxAQTlMubmVnYXRpdmVhdHRy +c1ZOUy5tYXhdTlMuYXR0cmlidXRlc18QEU5TLnBvc2l0aXZlZm9ybWF0XxAPTlMuYWxsb3dzZmxvYXRz +XxARTlMubmVnYXRpdmVmb3JtYXRfEBBOUy5wb3NpdGl2ZWF0dHJzW05TLnRob3VzYW5kVk5TLm1pblxO +Uy5sb2NhbGl6ZWRfEA9OUy5oYXN0aG91c2FuZHOBAXCBAWyBAWOBAW6AAIEBXYEBYoEBaIEBTYEBXAmB +AV6AAIEBaoEBWggI0wAOBlQAPgZVBlYGY1dOUy5rZXlzgQFrrAZXBlgGWQZaBlsGXAZdBl4GXwZgBmEG +YoEBToEBT4EBUIEBUYEBUoEBU4EBVIEBVYEBVoEBV4EBWIEBWawGUAZLBkcGSAZEBmkGagZrBkkGbQZN +Bk+BAVqBAVyBAV2BAWKBAWOBAWSBAWWBAWeBAWiBAWmBAV6BAWpXbWluaW11bV5wb3NpdGl2ZUZvcm1h +dF8QF2F0dHJpYnV0ZWRTdHJpbmdGb3JaZXJvXxAfdGV4dEF0dHJpYnV0ZXNGb3JOZWdhdGl2ZVZhbHVl +c18QEGRlY2ltYWxTZXBhcmF0b3JfEBFmb3JtYXR0ZXJCZWhhdmlvclZsb2NhbGVcYWxsb3dzRmxvYXRz +V21heGltdW1fEBV1c2VzR3JvdXBpbmdTZXBhcmF0b3JebmVnYXRpdmVGb3JtYXRfEBFncm91cGluZ1Nl +cGFyYXRvctcADgZ9Bn4GfwaABoEGggaDBlEBCAEBAQgGhQZRWk5TLmNvbXBhY3RbTlMuZXhwb25lbnRe +TlMubWFudGlzc2EuYm9ZTlMubGVuZ3RoW05TLm1hbnRpc3NhW05TLm5lZ2F0aXZlgQFbCE8QEAAAAAAA +AAAAAAAAAAAAAAAI0gA3ADgGiAaJogaJADtfEBpOU0RlY2ltYWxOdW1iZXJQbGFjZWhvbGRlclIwS9MA +DgaMAqgGjQaOBk1cTlNBdHRyaWJ1dGVzgQFhgQFfgQFeUTDTAA4GVAA+BpIGkwaUgQFgoKDSADcAOAaW +BpeiBpcAO1xOU0RpY3Rpb25hcnnSADcAOAaZBpqiBpoAO18QEk5TQXR0cmlidXRlZFN0cmluZ9MADgZU +AD4GkgadBp6BAWCgoGEAoBED6NIADgaiBqMA/11OUy5pZGVudGlmaWVygQFmgCPSADcAOAamBqeiBqcA +O1hOU0xvY2FsZdcADgZ9Bn4GfwaABoEGggaDAOQBCAEBAQEGqwZRgQFbCU8QEAAAQAAAAAAAAAAAAAAA +AAAIUSzSADcAOAavBrCjBrAGlwA7XxATTlNNdXRhYmxlRGljdGlvbmFyedIADgKoBo0Gs4EBYYEBbdMA +DgaMAqgGjQaOBreBAWGBAV+BAW9TTmFO0gA3ADgGuga7owa7BiUAO18QEU5TTnVtYmVyRm9ybWF0dGVy +WHZlbGlrb3N01QAOAeYB3wHoAekB4QFoAewGwAHugEWAX4EBc4BHXxATdGV4dEJhY2tncm91bmRDb2xv +ctUADgHmAd8B6AHpAeEB6wHsBsYB7oBFgEmBAXWAR1l0ZXh0Q29sb3LXAKgADgCpAN0AqgDeAK0A3wVU +BswGzQDjAOQA34AZgQE4gQF3gQF4CYAZXxAXe3szNjAsIDI4MX0sIHsxMjksIDI2fX3fEBMA8QVbBVwA +8gDzAA4A9AD2APgFXQD5BV4FXwVgAPoA+wVhAPwFYgVjAOQBbAD/AkoFZgFsBtUBBQbXBOgB4gDkAOQC +UAVtBtsFbwGACYAjgQE3gQF5gB2BAXqBAXYJCYEBe9IADgKjAqQBH4B33ASCAA4FcwV0BXUFdgV3BXgF +YQSDBFQFeQbNBXsFxwV9AP8FfwWABYEG2wbmBc0BbIEBeIEBJ4EBNIAjgQEigQEkgQF7gQF80wAOBXMF +hgWHBukG6oEBNoEBfYEBftIADgKjAqQFmoB30gAOAD4ARgbvgGKlBvAG8QbyBvMG14EBf4EBgYEBg4EB +hYEBetsEggAOBXMFdAV1BXYFdwV4BWEEgwRUBs0FewWmBX0A/wV/BYAFgQbbBv0FrIEBeIEBJ4EBK4Aj +gQEigQEkgQF7gQGA2wSCAA4FcwV0BXUFdgV3BXgFYQSDBFQGzQV7BXwFfQD/BX8FgAWBBtsHBgWEgQF4 +gQEngQEhgCOBASKBASSBAXuBAYLbBIIADgVzBXQFdQV2BXcFeAVhBIMEVAbNBXsFsQV9AP8FfwWABYEG +2wcPBbeBAXiBASeBAS6AI4EBIoEBJIEBe4EBhNsEggAOBXMFdAV1BXYFdwV4BWEEgwRUBs0FewW8BX0A +/wV/BYAFgQbbBxgFwoEBeIEBJ4EBMYAjgQEigQEkgQF7gQGG1wCoAA4AqQDdAKoA3gCtAN8A4AccBx0A +4wDkAN+AGYAlgQGIgQGJCYAZXxAWe3sxODMsIDUwfSwgezI3OSwgMTh9fd0A8QAOAPIA8wD0APUA9gD3 +APgA+QD6APsA/AD9AP4A/wEAAQEBAgD/ByYBBQTpAQcBCAEJgCSAI4AggCOBAYqAHYEBh28QJwBaAG8A +YgByAGEAegBpAHQAIABiAGUAegBlAGwAIABwAVkAaQAgAG8AdgBsAOEAZADhAG4A7QAgAGoAaQBuAOkA +aABvACAATQBhAGMAYdcAqAAOAKkA3QCqAN4ArQDfA6oHLQcuAOMA5ADfgBmAuoEBjIEBjQmAGV8QFXt7 +MTAyLCA3NX0sIHs2OSwgMTd9fdgA8QAOAVkA9wD4APkA+wHRAP0B+gMZBzUBBQTrBCACAIBSgJmBAY6A +HYEBi4BQVVN0YXY61wCoAA4AqQDdAKoA3gCtAN8HPAc9Bz4A4wDkAN+AGYEBloEBkIEBkQmAGV8QFnt7 +MzUxLCAyNjF9LCB7ODMsIDE1fX3eB0MA8QAOB0QHRQD3APgA+QdGB0cA+wdIB0kHSgdLAP0HTAEIAWwH +TQdOBOwHUAdRB1IGUQdUBlFXTlNWYWx1ZV8QE05TTnVtYmVyT2ZUaWNrTWFya3NfEBJOU1RpY2tNYXJr +UG9zaXRpb25aTlNNYXhWYWx1ZVpOU01pblZhbHVlWk5TVmVydGljYWxdTlNBbHRJbmNWYWx1ZV8QGk5T +QWxsb3dzVGlja01hcmtWYWx1ZXNPbmx5Iz/gAAAAAAAAgQGVgQGSgQGTgQGPIz/wAAAAAAAAIz+5mZmZ +mZmaEgACAAAIIwAAAAAAAAAACNIADgKjAqQBH4B31AAOAQwBDQEOAQ8HWgdbBc2AHyNAKAAAAAAAAIEB +lFlIZWx2ZXRpY2HSADcAOAdeB1+kB18BIwDdADtcTlNTbGlkZXJDZWxs0gA3ADgHYQdipQdiAScAwQDF +ADtYTlNTbGlkZXLXAKgADgCpAN0AqgDeAK0A3wOqB2YHZwDjAOQA34AZgLqBAZiBAZkJgBlfEBZ7ezQz +OSwgMjQ5fSwgezUxLCAyOH192ADxAA4BWQD3APgA+QD7AdEA/QH6AxkHbgHWBO0F5gIAgFKAmYEBmoBD +gQGXgFBXRGxvdWhvddcAqAAOAKkA3QCqAN4ArQDfAOAHdgd3AOMA5ADfgBmAJYEBnIEBnQmAGV8QFnt7 +MTgzLCAxNn0sIHsxOTgsIDE4fX3dAPEADgDyAPMA9AD1APYA9wD4APkA+gD7APwA/QD+AP8BAAEBAQIA +/weAAQUE7gEHAQgBCYAkgCOAIIAjgQGegB2BAZtvEBUATwB2ARsBWQBpAHQAIABwAVkAaQAgAHABWQBp +AGgAbADhAWEAZQBuAO1oAE0AbwF+AG4AbwBzAHQAadIANwA4B4YDyKQDyADBAMUAO1p7NTk1LCA0Mzh9 +Wns1OTUsIDQyOX1fEBZ7ezAsIDB9LCB7MTY4MCwgMTAyOH19XXsyMjQuNjY0LCAzMn1fEBp7My40MDI4 +MmUrMzgsIDMuNDAyODJlKzM4fdIANwA4B40HjqIHjgA7XxAQTlNXaW5kb3dUZW1wbGF0ZVdfd2luZG93 +1AAOAKAAoQCiAKMC0AAfB5SAEoCDgAKBAaxfEBdjaG9vc2VDZXJ0aWZpY2F0ZUJ1dHRvbtQADgCgAKEA +ogCjAVIHmQeagBKAMYEBroEBtdQADgecB50HngefB6AHoQeiXxAPX05TTWFuYWdlZFByb3h5XxARTlNP +YmplY3RDbGFzc05hbWVeTlNEZWNsYXJlZEtleXOBAbSBAbKBAbGBAa/SAA4APgBGB6WAYqEHpoEBsF8Q +FG51bWJlck9mU2VsZWN0ZWRSb3dz0QAOB6mBAbPSADcAOAerB5yiB5wAO9IANwA4B60HrqMHrgevADtf +EBJOU09iamVjdENvbnRyb2xsZXJcTlNDb250cm9sbGVyV2NvbnRlbnTUAA4AoAChAKIAowezAB8HtYAS +gQG3gAKBAezcA2MADgNkA2UDZgNnA2gDaQNqA2sDbANtAwQDbwe5AP8Huwe8B70HvgEBAWwHvwfAgJSB +AamBAemAIxJweAAAgQG5gQG4gQHrgQHqgQG6XxAYe3sxOTQsIDM3NX0sIHsyOTUsIDMwN319XxAPVFBD +bG9zaW5nV2luZG930gAOAqMCpAN8gHfSAA4APgBGB8eAYqcHyAfJAwAHywfMB80HzoEBvIEBy4CRgQHR +gQHWgQHdgQHi2gCoAA4AqQfQAN0H0QCqAN4AqwCtAwQH0wfUAOQH1gfXALwA5AMKAwRaTlNFZGl0YWJs +ZVtOU0RyYWdUeXBlc4CUgQHKgQHFCYEBxoEBvQmAloCU0gAOAD4APwfdgAenB94H3wfgB+EH4gfjB+SB +Ab6BAb+BAcCBAcGBAcKBAcOBAcRfEBlBcHBsZSBQREYgcGFzdGVib2FyZCB0eXBlXxAZQXBwbGUgUE5H +IHBhc3RlYm9hcmQgdHlwZV8QI0NvcmVQYXN0ZWJvYXJkRmxhdm9yVHlwZSAweDUwNEU0NzY2XxAVTlNG +aWxlbmFtZXNQYm9hcmRUeXBlXxAxTmVYVCBFbmNhcHN1bGF0ZWQgUG9zdFNjcmlwdCB2MS4yIHBhc3Rl +Ym9hcmQgdHlwZV8QHk5lWFQgVElGRiB2NC4wIHBhc3RlYm9hcmQgdHlwZV8QGkFwcGxlIFBJQ1QgcGFz +dGVib2FyZCB0eXBlXxAXe3syMCwgMTU5fSwgezI1NSwgMTI4fX3YAPEADgfuAPcH7wfwAPsH8QfyB/MB +CAf0AQgBCAf1BlFXTlNTdHlsZVdOU0FsaWduV05TU2NhbGVaTlNBbmltYXRlcxIgAf4AgQHJgQHHEgIA +AAAI0wAOADICmQKaApsH+oB1gHOBAchUaWNvbtIANwA4B/0H/qMH/gDdADtbTlNJbWFnZUNlbGzSADcA +OAgACAGlCAEBJwDBAMUAO1tOU0ltYWdlVmlld9gAqAAOAKkA3QCqAN4AqwCtAwQDqggFCAYAvADkAwoD +BICUgLqBAcyBAc0JgJaAlF8QFnt7MTcsIDEyOX0sIHsyNjEsIDIyfX3YAPEADgFZAPcA+AD5APsB0QD9 +AfoDGQgOCA8HyQMdAgCAUoCZgQHOgQHPgQHLgFBYdGVsZXBvcnTUAA4BDAENAQ4BDwgVCBYFzYAfI0Ay +AAAAAAAAgQHQXxARTHVjaWRhR3JhbmRlLUJvbGTYAKgADgCpAN0AqgDeAKsArQMEA6oIGwgcALwA5AMK +AwSAlIC6gQHSgQHTCYCWgJRfEBV7ezEyLCA0M30sIHsyNzQsIDM5fX3YAPEADgFZAPcA+AD5APsB0QD9 +AfoDGQgkCCUHywgnAgCAUoCZgQHUgQHVgQHREgBAAACAUG8QawCpACAAMgAwADAAMwAtADIAMAAwADgA +IABhAGIAeQBzAHMAbwBmAHQACgBQAW8AdgBvAGQAbgDtACAAbQB5AWEAbABlAG4AawBhADoAIABEAGEA +dgBlACAAUwBhAGcAIABhACAASgBlAHIAZQBtAHkAIABRAHUAaQBuAG4ALgAKAFoAdgBsAOEBYQB0AG4A +7QAgAHAAbwBkARsAawBvAHYA4QBuAO0AOgAgAE0AYQBuAHUAMAAyACwAIABKAHUAbABpAGUAbgAgAGEA +IABQAGEAdQBsAC7UAA4BDAENAQ4BDwgsAREILoAfI0AkAAAAAAAAgB4RCxvaAKgADgCpAVcA3QCqAN4A +qwAyAK0DBAFkCDIDBwg0CDUA5AMKCDgDBICUgGaBAdiAk4EB2REBAgmAloEB14CUXxAPVFBMaW5rVGV4 +dEZpZWxkXxATe3sxMiwgOH0sIHs1OSwgMTd9fdoA8QAOAVkA9wD4APkA+wORBiYB0Qg9AfoDGQhACEEH +zAXmAOQIRAIAEgQh/gGAUoCZgQHagQHbgQHWCYEB3IBQZSekACAAdwBlAGLUAA4BDAENAQ4BDwHbAREF +zYAfgB5fEBxodHRwOi8vdGVsZXBvcnQuYWJ5c3NvZnQuY29t2gCoAA4AqQFXAN0AqgDeAKsAMgCtAwQB +ZAhOAwcIUAg1AOQDCgg4AwSAlIBmgQHegJOBAd8JgJaBAdeAlF8QFHt7MTE5LCA4fSwgezYyLCAxN319 +2QDxAA4BWQD3APgA+QD7BiYB0QD9AfoDGQhZCEEHzQXmCFwCAIBSgJmBAeCBAduBAd2BAeGAUGknpAAg +AGQAaQBzAGsAdQBzAGVfEDFodHRwOi8vd3d3LmFieXNzb2Z0LmNvbS9zb2Z0d2FyZS90ZWxlcG9ydC9m +b3J1bXMv2gCoAA4AqQFXAN0AqgDeAKsAMgCtAwQBZAhjAwcIZQg1AOQDCgg4AwSAlIBmgQHjgJOBAeQJ +gJaBAdeAlF8QFXt7MjE4LCA4fSwgezYwMSwgMTd9fdkA8QAOAVkA9wD4APkA+wYmAdEA/QH6AxkIbghB +B84F5ghxAgCAUoCZgQHlgQHbgQHigQHmgFBrJ6QAIABwAVkAaQBzAHABGwBqAHQAZV8QrWh0dHBzOi8v +d3d3LnBheXBhbC5jb20vY2dpLWJpbi93ZWJzY3I/Y21kPV94Y2xpY2smYnVzaW5lc3M9anVsJTQwbWFj +JTJlY29tJml0ZW1fbmFtZT10ZWxlcG9ydCZub19zaGlwcGluZz0wJm5vX25vdGU9MCZ0YXg9MCZjdXJy +ZW5jeV9jb2RlPVVTRCZjaGFyc2V0PVVURiUyZDgmY2hhcnNldD1VVEYlMmQ4WnsyOTUsIDMwN31aezIx +MywgMTI5fVthYm91dFdpbmRvd9QADgCgAKEAogCjAB8HswCmgBKAAoEBt4AR1AAOAKAAoQCiAKMBUgAf +CIGAEoAxgAKBAe9fEBV0cnVzdGVkSG9zdHNUYWJsZVZpZXfUAA4AoAChAKIAowE1AB8Ih4ASgCuAAoEB +8VR2aWV31AAOAKAAoQCiAKMAHwC4AKaAEoACgLOAEdQADgCgAKEAogCjAB8IkQeagBKAAoEB9IEBtdQA +DgecB9AHngefCJUA5AiXgQG0gQIMCYEB9dIADgA+AEYImoBirxAWCJsInAidCJ4InwigCKEIogijCKQI +pQimCKcIqAipCKoIqwisCK0IrgivCLCBAfaBAfeBAfiBAfmBAfqBAfuBAfyBAf2BAf6BAf+BAgCBAgGB +AgKBAgOBAgSBAgWBAgaBAgeBAgiBAgmBAgqBAgtfEBJwcmVmcy5hbGxvd0NvbnRyb2xfEBVwcmVmcy5z +aGFyZVBhc3RlYm9hcmRfEBBwcmVmcy5yZXF1aXJlS2V5XxATcHJlZnMuZGVsYXllZFN3aXRjaF8QFHBy +ZWZzLnNob3dTdGF0dXNJdGVtXxAZcHJlZnMubGltaXRQYXN0ZWJvYXJkU2l6ZV8QGHByZWZzLm1heFBh +c3RlcmJvYXJkU2l6ZVZhY3RpdmVfEBZwcmVmcy5hdXRvY2hlY2tWZXJzaW9uXxAicHJlZnMucmVqZWN0 +QXV0aGVudGljYXRpb25SZXF1ZXN0c18QFnByZWZzLmVuYWJsZUVuY3J5cHRpb25fEBJwcmVmcy5zd2l0 +Y2hLZXlUYWdfEA9wcmVmcy5jb3B5RmlsZXNfEBpwcmVmcy5yZXF1aXJlUGFzdGVib2FyZEtleV8QFnBy +ZWZzLnBhc3RlYm9hcmRLZXlUYWdfEBpwcmVmcy50cnVzdFJlcXVlc3RCZWhhdmlvcl8QD3ByZWZzLndh +a2VPbkxBTl8QEXByZWZzLnN3aXRjaERlbGF5XxAabG9jYWxIb3N0LnN1cHBvcnREcmFnTkRyb3BfEBdw +cmVmcy5tYXhQYXN0ZWJvYXJkU2l6ZV8QFnByZWZzLmhpZGVDb250cm9sQmV6ZWxfEBlwcmVmcy5zd2l0 +Y2hXaXRoRG91YmxlVGFw0QAOB6mBAbPUAA4AoAChAKIAowTlAB8IzYASgQFBgAKBAg5fEBVkZWxheWVk +U3dpdGNoQ2hlY2tib3jUAA4AoAChAKIAowClAB8I04ASgAqAAoECEFpsYXlvdXRWaWV31AAOAKAAoQCi +AKMCsAAfCNmAEoB7gAKBAhJfEBVzaG93Q2VydGlmaWNhdGVCdXR0b27XAA4AoAjcCN0AoQCiCN4I3wiR +COEI4gTpCOQBAVlOU0tleVBhdGhZTlNCaW5kaW5nXxAcTlNOaWJCaW5kaW5nQ29ubmVjdG9yVmVyc2lv +boECF4EB9IECFoECFYEBh4ECFF8QGWVuYWJsZWQ6IHNlbGVjdGlvbi5hY3RpdmVXZW5hYmxlZF8QEHNl +bGVjdGlvbi5hY3RpdmXSADcAOAjpCOqjCOoAygA7XxAVTlNOaWJCaW5kaW5nQ29ubmVjdG9y1wAOAKAI +3AjdAKEAogjeCN8IkQjuCO8E3wjxAQGBAheBAfSBAhuBAhqBAReBAhlfECt2YWx1ZTogc2VsZWN0aW9u +LnByZWZzLnJlcXVpcmVQYXN0ZWJvYXJkS2V5VXZhbHVlXxAkc2VsZWN0aW9uLnByZWZzLnJlcXVpcmVQ +YXN0ZWJvYXJkS2V51wAOAKAI3AjdAKEAogjeCN8IkQj4CO8D6Qj7AQGBAheBAfSBAh6BAhqAy4ECHV8Q +J3ZhbHVlOiBzZWxlY3Rpb24ucHJlZnMuZW5hYmxlRW5jcnlwdGlvbl8QIHNlbGVjdGlvbi5wcmVmcy5l +bmFibGVFbmNyeXB0aW9u1wAOAKAI3AjdAKEAogjeCN8IkQjhCOIE7gjkAQGBAheBAfSBAhaBAhWBAZuB +AhTXAA4AoAjcCN0AoQCiCN4I3wiRCQgI4gTfCQsBAYECF4EB9IECIoECFYEBF4ECIV8QKGVuYWJsZWQ6 +IHNlbGVjdGlvbi5wcmVmcy5zaGFyZVBhc3RlYm9hcmRfEB9zZWxlY3Rpb24ucHJlZnMuc2hhcmVQYXN0 +ZWJvYXJk2QAOAKAI3AjdCQ8AoQCiCRAI3gjfCJEI4QkUAGgE3wkXCRgBAV8QE05TUHJldmlvdXNDb25u +ZWN0b3JZTlNPcHRpb25zgQIXgQH0gQIWgQIlgQIggQEXgQIkgQImXxAaZW5hYmxlZDI6IHNlbGVjdGlv +bi5hY3RpdmVYZW5hYmxlZDLTAA4GVAA+BpIJHQkigQFgpAkeCR8JIAkhgQIngQIogQIpgQIqpAkjCSMJ +IwkjgQIrgQIrgQIrgQIrXxARTlNOdWxsUGxhY2Vob2xkZXJfEBpOU05vdEFwcGxpY2FibGVQbGFjZWhv +bGRlcl8QGE5TTm9TZWxlY3Rpb25QbGFjZWhvbGRlcl8QG05TTXVsdGlwbGVWYWx1ZXNQbGFjZWhvbGRl +chP//////////9cADgCgCNwI3QChAKII3gjfCJEI4QjiBOYI5AEBgQIXgQH0gQIWgQIVgQFFgQIU1wAO +AKAI3AjdAKEAogjeCN8IkQk2CO8E5Qk5AQGBAheBAfSBAi+BAhqBAUGBAi5fECR2YWx1ZTogc2VsZWN0 +aW9uLnByZWZzLmRlbGF5ZWRTd2l0Y2hfEB1zZWxlY3Rpb24ucHJlZnMuZGVsYXllZFN3aXRjaNcADgCg +CNwI3QChAKII3gjfCJEI4QjiAVII5AEBgQIXgQH0gQIWgQIVgDGBAhTXAA4AoAjcCN0AoQCiCN4I3wiR +CUYI7wTZCUkBAYECF4EB9IECM4ECGoEBA4ECMl8QKnZhbHVlOiBzZWxlY3Rpb24ucHJlZnMubGltaXRQ +YXN0ZWJvYXJkU2l6ZV8QI3NlbGVjdGlvbi5wcmVmcy5saW1pdFBhc3RlYm9hcmRTaXpl1wAOAKAI3Ajd +AKEAogjeCN8IkQjhCOIC6QjkAQGBAheBAfSBAhaBAhWAioECFNcADgCgCNwI3QChAKII3gjfCJEJVgjv +BOcJWQEBgQIXgQH0gQI3gQIagQFJgQI2XxAodmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5tYXhQYXN0ZWJv +YXJkU2l6ZV8QIXNlbGVjdGlvbi5wcmVmcy5tYXhQYXN0ZWJvYXJkU2l6ZdcADgCgCNwI3QChAKII3gjf +CJEJXwlgA+sJYgEBgQIXgQH0gQI7gQI6gNOBAjlfEDFzZWxlY3RlZFRhZzogc2VsZWN0aW9uLnByZWZz +LnRydXN0UmVxdWVzdEJlaGF2aW9yW3NlbGVjdGVkVGFnXxAkc2VsZWN0aW9uLnByZWZzLnRydXN0UmVx +dWVzdEJlaGF2aW9y1wAOAKAI3AjdAKEAogjeCN8IkQkICOIE2QkLAQGBAheBAfSBAiKBAhWBAQOBAiHZ +AA4AoAjcCN0JDwChAKIJEAjeCN8IkQjhCRQAcQTZCRcJdQEBgQIXgQH0gQIWgQIlgQI8gQEDgQIkgQI+ +0wAOBlQAPgaSCXgJfYEBYKQJHgkfCSAJIYECJ4ECKIECKYECKqQJIwkjCSMJI4ECK4ECK4ECK4ECK9cA +DgCgCNwI3QChAKII3gjfCJEJCAjiBOcJCwEBgQIXgQH0gQIigQIVgQFJgQIh2QAOAKAI3AjdCQ8AoQCi +CRAI3gjfCJEI4QkUAHME5wkXCZEBAYECF4EB9IECFoECJYECP4EBSYECJIECQdMADgZUAD4GkgmUCZmB +AWCkCR4JHwkgCSGBAieBAiiBAimBAiqkCSMJIwkjCSOBAiuBAiuBAiuBAivZAA4AoAjcCN0JDwChAKIJ +EAjeCN8IkQlGCaIAdATnCaUJpgEBgQIXgQH0gQIzgQJEgQJAgQFJgQJDgQJFXxAtZW5hYmxlZDM6IHNl +bGVjdGlvbi5wcmVmcy5saW1pdFBhc3RlYm9hcmRTaXplWGVuYWJsZWQz0wAOBlQAPgaSCasJsIEBYKQJ +HgkfCSAJIYECJ4ECKIECKYECKqQJIwkjCSMJI4ECK4ECK4ECK4ECK9cADgCgCNwI3QChAKII3gjfCJEJ +uAjvBO4JuwEBgQIXgQH0gQJIgQIagQGbgQJHXxAndmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5hdXRvY2hl +Y2tWZXJzaW9uXxAgc2VsZWN0aW9uLnByZWZzLmF1dG9jaGVja1ZlcnNpb27XAA4AoAjcCN0AoQCiCN4I +3wiRCcEI7wTkCcQBAYECF4EB9IECS4ECGoEBPYECSl8QIHZhbHVlOiBzZWxlY3Rpb24ucHJlZnMud2Fr +ZU9uTEFOXxAZc2VsZWN0aW9uLnByZWZzLndha2VPbkxBTtcADgCgCNwI3QChAKII3gjfCJEJygjiBOgJ +zQEBgQIXgQH0gQJOgQIVgQF2gQJNXxAjZW5hYmxlZDogc2VsZWN0aW9uLnByZWZzLnJlcXVpcmVLZXlf +EBpzZWxlY3Rpb24ucHJlZnMucmVxdWlyZUtledkADgCgCNwI3QkPAKEAogkQCN4I3wiRCOEJFAB4BOgJ +FwnYAQGBAheBAfSBAhaBAiWBAkyBAXaBAiSBAlDTAA4GVAA+BpIJ2wnggQFgpAkeCR8JIAkhgQIngQIo +gQIpgQIqpAkjCSMJIwkjgQIrgQIrgQIrgQIr1wAOAKAI3AjdAKEAogjeCN8IkQnoCO8DQgnrAQGBAheB +AfSBAlOBAhqAo4ECUl8QJXZhbHVlOiBzZWxlY3Rpb24ucHJlZnMuc2hvd1N0YXR1c0l0ZW1fEB5zZWxl +Y3Rpb24ucHJlZnMuc2hvd1N0YXR1c0l0ZW3XAA4AoAjcCN0AoQCiCN4I3wiRCOEI4gNCCOQBAYECF4EB +9IECFoECFYCjgQIU1wAOAKAI3AjdAKEAogjeCN8IkQn4CO8E5gn7AQGBAheBAfSBAleBAhqBAUWBAlZf +ECp2YWx1ZTogc2VsZWN0aW9uLnByZWZzLnN3aXRjaFdpdGhEb3VibGVUYXBfECNzZWxlY3Rpb24ucHJl +ZnMuc3dpdGNoV2l0aERvdWJsZVRhcNcADgCgCNwI3QChAKII3gjfCJEI+AjiArAKBAEBgQIXgQH0gQIe +gQIVgHuBAllfECllbmFibGVkOiBzZWxlY3Rpb24ucHJlZnMuZW5hYmxlRW5jcnlwdGlvbtkADgCgCNwI +3QkPAKEAogkQCN4I3wiRCOEJFAB9ArAJFwoOAQGBAheBAfSBAhaBAiWBAliAe4ECJIECW9MADgZUAD4G +kgoRChaBAWCkCR4JHwkgCSGBAieBAiiBAimBAiqkCSMJIwkjCSOBAiuBAiuBAiuBAivXAA4AoAjcCN0A +oQCiCN4I3wiRCOEI4gMpCOQBAYECF4EB9IECFoECFYCdgQIU1wAOAKAI3AjdAKEAogjeCN8IkQolCO8E +3gooAQGBAheBAfSBAl+BAhqBAROBAl5fECB2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmNvcHlGaWxlc18Q +GXNlbGVjdGlvbi5wcmVmcy5jb3B5RmlsZXPXAA4AoAjcCN0AoQCiCN4I3wiRCi4JYAToCjEBAYECF4EB +9IECYoECOoEBdoECYV8QKXNlbGVjdGVkVGFnOiBzZWxlY3Rpb24ucHJlZnMuc3dpdGNoS2V5VGFnXxAc +c2VsZWN0aW9uLnByZWZzLnN3aXRjaEtleVRhZ9cADgCgCNwI3QChAKII3gjfCJEI7gjiBOIKOgEBgQIX +gQH0gQIbgQIVgQEbgQJkXxAtZW5hYmxlZDogc2VsZWN0aW9uLnByZWZzLnJlcXVpcmVQYXN0ZWJvYXJk +S2V52QAOAKAI3AjdCQ8AoQCiCRAI3gjfCJEJCAkUAIIE4gpDCkQBAYECF4EB9IECIoECJYECY4EBG4EC +ZoECZ18QKWVuYWJsZWQyOiBzZWxlY3Rpb24ucHJlZnMuc2hhcmVQYXN0ZWJvYXJk0wAOBlQAPgaSCkgK +TYEBYKQJHgkfCSAJIYECJ4ECKIECKYECKqQJIwkjCSMJI4ECK4ECK4ECK4ECK9kADgCgCNwI3QkPAKEA +ogkQCN4I3wiRCOEJogCDBOIKWQpaAQGBAheBAfSBAhaBAkSBAmWBARuBAmmBAmpfEBplbmFibGVkMzog +c2VsZWN0aW9uLmFjdGl2ZdMADgZUAD4GkgpeCmOBAWCkCR4JHwkgCSGBAieBAiiBAimBAiqkCSMJIwkj +CSOBAiuBAiuBAiuBAivXAA4AoAjcCN0AoQCiCN4I3wiRCOEI4gOnCOQBAYECF4EB9IECFoECFYC2gQIU +1wAOAKAI3AjdAKEAogjeCN8IkQjhCOICZgjkAQGBAheBAfSBAhaBAhWAaIECFNcADgCgCNwI3QChAKII +3gjfCJEKeQjvBOwKfAEBgQIXgQH0gQJvgQIagQGPgQJuXxAidmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5z +d2l0Y2hEZWxheV8QG3NlbGVjdGlvbi5wcmVmcy5zd2l0Y2hEZWxhedcADgCgCNwI3QChAKII3gjfCJEI ++AjiAtAKBAEBgQIXgQH0gQIegQIVgIOBAlnZAA4AoAjcCN0JDwChAKIJEAjeCN8IkQjhCRQAiALQCRcK +jgEBgQIXgQH0gQIWgQIlgQJwgIOBAiSBAnLTAA4GVAA+BpIKkQqWgQFgpAkeCR8JIAkhgQIngQIogQIp +gQIqpAkjCSMJIwkjgQIrgQIrgQIrgQIr1wAOAKAI3AjdAKEAogjeCN8IkQjhCOID6wjkAQGBAheBAfSB +AhaBAhWA04ECFNcADgCgCNwI3QChAKII3gjfCJEI4QjiBOUI5AEBgQIXgQH0gQIWgQIVgQFBgQIU1wAO +AKAI3AjdAKEAogjeCN8IkQjhCOIA2QjkAQGBAheBAfSBAhaBAhWAGIECFNcADgCgCNwI3QChAKII3gjf +CJEKswjvAykKtgEBgQIXgQH0gQJ4gQIagJ2BAndfECN2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmFsbG93 +Q29udHJvbF8QHHNlbGVjdGlvbi5wcmVmcy5hbGxvd0NvbnRyb2zXAA4AoAjcCN0AoQCiCN4I3wiRCOEI +4gPpCOQBAYECF4EB9IECFoECFYDLgQIU1wAOAKAI3AjdAKEAogjeCN8IkQjhCOIE5AjkAQGBAheBAfSB +AhaBAhWBAT2BAhTXAA4AoAjcCN0AoQCiCN4I3wiRCcoI7wDZCs0BAYECF4EB9IECToECGoAYgQJ8XxAh +dmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5yZXF1aXJlS2V51wAOAKAI3AjdAKEAogjeCN8IkQjhCOIE3gjk +AQGBAheBAfSBAhaBAhWBAROBAhTZAA4AoAjcCN0JDwChAKIJEAjeCN8IkQrZCRQAkQTeCt0K3gEBgQIX +gQH0gQKAgQIlgQJ9gQETgQJ/gQKBXxAuZW5hYmxlZDI6IHNlbGVjdGlvbi5sb2NhbEhvc3Quc3VwcG9y +dERyYWdORHJvcF8QJHNlbGVjdGlvbi5sb2NhbEhvc3Quc3VwcG9ydERyYWdORHJvcNMADgZUAD4Gkgrj +CuiBAWCkCR4JHwkgCSGBAieBAiiBAimBAiqkCSMJIwkjCSOBAiuBAiuBAiuBAivXAA4AoAjcCN0AoQCi +CN4I3wiRCvAJYATiCvMBAYECF4EB9IEChIECOoEBG4ECg18QLXNlbGVjdGVkVGFnOiBzZWxlY3Rpb24u +cHJlZnMucGFzdGVib2FyZEtleVRhZ18QIHNlbGVjdGlvbi5wcmVmcy5wYXN0ZWJvYXJkS2V5VGFn1wAO +AKAI3AjdAKEAogjeCN8IkQkICO8CZgr8AQGBAheBAfSBAiKBAhqAaIEChl8QJnZhbHVlOiBzZWxlY3Rp +b24ucHJlZnMuc2hhcmVQYXN0ZWJvYXJk2AAOAKAI3AjdAKEAogkQCN4I3wiRCwEI7wTpCwQLBQEBgQIX +gQH0gQKJgQIagQGHgQKIgQKKXxAndmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5oaWRlQ29udHJvbEJlemVs +XxAgc2VsZWN0aW9uLnByZWZzLmhpZGVDb250cm9sQmV6ZWzTAA4GVAA+BpILCgsMgQFgoQsLgQKLoQsN +gQKMXxAWTlNWYWx1ZVRyYW5zZm9ybWVyTmFtZV8QD05TTmVnYXRlQm9vbGVhbtcADgCgCNwI3QChAKII +3gjfCJEI4QjvATELFgEBgQIXgQH0gQIWgQIagCqBAo5fEBd2YWx1ZTogc2VsZWN0aW9uLmFjdGl2ZdcA +DgCgCNwI3QChAKII3gjfCJEJNgjiBOwLHgEBgQIXgQH0gQIvgQIVgQGPgQKQXxAmZW5hYmxlZDogc2Vs +ZWN0aW9uLnByZWZzLmRlbGF5ZWRTd2l0Y2jZAA4AoAjcCN0JDwChAKIJEAjeCN8IkQjhCRQAlwTsCRcL +KAEBgQIXgQH0gQIWgQIlgQKPgQGPgQIkgQKS0wAOBlQAPgaSCysLMIEBYKQJHgkfCSAJIYECJ4ECKIEC +KYECKqQJIwkjCSMJI4ECK4ECK4ECK4ECK9QADgCgCzYLNws4AykLOgs7WE5TTWFya2VyVk5TRmlsZYEC +loCdgQKVgQKUXxAQTlNUb29sVGlwSGVscEtleW8QOQBaAGEBYQBrAHIAdABuAHUAdADtAG0AIAB1AG0A +bwF+AG4A7QB0AGUAIABvAHMAdABhAHQAbgDtAG0AIAB1AX4AaQB2AGEAdABlAGwBbwBtACAAbwB2AGwA +4QBkAGEAdAAgAHYA4QFhACAATQBhAGMALtIANwA4Cz8LQKILQAA7XxARTlNJQkhlbHBDb25uZWN0b3LU +AA4AoAs2CzcLOAJ/C0QLO4ECloBugQKYgQKUbxAPAEMAbwAgAGoAZQAgAHQAZQBsAGUAcABvAHIAdCAm +1AAOAKALNgs3CzgBMQtKCzuBApaAKoECmoEClG8QIABaAGEBYQBrAHIAdABuAHUAdADtAG0AIABhAGsA +dABpAHYAdQBqAGUAdABlACAAdABlAGwAZQBwAG8AcgB0AC7UAA4AoAs2CzcLOATmC1ALO4ECloEBRYEC +nIEClG8QaABLACAAZAB2AG8AagBkAG8AdABlAGsAdQAgAGQAbwBjAGgA4QB6AO0ALAAgAGsAZAB5AX4A +IABzAGUAIABkAG8AdABrAG4AZQB0AGUAIABrAHUAcgB6AG8AcgBlAG0AIABvAGsAcgBhAGoAZQAgAG8A +YgByAGEAegBvAHYAawB5ACwAIAB2AHIA4QB0AO0AdABlACAAcwBlACAAegBwARsAdAAgAGEAIABkAG8A +dABrAG4AZQB0AGUAIABzAGUAIABwAG8AZAByAHUAaADpAC7UAA4AoAs2CzcLOATkC1YLO4ECloEBPYEC +noEClG8QpQBQAG8AdQB6AGUAIABuAGEAIABwAG8BDQDtAHQAYQENAO0AYwBoACAAcAFZAGkAcABvAGoA +ZQBuAP0AYwBoACAAawAgAHMA7QB0AGkAIABFAHQAaABlAHIAbgBlAHQAIABzAGUAIAB6AGEAcABuAHUA +dABvAHUAIAB2AG8AbABiAG8AdQAgACIAUAByAG8AYgB1AGQAaQB0ACAAcAByAG8AIABwAVkA7QBzAHQA +dQBwACAAYQBkAG0AaQBuAGkAcwB0AHIA4QB0AG8AcgBhACAAcAFZAGUAcwAgAEUAdABoAGUAcgBuAGUA +dAAiACAAegBhAWEAawByAHQAbgB1AHQAbwB1ACAAdgAgAG8AdgBsAOEAZABhAGMA7QBtACAAcABhAG4A +ZQBsAHUAIADaAHMAcABvAHIAYQAgAGUAbgBlAHIAZwBpAGUALtQADgCgCzYLNws4AKULXAs7gQKWgAqB +AqCBApRvEG8AVABvAHQAbwAgAGoAZQAgAG8AYgBsAGEAcwB0ACwAIAB2AGUAIABrAHQAZQByAOkAIAB1 +AHIBDQB1AGoAZQB0AGUAIAByAG8AegBtAO0AcwB0ARsAbgDtACAAagBlAGQAbgBvAHQAbABpAHYA/QBj +AGgAIABwAGwAbwBjAGgALgAgAFABWQBlAHQA4QBoAG4BGwB0AGUAIABvAHYAbADhAGQAYQBuAOkAIABw +AG8BDQDtAHQAYQENAGUAIABuAGEAIAB6AHYAbwBsAGUAbgDpACAAbQDtAHMAdABvAC7SAA4APgtgC2GB +AqSvEH8D7gQxAwgIHAUkB5kIZQDfBiABvAWeAwQBsALpBg8E5gTzA+kE4wYsAacE6ATtBEMIBgfJAWcF +NAVuArACtgQFB7MG1wIzBRQBugQWB84EpwDZAn8E5AKFBy4DQgPqBLcE6wdnAT0E3gPFBs0F7QTiBQQB +NQRCA8YAuAX+AykHPgNfBVYD6wcdAagF3AOsBOUIUAbbBNsHywJmAX0DMAE4BaAHzAbwA/MCHADiC7gH +zQiRAu8ApQLWBOkCbQTuBNoBuwbzBNkFaQTcB3cD7QNJAK4EMgfIA+YA6AWhB9YE3wFSBOcG8gVFAtAG +8QOnBOwBywOeAwAINAK6ATEFooD3gOKAl4EB04EBEYEBroEB5IAZgQFLgFmBASqAlIDpgIqBAUeBAUWB +AQWAy4EBOYEBTIB8gQF2gQGXgN2BAc2BAcuANoEBFYEBIIB7gH6AzYEBt4EBeoBggQENgD+A0YEB4oD1 +gBiAboEBPYBwgQGNgKOAz4D5gQGLgQGZgLCBAROAw4EBeIEBP4EBG4EBCYArgNuA/oCzgQFDgJ2BAZGA +q4EBHYDTgQGJgO2BATuAuIEBQYEB34EBe4EBC4EB0YBogDuAn4AtgQEtgQHWgQF/gMmAWIAbgQKigQHd +gQH0gIyACoCFgQGHgGqBAZuBAQeAVIEBhYEBA4EBH4EBD4EBnYDzgKWAC4DWgQG8gMeBAQCBATCBAcaB +AReAMYEBSYEBg4EBGYCDgQGBgLaBAY+AS4C/gJGBAdmAxIAqgQEz0gAOADIAMwvjgASBAqNdTlNBcHBs +aWNhdGlvbtIANwA4C+YCV6ICVwA70gAOAD4LYAvpgQKkrxB/AacD6wMAB8sE3AAfB84A6ATnAVIFbgez +AX0A3wTmAN8E2QGnAN8GIAK6AN8A3wPrB8kDBAF9BN4FVgGnArAD6QAfBtsBvATbAVID6gMEA+0A3wE1 +AN8CfwTrAN8BpwPuAN8E7QNfAN8AuAToBOQA3wTaAT0D6wC4ATUE5QE1BOwAHwTiAacE6QF9BOMDpwDf +B80GzQDfAwQA3wGnAykBMQVuAwQG2wPmAbsA2QAfAwQAHwLpAK4C0ADfAmYA3wDfAVIG2wDfBW4A3wTu +AacDQgOeA+sDBAGnA8YFbgfIAN8BfQDfBtsE3wGnBtsArgDfAboAuAMEB8wDxQE1BW6AfIDTgJGBAdGB +AQ+AAoEB4oEBAIEBSYAxgQEggQG3gDuAGYEBRYAZgQEDgHyAGYEBS4DEgBmAGYDTgQHLgJSAO4EBE4EB +HYB8gHuAy4ACgQF7gFmBAQuAMYDPgJSA84AZgCuAGYBugQGLgBmAfID3gBmBAZeAq4AZgLOBAXaBAT2A +GYEBB4CwgNOAs4ArgQFBgCuBAY+AAoEBG4B8gQGHgDuBATmAtoAZgQHdgQF4gBmAlIAZgHyAnYAqgQEg +gJSBAXuAx4BUgBiAAoCUgAKAioALgIOAGYBogBmAGYAxgQF7gBmBASCAGYEBm4B8gKOAv4DTgJSAfID+ +gQEggQG8gBmAO4AZgQF7gQEXgHyBAXuAC4AZgD+As4CUgQHWgMOAK4EBINIADgA+C2AMa4ECpK8QgAMI +BDEHmQPuBSQFoghlAwQA3wYgAbwFngGwAukGDwTmBPMD6QTjBiwBpwToBO0EQwgGB8kBZwU0BW4CsAK2 +B7MEBQAfBtcFFAfOAboEFgIzANkEpwJ/BOQChQcuA0ID6gS3BOsHZwE9A8UE3gbNBe0E4gUEATUDxgRC +ALgF/gMpBz4DXwVWA+sHHQGoBdwDrATlCFAG2wTbB8sCZgF9AzABOAfMBaAG8APzB80A4gu4CJECHACl +Au8C1gTpBNoE7gTZAm0BuwbzBWkArgTcA+0DSQd3B8gEMgPmAOgH1gWhBN8INATnAVIDpwLQBUUG8gTs +AwADngHLBvECugExCByAl4DigQGugPeBARGBATOBAeSAlIAZgQFLgFmBASqA6YCKgQFHgQFFgQEFgMuB +ATmBAUyAfIEBdoEBl4DdgQHNgQHLgDaBARWBASCAe4B+gQG3gM2AAoEBeoEBDYEB4oA/gNGAYIAYgPWA +boEBPYBwgQGNgKOAz4D5gQGLgQGZgLCAw4EBE4EBeIEBP4EBG4EBCYArgP6A24CzgQFDgJ2BAZGAq4EB +HYDTgQGJgO2BATuAuIEBQYEB34EBe4EBC4EB0YBogDuAn4AtgQHWgQEtgQF/gMmBAd2AG4ECooEB9IBY +gAqAjICFgQGHgQEHgQGbgQEDgGqAVIEBhYEBH4ALgQEPgPOApYEBnYEBvIDWgMeBAQCBAcaBATCBAReB +AdmBAUmAMYC2gIOBARmBAYOBAY+AkYC/gEuBAYGAxIAqgQHT0gAOAD4LYAzugQKkrxCADO8M8AzxDPIM +8wz0DPUM9gz3DPgM+Qz6DPsM/Az9DP4M/w0ADQENAg0DDQQNBQ0GDQcNCA0JDQoNCw0MDQ0NDg0PDRAN +EQ0SDRMNFA0VDRYNFw0YDRkNGg0bDRwNHQ0eDR8NIA0hDSINIw0kDSUNJg0nDSgNKQ0qDSsNLA0tDS4N +Lw0wDTENMg0zDTQNNQ02DTcNOA05DToNOw08DT0NPg0/DUANQQ1CDUMNRA1FDUYNRw1IDUkNSg1LDUwN +TQ1ODU8NUA1RDVINUw1UDVUNVg1XDVgNWQ1aDVsNXA1dDV4NXw1gDWENYg1jDWQNZQ1mDWcNaA1pDWoN +aw1sDW0NboECqIECqYECqoECq4ECrIECrYECroECr4ECsIECsYECsoECs4ECtIECtYECtoECt4ECuIEC +uYECuoECu4ECvIECvYECvoECv4ECwIECwYECwoECw4ECxIECxYECxoECx4ECyIECyYECyoECy4ECzIEC +zYECzoECz4EC0IEC0YEC0oEC04EC1IEC1YEC1oEC14EC2IEC2YEC2oEC24EC3IEC3YEC3oEC34EC4IEC +4YEC4oEC44EC5IEC5YEC5oEC54EC6IEC6YEC6oEC64EC7IEC7YEC7oEC74EC8IEC8YEC8oEC84EC9IEC +9YEC9oEC94EC+IEC+YEC+oEC+4EC/IEC/YEC/oEC/4EDAIEDAYEDAoEDA4EDBIEDBYEDBoEDB4EDCIED +CYEDCoEDC4EDDIEDDYEDDoEDD4EDEIEDEYEDEoEDE4EDFIEDFYEDFoEDF4EDGIEDGYEDGoEDG4EDHIED +HYEDHoEDH4EDIIEDIYEDIoEDI4EDJIEDJYEDJoEDJ18QG1RleHQgRmllbGQgQ2VsbCAoPHZlcnNpb24+ +KV8QJ1Byb3RvdHlwZSBQcm90b3R5cGUgQnV0dG9uIENlbGwgKFJhZGlvKV8QEVRydXN0ZWRIb3N0c1Rh +YmxlbxAhAFMAdABhAHQAaQBjACAAVABlAHgAdAAgACgARAFvAHYBGwByAHkAaABvAGQAbgDpACAAcwB0 +AHIAbwBqAGUAOgApbxAcAFQAZQB4AHQAIABGAGkAZQBsAGQAIABDAGUAbABsACAAKABQAVkAZQBwAO0A +bgDhAG4A7QA6AClvEBkATQBlAG4AdQAgAEkAdABlAG0AIAAoIeoAIABDAGEAcABzACAATABvAGMAawAp +AC0AMW8QHQBUAGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgnpAAgAHABWQBpAHMAcAEbAGoA +dABlACleQ29udGVudCBWaWV3LTFdQ3VzdG9tIFZpZXctMl8QD1RleHQgRmllbGQgQ2VsbF8QGlRhYmxl +IENvbHVtbiAoY2VydGlmaWNhdGUpbxAWAE0AZQBuAHUAIABJAHQAZQBtACAAKCMYACAASgBhAGIAbABr +AG8AKQAtADFfEBFWZXJ0aWNhbCBTY3JvbGxlcm8QGABQAHUAcwBoACAAQgB1AHQAdABvAG4AIAAoAE8A +dgEbAVkAaQB0ACAAdABlAQ8AKW8QHABCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAFABWQBpACAAZAB2 +AG8AagBkAG8AdABlAGsAdQApbxAaAEMAaABlAGMAawAgAEIAbwB4ACAAKABQAVkAaQAgAGQAdgBvAGoA +ZABvAHQAZQBrAHUAKW8QHQBCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAEoAZQAtAGwAaQAgAG0AZQBu +AWEA7QAgAG4AZQF+AClvEB0AQwBoAGUAYwBrACAAQgBvAHgAIAAoAFAAbwB2AG8AbABpAHQAIAFhAGkA +ZgByAG8AdgDhAG4A7QApbxAVAFMAdABhAHQAaQBjACAAVABlAHgAdAAgACgASwByAOEAdABrAG8AdQAp +XxAQTnVtYmVyIEZvcm1hdHRlcl1DdXN0b20gVmlldy0xbxAaAFAAbwBwAHUAcAAgAEIAdQB0AHQAbwBu +ACAAKCHqACAAQwBhAHAAcwAgAEwAbwBjAGsAKV8QFVN0YXRpYyBUZXh0IChEbG91aG91KV8QJEJ1dHRv +biBDZWxsIChBdXRvbWF0aWNreSBwb3NreXRub3V0KV8QGlRleHQgRmllbGQgQ2VsbCAodGVsZXBvcnQp +XxAWU3RhdGljIFRleHQgKHRlbGVwb3J0KV8QEVRhYmxlIEhlYWRlciBWaWV3bxAkAEIAdQB0AHQAbwBu +ACAAQwBlAGwAbAAgACgAVADhAGgAbgBpACAAJgAgAFAAdQBzAWUAIABtAGUAegBpACAATQBhAGMAeQAp +XxATTWVudSAoT3RoZXJWaWV3cyktMW8QIQBQAHUAcwBoACAAQgB1AHQAdABvAG4AIAAoAFoAbwBiAHIA +YQB6AGkAdAAgAGMAZQByAHQAaQBmAGkAawDhAHQAKW8QIQBCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAo +AFoAbwBiAHIAYQB6AGkAdAAgAGMAZQByAHQAaQBmAGkAawDhAHQAKVVBYm91dG8QHwBCAHUAdAB0AG8A +bgAgAEMAZQBsAGwAIAAoAFAAbwB2AG8AbABpAHQAIAFhAGkAZgByAG8AdgDhAG4A7QApXEZpbGUncyBP +d25lcm8QFwBNAGUAbgB1ACAASQB0AGUAbQAgACgh6gAgAEMAYQBwAHMAIABMAG8AYwBrAClfEB5UZXh0 +IEZpZWxkIENlbGwgKEFrdHVhbGl6YWNlOilvEB0ATABpAG4AawAgAFQAZQB4AHQAIABGAGkAZQBsAGQA +IAAoJ6QAIABwAVkAaQBzAHABGwBqAHQAZQApXxATVGFibGUgQ29sdW1uIChuYW1lKW8QHABUAGUAeAB0 +ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgBYABpAGYAcgBvAHYA4QBuAO0AOgApbxASAEIAdQB0AHQA +bwBuACAAQwBlAGwAbAAgACgAVQBrAGEBfgApbxAkAEMAaABlAGMAawAgAEIAbwB4ACAAKABQAG8AdQB6 +AGUAIABwAVkAaQAgAHMAdABpAHMAawB1ACAAawBsAOEAdgBlAHMAeQApbxAnAFQAZQB4AHQAIABGAGkA +ZQBsAGQAIABDAGUAbABsACAAKABQAG8BfgBhAGQAYQB2AGUAawAgAGsAIABvAHYAbADhAGQA4QBuAO0A +OgApbxAVAEIAZQB2AGUAbAAgAEIAdQB0AHQAbwBuACAAKABDAG8AIABqAGUgJgApbxAqAEMAaABlAGMA +awAgAEIAbwB4ACAAKABQAG8AawB1AHMAaQB0ACAAcwBlACAAcAByAG8AYgB1AGQAaQB0ACAAcwBwAO0A +YwDtACAATQBhAGMAeQApbxAUAEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgAQwBvACAAagBlICYAKV8Q +F1RleHQgRmllbGQgQ2VsbCAoU3RhdjopbxAhAEMAaABlAGMAawAgAEIAbwB4ACAAKABaAG8AYgByAGEA +egBpAHQAIABzAHQAYQB2ACAAdgAgAGwAaQFhAHQBGwApbxAYAFMAdABhAHQAaQBjACAAVABlAHgAdAAg +ACgBYABpAGYAcgBvAHYA4QBuAO0AOgApbxAlAFQAZQB4AHQAIABGAGkAZQBsAGQAIABDAGUAbABsACAA +KABEAW8AdgEbAHIAeQBoAG8AZABuAOkAIABzAHQAcgBvAGoAZQA6AClfEBNTdGF0aWMgVGV4dCAoU3Rh +djopXxAZVGV4dCBGaWVsZCBDZWxsIChEbG91aG91KVxDb250ZW50IFZpZXdvEBsAVABhAGIAIABWAGkA +ZQB3ACAASQB0AGUAbQAgACgAWgBhAGIAZQB6AHAAZQENAGUAbgDtAClvECIAQwBoAGUAYwBrACAAQgBv +AHgAIAAoAFQA4QBoAG4AaQAgACYAIABQAHUAcwFlACAAbQBlAHoAaQAgAE0AYQBjAHkAKW8QIABQAG8A +cAAgAFUAcAAgAEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgh6gAgAEMAYQBwAHMAIABMAG8AYwBrAClv +ECwAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABQAG8AawB1AHMAaQB0ACAAcwBlACAAcAByAG8AYgB1 +AGQAaQB0ACAAcwBwAO0AYwDtACAATQBhAGMAeQApbxAUAFAAbwBwAHUAcAAgAEIAdQB0AHQAbwBuACAA +KCMlACAAQQBsAHQAKW8QGgBUAGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgAUAFZAGUAbgBv +AHMAeQA6AClbQ3VzdG9tIFZpZXdvEBgAVABhAGIAIABWAGkAZQB3ACAASQB0AGUAbQAgACgATQBvAX4A +bgBvAHMAdABpAClvEC0AQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABPAGQAbQDtAHQAbgBvAHUAdAAs +ACAAcABvAGsAdQBkACAAbgBlAG4A7QAgAHYAIABzAGUAegBuAGEAbQB1AClvEC8AVABvAHAAIABUAGEA +YgAgAFYAaQBlAHcAIAAoAFIAbwB6AGwAbwF+AGUAbgDtACwAIABaAGEAYgBlAHoAcABlAQ0AZQBuAO0A +LAAgAE0AbwF+AG4AbwBzAHQAaQApXxAZQnV0dG9uIENlbGwgKFMgcHJvZGxldm91KW8QHABDAGgAZQBj +AGsAIABCAG8AeAAgACgAUwBkAO0AbABlAHQAIAB0AGUAbgB0AG8AIABNAGEAYwApW1NsaWRlciBDZWxs +WFByZWZQYW5lbxAaAFAAbwBwACAAVQBwACAAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKCMlACAAQQBs +AHQAKVZNYXRyaXhvEDUAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABaAG8AYgByAGEAegBpAHQAIABi +AGUAegBlAGwAIABwAVkAaQAgAG8AdgBsAOEAZADhAG4A7QAgAGoAaQBuAOkAaABvACAATQBhAGMAYQAp +XxATSG9yaXpvbnRhbCBTY3JvbGxlcm8QGQBUAGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgA +SwByAOEAdABrAG8AdQApbxBaAFQAZQB4AHQAIABGAGkAZQBsAGQAIABDAGUAbABsACAAKABQAHIAbwAg +AG8AdgBsAOEAZADhAG4A7QAgAHIAbwB6AG0A7QBzAHQBGwB0AGUAIABzAGQA7QBsAGUAbgDpACAATQBh +AGMAeQAgAHoAIABoAG8AcgBuAO0AIABsAGkBYQB0AHkAIABvAGsAbwBsAG8AIABzAHYA6QBoAG8AIABt +AG8AbgBpAHQAbwByAHUALgApXxAXQ2hlY2sgQm94IChTIHByb2RsZXZvdSlvEBsAVABlAHgAdAAgAEYA +aQBlAGwAZAAgAEMAZQBsAGwAIAAoJ6QAIABkAGkAcwBrAHUAcwBlAClfEBFNZW51IChPdGhlclZpZXdz +KV8QGlN0YXRpYyBUZXh0IChBa3R1YWxpemFjZTopbxB5AFMAdABhAHQAaQBjACAAVABlAHgAdAAgACgA +qQAgADIAMAAwADMALQAyADAAMAA4ACAAYQBiAHkAcwBzAG8AZgB0AAoAUAFvAHYAbwBkAG4A7QAgAG0A +eQFhAGwAZQBuAGsAYQA6ACAARABhAHYAZQAgAFMAYQBnACAAYQAgAEoAZQByAGUAbQB5ACAAUQB1AGkA +bgBuAC4ACgBaAHYAbADhAWEAdABuAO0AIABwAG8AZAEbAGsAbwB2AOEAbgDtADoAIABNAGEAbgB1ADAA +MgAsACAASgB1AGwAaQBlAG4AIABhACAAUABhAHUAbAAuAClvEBsAQwBoAGUAYwBrACAAQgBvAHgAIAAo +AFMAZADtAGwAZQB0ACAAcwBjAGgAcgDhAG4AawB1AClbU2Nyb2xsIFZpZXdvEB4AQgB1AHQAdABvAG4A +IABDAGUAbABsACAAKABTAGQA7QBsAGUAdAAgAHQAZQBuAHQAbwAgAE0AYQBjAClfECBCdXR0b24gQ2Vs +bCAoQWt0aXZvdmF0IHRlbGVwb3J0KW8QFwBMAGkAbgBrACAAVABlAHgAdAAgAEYAaQBlAGwAZAAgACgn +pAAgAHcAZQBiAClvEBcATQBlAG4AdQAgAEkAdABlAG0AIAAoIwMAIABDAG8AbgB0AHIAbwBsACkALQAx +bxAUAE0AZQBuAHUAIABJAHQAZQBtACAAKCMYACAASgBhAGIAbABrAG8AKW8QQQBUAGUAeAB0ACAARgBp +AGUAbABkACAAQwBlAGwAbAAgACgATwB6AG4AYQENAGUAbgD9ACAAcABvAQ0A7QB0AGEBDQAgAG8AZABz +AHQAcgBhAG4A7QB0AGUAIABrAGwA4QB2AGUAcwBvAHUAIABCAGEAYwBrAHMAcABhAGMAZQAuAClvEBsA +TABpAG4AawAgAFQAZQB4AHQAIABGAGkAZQBsAGQAIAAoJ6QAIABkAGkAcwBrAHUAcwBlAClvECYAQgB1 +AHQAdABvAG4AIABDAGUAbABsACAAKABQAG8AdQB6AGUAIABwAVkAaQAgAHMAdABpAHMAawB1ACAAawBs +AOEAdgBlAHMAeQApW0FwcGxpY2F0aW9uXxASUHJlZlBhbmVDb250cm9sbGVyXxAdVGV4dCBGaWVsZCBD +ZWxsIChUZXh0IENlbGwpLTFbTGF5b3V0IFZpZXdvEBgAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABP +AHYBGwFZAGkAdAAgAHQAZQEPAClvECIAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABaAHYAbwBsAGkA +dAAgAGMAZQByAHQAaQBmAGkAawDhAHQALgAuAC4AKW8QMwBDAGgAZQBjAGsAIABCAG8AeAAgACgAWgBv +AGIAcgBhAHoAaQB0ACAAYgBlAHoAZQBsACAAcAFZAGkAIABvAHYAbADhAGQA4QBuAO0AIABqAGkAbgDp +AGgAbwAgAE0AYQBjAGEAKW8QFgBTAHQAYQB0AGkAYwAgAFQAZQB4AHQAIAAoAFABWQBlAG4AbwBzAHkA +OgApbxAhAEMAaABlAGMAawAgAEIAbwB4ACAAKABPAHYBGwFZAGkAdAAgAHABWQBpACAAcAFZAGkAaABs +AOEBYQBlAG4A7QApbxAbAEMAaABlAGMAawAgAEIAbwB4ACAAKABKAGUALQBsAGkAIABtAGUAbgFhAO0A +IABuAGUBfgApbxAdAEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgAUwBkAO0AbABlAHQAIABzAGMAaABy +AOEAbgBrAHUAKV8QFlRhYmxlIENvbHVtbiAoYWRkcmVzcylvEBMATQBlAG4AdQAgAEkAdABlAG0AIAAo +IecAIABTAGgAaQBmAHQAKW8QEwBNAGUAbgB1ACAASQB0AGUAbQAgACgjJQAgAEEAbAB0ACkALQAxVlZp +ZXctMm8QGABTAHQAYQB0AGkAYwAgAFQAZQB4AHQAIAAoAFABWQBlAHAA7QBuAOEAbgDtADoAKW8QIwBT +AHQAYQB0AGkAYwAgAFQAZQB4AHQAIAAoAFAAbwF+AGEAZABhAHYAZQBrACAAawAgAG8AdgBsAOEAZADh +AG4A7QA6AClvECMAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABaAG8AYgByAGEAegBpAHQAIABzAHQA +YQB2ACAAdgAgAGwAaQFhAHQBGwApbxAjAEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgATwB2ARsBWQBp +AHQAIABwAVkAaQAgAHABWQBpAGgAbADhAWEAZQBuAO0AKV8QEUltYWdlIFZpZXcgKGljb24pbxAyAEIA +dQB0AHQAbwBuACAAQwBlAGwAbAAgACgARABvAHQA4QB6AGEAdAAgAHMAZQAsACAAegBkAGEAIABqAGUA +IABzAHQAcgBvAGoAIABkAW8AdgEbAHIAeQBoAG8AZABuAP0AKW8QPQBTAHQAYQB0AGkAYwAgAFQAZQB4 +AHQAIAAoAE8AegBuAGEBDQBlAG4A/QAgAHAAbwENAO0AdABhAQ0AIABvAGQAcwB0AHIAYQBuAO0AdABl +ACAAawBsAOEAdgBlAHMAbwB1ACAAQgBhAGMAawBzAHAAYQBjAGUALgApVlZpZXctMV8QEUltYWdlIENl +bGwgKGljb24pbxAVAE0AZQBuAHUAIABJAHQAZQBtACAAKCHnACAAUwBoAGkAZgB0ACkALQAxbxAfAEMA +aABlAGMAawAgAEIAbwB4ACAAKABQAVkAaQAgAHMAdABpAHMAawB1ACAAawBsAOEAdgBlAHMAeQA6AClv +EBcAVABlAHgAdAAgAEYAaQBlAGwAZAAgAEMAZQBsAGwAIAAoJ6QAIAB3AGUAYgApWlRleHQgRmllbGRv +ECYAVABhAGIAbABlACAAVgBpAGUAdwAgACgATgDhAHoAZQB2ACwAIABBAGQAcgBlAHMAYQAsACAAQwBl +AHIAdABpAGYAaQBrAOEAdAApbxBWAFMAdABhAHQAaQBjACAAVABlAHgAdAAgACgAUAByAG8AIABvAHYA +bADhAGQA4QBuAO0AIAByAG8AegBtAO0AcwB0ARsAdABlACAAcwBkAO0AbABlAG4A6QAgAE0AYQBjAHkA +IAB6ACAAaABvAHIAbgDtACAAbABpAWEAdAB5ACAAbwBrAG8AbABvACAAcwB2AOkAaABvACAAbQBvAG4A +aQB0AG8AcgB1AC4AKW8QIgBQAHUAcwBoACAAQgB1AHQAdABvAG4AIAAoAFoAdgBvAGwAaQB0ACAAYwBl +AHIAdABpAGYAaQBrAOEAdAAuAC4ALgApbxAhAEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgAUAFZAGkA +IABzAHQAaQBzAGsAdQAgAGsAbADhAHYAZQBzAHkAOgApbxAVAE0AZQBuAHUAIABJAHQAZQBtACAAKCMD +ACAAQwBvAG4AdAByAG8AbAApXxARSG9yaXpvbnRhbCBTbGlkZXJfEB5WZXJzaW9uIFRleHQgRmllbGQg +KDx2ZXJzaW9uPilvEBkAVABhAGIAIABWAGkAZQB3ACAASQB0AGUAbQAgACgAUgBvAHoAbABvAX4AZQBu +AO0AKV8QG1RleHQgRmllbGQgQ2VsbCAoVGV4dCBDZWxsKW8QEQBNAGUAbgB1ACAASQB0AGUAbQAgACgj +JQAgAEEAbAB0AClfEB5DaGVjayBCb3ggKEFrdGl2b3ZhdCB0ZWxlcG9ydClvEH0AVABlAHgAdAAgAEYA +aQBlAGwAZAAgAEMAZQBsAGwAIAAoAKkAIAAyADAAMAAzAC0AMgAwADAAOAAgAGEAYgB5AHMAcwBvAGYA +dAAKAFABbwB2AG8AZABuAO0AIABtAHkBYQBsAGUAbgBrAGEAOgAgAEQAYQB2AGUAIABTAGEAZwAgAGEA +IABKAGUAcgBlAG0AeQAgAFEAdQBpAG4AbgAuAAoAWgB2AGwA4QFhAHQAbgDtACAAcABvAGQBGwBrAG8A +dgDhAG4A7QA6ACAATQBhAG4AdQAwADIALAAgAEoAdQBsAGkAZQBuACAAYQAgAFAAYQB1AGwALgAp0gAO +AD4LYA3wgQKkpQMAB84HzQfMAVKAkYEB4oEB3YEB1oAx0gAOAD4LYA34gQKkpQMLCDgIOAg4AXCAkoEB +14EB14EB14Ay0gAOAD4LYA4AgQKkrxDXAwgAVwExBaIAiAhlBiAATgBgAI8ATQCNA+kAngToCAYAVACF +B8kAUgBzAIQAXACAAH8ASAB8ArYEBQBWAJEATAbXBRQBugB3ANkCfwTkBy4AbQCDAH0AagNCB2cBPQTe +A8UE4gBlAFoBNQRCA8YDKQc+BVYAnAGoAF8F3AOsAJ0AjgbbB8sAiwBoAIEAlwbwAEsA4gu4CJEAXgCl +AHoC1gJtAHUAcgG7BvMAmgCuA+0DSQQyA+YA6ABbBaEAZgTnBvIFRQbxA6cDngMABDEHmQBVBSQAlACS +AN8DBAG8BZ4AdACQAJkBsALpBg8E5gTzBOMGLAGnAIwE7QRDAEoAhgFnBTQAYgVuArAHswBYAB8CMwfO +AHAEFgB2BKcAbwBuAoUAWQBQAGsAYQPqBLcAaQTrAGcAcQbNBe0FBAC4Bf4AmACWAJUDXwCTA+sATwcd +BOUIUATbAmYAYwCKAX0AiQMwATgAhwCCBaAHzAB+A/MCHAfNAHsC7wBJAHkE6QTaBO4E2QB4AJsFaQBd +BNwHdwBsB8gH1gTfAVIINABRAtAAUwTsAcsAZAgcAroD7oCXgKiAKoEBM4ECcIEB5IEBS4AwgQHzgQJ6 +gCmBAnaAy4ECn4EBdoEBzYCQgQJrgQHLgIKBAj+BAmiBAe2BAl2BAlyACYECVYB+gM2AooECfYAngQF6 +gQENgD+BAkmAGIBugQE9gQGNgQIxgQJlgQJYgQIsgKOBAZmAsIEBE4DDgQEbgQIYgQGtgCuA24D+gJ2B +AZGBAR2BApuA7YEB8oEBO4C4gQKdgQJ5gQF7gQHRgQJ0gQIggQJggQKPgQF/gBeAG4ECooEB9IEB8IAK +gQJRgIWAaoECQoECPYBUgQGFgQKXgAuA84ClgNaAx4EBAIEBtoEBMIECHIEBSYEBg4EBGYEBgYC2gL+A +kYDigQGugJyBARGBAoWBAn6AGYCUgFmBASqBAkCBAnuBApOA6YCKgQFHgQFFgQEFgQE5gQFMgHyBAnWB +AZeA3YAVgQJsgDaBARWBAg+BASCAe4EBt4CqgAKAYIEB4oECOIDRgQJGgPWBAjWBAjSAcIEBq4BtgQIt +gQINgM+A+YECI4EBi4ECH4ECPIEBeIEBP4EBCYCzgQFDgQKRgQKNgQKHgKuBAoKA04BngQGJgQFBgQHf +gQELgGiBAhGBAnOAO4ECcYCfgC2BAm2BAmOBAS2BAdaBAlqAyYBYgQHdgQJUgIyAE4ECT4EBh4EBB4EB +m4EBA4ECTIECmYEBH4EB7oEBD4EBnYECMIEBvIEBxoEBF4AxgQHZgHqAg4CJgQGPgEuBAhOBAdOAxID3 +0gAOAD4LYA7agQKkrxDXDtsO3A7dDt4O3w7gDuEO4g7jDuQO5Q7mDucO6A7pDuoO6w7sDu0O7g7vDvAO +8Q7yDvMO9A71DvYO9w74DvkO+g77DvwO/Q7+Dv8PAA8BDwIPAw8EDwUPBg8HDwgPCQ8KDwsPDA8NDw4P +Dw8QDxEPEg8TDxQPFQ8WDxcPGA8ZDxoPGw8cDx0PHg8fDyAPIQ8iDyMPJA8lDyYPJw8oDykPKg8rDywP +LQ8uDy8PMA8xDzIPMw80DzUPNg83DzgPOQ86DzsPPA89Dz4PPw9AD0EPQg9DD0QPRQ9GD0cPSA9JD0oP +Sw9MD00PTg9PD1APUQ9SD1MPVA9VD1YPVw9YD1kPWg9bD1wPXQ9eD18PYA9hD2IPYw9kD2UPZg9nD2gP +aQ9qD2sPbA9tD24Pbw9wD3EPcg9zD3QPdQ92D3cPeA95D3oPew98D30Pfg9/D4APgQ+CD4MPhA+FD4YP +hw+ID4kPig+LD4wPjQ+OD48PkA+RD5IPkw+UD5UPlg+XD5gPmQ+aD5sPnA+dD54Pnw+gD6EPog+jD6QP +pQ+mD6cPqA+pD6oPqw+sD60Prg+vD7APsYEDLIEDLYEDLoEDL4EDMIEDMYEDMoEDM4EDNIEDNYEDNoED +N4EDOIEDOYEDOoEDO4EDPIEDPYEDPoEDP4EDQIEDQYEDQoEDQ4EDRIEDRYEDRoEDR4EDSIEDSYEDSoED +S4EDTIEDTYEDToEDT4EDUIEDUYEDUoEDU4EDVIEDVYEDVoEDV4EDWIEDWYEDWoEDW4EDXIEDXYEDXoED +X4EDYIEDYYEDYoEDY4EDZIEDZYEDZoEDZ4EDaIEDaYEDaoEDa4EDbIEDbYEDboEDb4EDcIEDcYEDcoED +c4EDdIEDdYEDdoEDd4EDeIEDeYEDeoEDe4EDfIEDfYEDfoEDf4EDgIEDgYEDgoEDg4EDhIEDhYEDhoED +h4EDiIEDiYEDioEDi4EDjIEDjYEDjoEDj4EDkIEDkYEDkoEDk4EDlIEDlYEDloEDl4EDmIEDmYEDmoED +m4EDnIEDnYEDnoEDn4EDoIEDoYEDooEDo4EDpIEDpYEDpoEDp4EDqIEDqYEDqoEDq4EDrIEDrYEDroED +r4EDsIEDsYEDsoEDs4EDtIEDtYEDtoEDt4EDuIEDuYEDuoEDu4EDvIEDvYEDvoEDv4EDwIEDwYEDwoED +w4EDxIEDxYEDxoEDx4EDyIEDyYEDyoEDy4EDzIEDzYEDzoEDz4ED0IED0YED0oED04ED1IED1YED1oED +14ED2IED2YED2oED24ED3IED3YED3oED34ED4IED4YED4oED44ED5IED5YED5oED54ED6IED6YED6oED +64ED7IED7YED7oED74ED8IED8YED8oED84ED9IED9YED9oED94ED+IED+YED+oED+4ED/IED/YED/oED +/4EEAIEEAYEEAhIAAYdGEK4RAZ8SAASVBRIAAYfFEgABh+YRAQMRASoRAb0QrxEBQBEBfRIABJUjEQGF +EgABh0UQtxEBsBClEgAElPwRAUsRAagQqxEBlREBVxC4EgAElRESAAGIIRIAAYgdEQEKEQGTEOkRAYoS +AAGIDxD4EQG+EMIQrBEBuxIAAYgQEQFJEQGnEQGCEgAElRIRAQgSAAGIYREBkhEBZREBnBEBpREBlxDJ +EQG2ELwQiBIAAYhfEgABiDwSAASVIRIAAw42EgABiGASAAGHOBIABJUiEQF/EQGGEKgRAV0RAaMRAZER +AcMRAYgQzhIAAYdiE//////////9EQEpEMoQiREBRBIABJT4EgABh18RAVsRAVkQ+REBixIABJUfEL0R +AbgSAAGHqBEBtREBuRC+EKoRAaIRAYARAUYRAYcSAAGIOxEBiRCYELsQphIAAYhSEQGWEJwSAAGIDhEB +QREBxRIABJUcEKIRAWgRAaERAVoRAUISAASVHhIAAYeWEQFQEgAElQ8SAASVDhIAAYflEQHAEQFOEgAE +lRsRAVwRAcERAbcQ6hEBWBIABJTWEgABiDIQkREBnREBgRChEBoSAASVHREBaREBJREBuhIABJUKEQFj +EgABiFgRAccRAWASAAGHTBIABJT6EK0RAUMRARASAASVCRIAAYgKEQGkEQFwEQFkEQFKEgABiCUSAAGI +WxIAAYgLELoSAAGHrxEBxBEBVhIABJUMEAwRAakRAbIQzRIABJUIEQEPEgABh8QRAW8QvxIABJT5EQHG +EPYSAASVBhIAAYcoEgABh04RAcIRAaYRAaARASMRAYMSAAGIWRIAAYeZEQFeEgABh/AQ6BIABJUHEQFr +EQFhEQFFEQGPEgAElSARAZ4RAQQRAW4SAAGIAREBrxCkEgABh0QRAZsQ9xIAAYfDEgAElPsSAASU9xEB +UREBvxIAAYeYEgAElQ0SAAGHSBEBZhEBatIADgA+AEYQhoBioNIADgA+C2AQiYECpKDSAA4APgtgEIyB +AqSg0gA3ADgQjhCPohCPADteTlNJQk9iamVjdERhdGEACAAZACIAJwAxADoAPwBEAFIAVABmCHgIfgjJ +CNAI1wjlCPcJEwkhCS0JOQlHCVIJYAl8CYoJnQmvCckJ0wngCeIJ5QnoCesJ7gnwCfMJ9Qn4CfsJ/goB +CgMKBQoICgsKEAoTChwKKAoqCiwKPwpIClEKXAphCnAKeQqMCpUKoAqiCqMKrAqzCsAKxgrPCtELgguE +C4YLiAuKC4wLjguQC5ILlAuWC5gLmgucC54LoAuiC6QLpwuqC60LsAuzC7YLuQu8C78LwgvFC8gLywvO +C9EL1AvXC9oL3QvgC+ML5gvpC+wL7wvyC/UL+Av7C/4MAQwEDAcMCgwNDBAMEwwWDBkMHAwfDCIMJQwo +DCsMLgwxDDQMNww6DD0MQAxDDEYMSQxMDE8MUgxVDFgMWwxeDGEMZAxnDGoMbQxwDHMMdgyHDJUMngym +DKgMqgysDK4MzwzhDOkM8gz7DQcNEw0VDRcNGQ0cDR4NIA0iDSQNQQ1MDU4NUA1SDVQNVw1ZDVsNdA2B +DYgNkQ2aDacNsw28DcUNzA3jDfIOAw4FDgcOCQ4LDhkOKg4sDi4OMA4yDj8OUA5SDlQOVg5YDnUOfA6G +DogOig6MDo4OkQ6SDpQOsQ69DsAOwg7FDsgOyw7NDtAO6g8fDysPQQ9WD2UPeA+KD5UPnw+tD78PzA/a +D98P4Q/jD+UP5w/pD+sP7Q/vD/EP8w/1D/oQLRA+EEUQTBBVEFcQYBBiEGUQchB7EIAQhxCQEJwQnhCg +EKkQshC3EM0QzhDXEOAQ7RD6EQMRDhEXESERNhFHEUkRSxFNEU8RYRFyEXQRdhF4EXoRmxGdEZ8RoRGj +EaQRphGoEckRyxHNEc8R0hHUEdYR2BHxEiYSKBIqEiwSLhIwEjISNBJJEl4SbxJxEnMSdRJ3Es4S8BL6 +ExATHRMxE0YTUxNtE4kTpBOwE88T3hPqE+wT7hPzE/UT9xP5E/oT/BQFFAcUEBQSFBMUFRQXFBkUGxQd +FCYUMhQ+FGcUcRR7FIUUkxSVFJcUmRSbFJ0UoBSiFKQUphSoFLMUzBTOFNAU0hTUFNYU/xUBFQMVBRUH +FQkVCxUNFQ8VGRUiFSsVPxVUFVYVWBVaFVwVlRWhFaoVvRXKFdYV5BXmFegV6hXsFe4V8BXyFgUWBxYJ +FgsWDRYkFi0WNhZEFk0WTxZWFlgWWhZcFoUWlBahFq4WthbBFtAW2xbmFucW6RbrFu0W9hb4FwEXChcM +FxEXLhc6Fz8XQRdDF0UXRxdJF1QXZRdnF3AXchd1F4IXjxeXF5kXmxenF7AXtRe9F9IX3hfsF+4X8Bfy +F/QX9hf9GA8YHBgeGCEYKhg1GEkYWxh8GIEYgxiFGIcYiRiLGJAYkhicGLEYsxi1GLcYuRjSGN8Y4Rjt +GQIZBBkGGQgZChkdGSYZLxk4GT0ZSxl0GXUZdxl5GXsZhBmGGY8ZkRmZGbYZuBm6GbwZvhnAGccZ6Bnq +GewZ7hnwGfIZ9BoVGhcaGRobGiQaJhovGjEaPRpaGlwaXhpgGmIaZBp5Go4akBqSGpQalhqiGq8asRq0 +GuUa5xrpGusa7RrvGvEa8xr1Gvga/RsGGw8bGBsfGy4bNhtLG00bTxtRG1MbXRtqG2wbcRt6G38bjhuf +G6EboxulG6cbxBvGG8gbyhvMG80bzxvpHB4cJxwpHCscLRwvHDEcMxw1HFYccByBHIMchRyHHIkcqhys +HK4csByyHLUcthy4HLoc0x0IHRYdGB0aHRwdHh0gHSIdJB0mHSsdMB09HUodWR1bHV0dXx1nHXIdex2A +HZMdnB2mHagdsR24Hcod0x3lHe4d9R4NHh4eIB4iHiQeJh5DHkUeRx5JHkseTB5OHmsebR5vHnEecx51 +HncekR7CHsQexh7IHsoezB7OHtcfAB8JHwsfJB81HzcfOR87Hz0fWh9cH14fYB9iH2sfbB9uH4gfuR+7 +H70fvx/BH8MfxR/wH/kf+yAVICYgKCAqICwgLiBLIE0gTyBRIFMgVCBWIG4gnyChIKMgpSCnIKkgqyDA +IMkgyyDZIOog7CDuIPAg8iEbIR0hHyEhISMhJSEmISghKiEsIUEhTSFqIWwhbiFxIXQhdiF5IZIhsyG1 +IbchuSG7Ib0hwiHEIc4h4yHlIech6SHrIfgiCyIcIh4iICIiIiQiRSJHIkkiSyJNIk4iUCJSImwioSKm +IqgiqiKsIq4isCKyIrQi1yLuIv8jASMDIwUjByMkIyYjKCMqIywjLSMvI0gjfSN/I4EjgyOFI4cjiSOL +I7gjxyPYI9oj3CPeI+Aj6yP8I/4kACQCJAUkNiRDJFAkXiRoJHYkgySNJJ8ksyS9JMkkyyTOJNEk0yTY +JNok3CTfJOIk5CT/JRclICUiJSclRCVGJUglSiVNJU8lUiVbJV0lYCViJWslbSV2JXgleiV8JX4lsyXC +JdYl7yYHJgkmDCYOJhAmEiYUJhYmFyYZJhomHCYlJicmKiYsJjUmNyY8Jj4mQCZhJmMmZSZnJmkmbCZt +Jm8mcSaIJqkmqyatJq8msSazJrgmuidNJ1YnYSd6J4MniiejJ6wnrie1J7cnuSe7J9Qn3ifgJ+In5Cfm +J+gn6ifxKAQoDSgSKCAoNSg3KDkoOyg9KD8oVChWKFgoWihcKGUoZyhqKGwodSh3KIoojCiOKJAokiiU +KJYomCiaKJwouSi7KL0ovyjBKMQoxSjHKOEpAikEKQYpCCkKKQwpESkTKXQpkSmTKZUplymZKZopnCm2 +Kesp7SnvKfEp8yn1Kfcp+SoeKjsqPSo/KkEqQypEKkYqXyqAKoIqhCqGKogqiiqPKpEqpirxKv0rBysW +KyIrOitFK08rZCtyK3orfCt+K4ArgiuEK4YriCuKK4wrjiuPK5ErkyuYK5orsyu8K74rxSvHK8kryywA +LAIsBCwGLAgsCiwMLA4sWSxiLGQsZix0LH0sfyy4LL4swCzCLMQsxizILMoszC0NLUYtSC1KLUwtTi1Q +LVItVC1tLXctfi2nLaktqy2tLa8ttS2+Lckt0i3bLd0t6C3qLewt7i3wLfIt+y39LgAuAi4aLiMuLC43 +LlguYS5qLnQudi54LnoufC5+LoAuiS6lLrIuuy7GLtEu9i74Lvou/C7+LwAvAi8LLycvMC8yLzUvNy9N +L2gvcS96L4cvpC+mL6gvqi+sL60vry/HL+gv6i/sL+4v8C/yL/QwITA+MEAwQjBEMEYwRzBJMGEwgjCE +MIYwiDCKMIwwjjC3MMIw2TDyMPQw9jD5MPsw/TEAMQgxHTEfMSExJDEnMTAxMjE1MTcxQDFCMXExdDF3 +MXoxfTF/MYIxhTGHMYkxjDGPMZIxlTGYMZsxnjGhMaMxpjGpMawxrzHMMc4x0DHTMdYx1zHZMfMyKDIq +MiwyLjIwMjMyNTI4MlkydjJ4MnoyfTKAMoEygzKcMr0yvzLBMsQyxjLJMssy3DL5Mvsy/TMAMwMzBDMG +Mx4zPzNBM0MzRjNIM0szTTNaM3czeTN7M34zgTOCM4QznTO+M8AzwjPFM8czyjPMM+Ez/jQANAI0BTQI +NAk0CzQlNFo0XDReNGA0YjRlNGc0ajSZNLY0uDS6NL00wDTBNMM03TUSNRQ1FjUYNRo1HTUfNSI1SzVo +NWo1bTVwNXM1dDV2NZA13zX8Ng42GTYrNkA2TjZVNmc2cDZxNnM2djZ5Nns2fjaBNoI2gzaGNok2jjaX +Npk2yjbSNuY28Tb/Nwk3FjceNyE3JDcnNyw3LjczNzY3OTc8Nz83QTdON1o3XTdgN2M3bjd7N303fzeC +N5Q3oTejN6U3qDe7N8832DfdN+Y36DfzN/w3/jgJOAw4DzgSOBU4GDhFOEg4SzhOOFA4UzhWOFk4XDhe +OG84nDifOKI4pTinOKo4rTiwOLM4tTjIOPU4+Dj7OP45ADkDOQY5CTkMOQ45HTlKOU05UDlTOVU5WDlb +OV45YTljOXo5gzmIOZE5njmyOcE5yjnXOeU6AjoEOgY6CToMOg06DzooOkk6SzpNOlA6UjpVOlo6XDpr +Oog6ijqMOo86kjqTOpU6rzrkOuY66DrqOuw67zrxOvQ7MztQO1I7VDtXO1o7WztdO3c7rDuuO7A7sju0 +O7c7uTu8O8g75TvnO+k77DvvO/A78jwMPEE8QzxFPEc8STxMPE48UTxuPIs8jTyPPJI8lTyWPJg8sTza +POY8/D0FPQc9Cj0MPQ89Ej0XPRg9Gz0ePWU9bD13PX49ij2SPaU9rD26Pc494D30Pgc+Ez4aPic+OT48 +Pj8+Qj5FPkc+Sj5NPlA+Uz5WPlc+Wj5cPl8+Yj5jPmQ+cT55Pnw+lT6YPps+nj6hPqQ+pz6qPq0+sD6z +PrY+uT7SPtU+2D7bPt4+4T7kPuc+6j7tPvA+8z72Pv4/DT8nP0k/XD9wP3c/hD+MP6Q/sz/HP+Q/7z/7 +QApAFEAgQCxAL0AwQENAREBNQFJAb0ByQH9AjECPQJJAlUCXQKRAp0CoQKlAskC3QMRAzUDSQOdA9ED3 +QPhA+UD8QP9BCEEWQRlBG0EkQSlBMkFPQVJBU0FmQWdBaUFyQXlBj0GYQZtBnkGrQa5BsUG0QbhBwUHI +QdxB5UH6QfxB/kIBQgNCGUIuQjBCMkI1QjdCQUJeQmBCY0JmQmlCakJsQoZC1ULWQthC20LeQuBC40Lm +QudC6ELrQvRC9kMnQypDLUMwQzJDNUM4QztDPkNLQ05DUUNUQ11DX0NoQ2pDdUN4Q3tDfkOBQ4RDsUO0 +Q7dDukO8Q79DwkPFQ8hD9UP4Q/tD/kQARANEBkQJRAxEOUQ8RD9EQkREREdESkRNRFBEfUSARINEhkSI +RItEjkSRRJREsUSzRLVEuES7RLxEvkTXRQxFDkUQRRJFFEUXRRlFHEVtRYpFjEWORZFFlEWVRZdFr0XQ +RdJF1EXXRdlF3EXeReRGAUYDRgZGCUYMRg1GD0YoRmFGaUZ/RpRGn0aqRrVGw0bgRulG7EbvRvJG9Ub+ +RwdHDEcNRxZHF0cgRyJHM0c1Rz5HQUdLR1RHXUdqR3NHfkeHR6RHpkeoR6tHrkevR7FHykfrR+1H70fy +R/RH90f5SAFIHkggSCJIJUgoSClIK0hESHlIe0h9SH9IgUiESIZIiUi2SMdI0EjZSORI70kISRZJM0k8 +SUFJVElcSW1Jb0lxSXNJdkmQSaFJo0mlSahJq0m8Sc5J4knxSfRJ90n6Sf1KBkoISgtKDkolSipKLUo2 +SjtKREpLSmBKbUp1SoZKiEqLSo1KkErBSsNKxkrJSstK0ErTStZK2UrcSt9K+ksMSxVLF0sgSyJLMUs0 +SzdLOUs8Sz9LQktFS25LeUuFS4dLikuNS45LkUuUS5VLl0uZS6JLpEuzS7ZLuUu8S79LwkvFS8hL5EwA +TCZMPkxyTJNMsEzKTOtM80z7TQNNDk0TTRZNGU0eTR9NLE0uTTBNM004TUFNSE1UTV1NaE10TZVNl02Z +TZxNn02gTaJNpE29Td5N4E3iTeVN6E3rTe1N9k4HTglOEk4VTilOSk5MTk5OUU5UTlVOV05ZTnFOkk6U +TpZOmU6cTp9OpE6mT39PkE+ST5tPnU+gT8lPy0/NT9BP0k/VT9hP2U/bT95P4E/yUAhQMVA2UDhQOlA9 +UEBQQ1BEUEdQSVBUUGVQZ1BpUIhQsVCzULVQuFC6UL1QvlDAUMNQxVDcUQFRA1EFUQhRC1EOURFRE1Em +UVpRg1GFUYdRilGMUY9RkFGSUZVRl1GvUdRR1lHYUdtR3lHhUeRR5lH9Uq1SuFLDUs9S4FLiUuRS51Lp +UvpS/FL+UwBTA1MbUyxTLlMwUzJTNVM6U0tTTVNPU1FTU1NkU2ZTaFNrU25Tf1OCU4VThlOJU5JTlFPD +U8ZTyVPMU89T0lPVU9hT21PeU+FT5FPnU+pT7VPwU/NT9lP5U/xT/1QCVAVUGlQyVEVUW1RyVI5UqVSw +VMlU7lUHVRxVLlVLVWRVgVWTVadVxFXeVfdWE1YYVhtWLFYuVjFWM1Y2Vk5WX1ZhVmNWZVZoVnNWhFaG +VohWilaNVqVWwlbMVtZW9Vb4VvtW/lcBVwRXB1cjVytXPldHV05XZleDV4ZXiVeMV49XkleVV8NXyVfw +WA1YEFgTWBZYGVgbWB5YSFhrWIhYi1iOWJFYlFiXWJpYt1i6WL1YwFjDWMZYyVj0WRZZO1lRWVtZXllh +WWRZZ1lqWW1ZcFlzWZBZmVmmWalZslm1WbhZu1m+WcdZylnNWdBZ01nnWgRaH1o9WkZaY1pmWmlabFpv +WnJadVqSWpVamFqbWp5aoVqkWsta61sIWwtbDlsRWxRbFlsZWzZbOVs8Wz9bQltFW0hbdVubW7hbu1u+ +W8FbxFvGW8lb5lvpW+xb71vyW/Vb+FwjXEdcZFxnXGpcbVxwXHJcdVypXLVc3Fz5XPxc/10CXQVdCF0L +XTBdM102XTldPF0/XUJdRV1IXVVdWF1hXWRdZ11qXW1ddl15XXxdf12CXZ9dol2lXahdq12uXbFd1l3Z +Xdxd313iXeVd6F3rXe5d+13+XgdeCl4NXhBeE14cXh9eIl4lXiheTV5QXlNeVl5ZXlxeX15iXmVelV6e +Xqterl63XrpevV7AXsNezF7PXtJe1V7YXvVe+F77Xv5fAV8EXwdfMV9UX3FfdF93X3pffV+AX4Nfpl/C +X99f4l/lX+hf61/uX/FgF2A0YFlgXGBfYGJgZWBoYGtgbmBxYH5ggWCKYI1gkGCTYJZgn2CiYKVgqGCr +YMhgy2DOYNFg1GDWYNlhAWEiYT9hQmFFYUhhS2FNYVBhbWFwYXNhdmF5YXxhf2GsYdJh72HyYfVh+GH7 +Yf1iAGIsYlFiVGJXYlpiXWJgYmJiZWJoYnVieGKBYoRih2KKYo1ilmKZYpxin2KiYr9iwmLFYshiy2LN +YtBi7WLwYvNi9mL5Yvxi/2MiYz5jW2NeY2FjZGNnY2pjbWOZY7hj1WPYY9tj3mPhY+Rj52QXZDxkP2RC +ZEVkSGRLZE5kUWRUZIBkjWSQZJlknGSfZKJkpWSuZLFktGS3ZLpk32TiZOVk6GTrZO5k8WT0ZPdlFGUh +ZSRlLWUwZTNlNmU5ZUJlRWVIZUtlTmVrZW5lcWV0ZXdleWV8ZZllnGWfZaJlpWWnZaplx2XKZc1l0GXT +ZdZl2WX+ZhxmOWY8Zj9mQmZFZkdmSmZvZnJmdWZ4ZntmfmaAZoNmhmaTZpZmn2aiZqVmqGarZrRmt2a6 +Zr1mwGbdZuBm42bmZulm62buZwtnDmcRZxRnF2caZx1nOmc9Z0BnQ2dGZ0hnS2doZ2tnbmdxZ3Rndmd5 +Z59nvmfbZ95n4WfkZ+dn6WfsaAloDGgPaBJoFWgYaBtoOGg7aD5oQWhEaEZoSWhtaIpojWiQaJNolmiZ +aJxowWjEaMdoymjNaNBo02jWaNlpCmkxaT5pQWlKaU1pUGlTaVZpX2liaWVpaGlraYhpi2mOaZFplGmX +aZppymntagpqDWoQahNqFmoYahtqRGplamhqa2puanFqdGp3anpqpGrHatRq12raat1q4GrjavxrDmsr +ay5rMWs0azdrOWs8a1Zrc2t2a3lrfGt/a4JrhWuua9Nr1mvZa9xr32via+Vr6Gvra/hr+2wEbAdsCmwN +bBBsGWwcbB9sImwlbDZsP2xGbElsS2xObFFsZGzZbOJs52z7bQxtD20RbRRtF204bUltTG1ObVFtVG2X +bahtq22ubbFttG6Hbphum26ebqFupG/xcAJwBXAHcApwDXDucPdw+nH7cf1x/3IBcgRyB3IKcg1yD3IS +chRyF3IZchtyHXIgciNyJnIocityLnIwcjNyNnI4cjtyPnJAckNyRnJIckpyTHJPclJyVHJXcllyW3Je +cmByYnJkcmdyaXJscm5ycHJycnVyeHJ6cn1yf3KCcoVyiHKLco1yj3KRcpNylnKYcptynXKgcqJypXKn +cqpyrHKvcrJytXK4crtyvXK/csFyw3LGcslyzHLOctBy0nLVcthy23Ldct9y4XLkcuZy6XLscu5y8XL0 +cvdy+nL9cv9zAXMDcwVzCHMKcw1zEHMTcxZzGHMbcx5zIXMjcyZzKHMrcy1zL3MxczRzNnM4cztzRHNG +c0lzV3Ngc2VzbnNxdHJ0dHR2dHh0e3R+dIB0g3SGdIl0i3SOdJF0k3SVdJh0mnSddJ90oXSkdKZ0qHSq +dKx0r3SxdLN0tnS5dLt0vXS/dMF0xHTGdMl0y3TNdM900XTTdNV013TZdNx03nTgdOJ05HTndOl063Tt +dPB083T1dPh0+nT8dP51AHUDdQV1CHUKdQ11D3USdRR1F3UZdRt1HnUhdSN1JXUndSl1K3UtdTB1MnU1 +dTd1OXU7dT11P3VBdUN1RXVHdUl1S3VNdU91UXVUdVZ1WXVbdV51YHVidWR1ZnVodWp1bHVvdXJ1dHV2 +dXh1e3V+dYB1g3WFdYd1iXWLdY11kHWSdZR1l3WgdaN2pnaodqp2rXavdrJ2tXa4drp2vHa/dsF2xHbG +dsh2y3bOdtF203bWdtl223beduF243bmdul263budvF283b1dvh2+nb8dv93AncFdwd3CXcLdw13D3cR +dxR3FncZdxt3HXcfdyJ3JXcndyl3LHcvdzJ3NXc4dzp3PHc+d0B3Q3dFd0h3SndNd093UndUd1d3WXdc +d193Yndld2h3andsd253cHdzd3Z3eXd7d353gHeDd4Z3iHeKd4x3jneRd5R3l3ead5x3nnehd6R3pnep +d6t3rXewd7N3tXe3d7p3vXfAd8N3xnfJd8t3zXfPd9J31XfYd9p33Hfed+F343fld+h38Xf0ePd4+nj9 +eQB5A3kGeQl5DHkPeRJ5FXkYeRt5HnkheSR5J3kqeS15MHkzeTZ5OXk8eT95QnlFeUh5S3lOeVF5VHlX +eVp5XXlgeWN5ZnlpeWx5b3lyeXV5eHl7eX55gXmEeYd5inmNeZB5k3mWeZl5nHmfeaJ5pXmoeat5rnmx +ebR5t3m6eb15wHnDecZ5yXnMec950nnVedh523neeeF55Hnneep57XnwefN59nn5efx5/3oCegV6CHoL +eg56EXoUehd6GnodeiB6I3omeil6LHovejJ6NXo4ejt6PnpBekR6R3pKek16UHpTelZ6WXpcel96Ynpl +emh6a3puenF6dHp3epV6v3rTexh7U3uIe8V71Hvie/R8EXxAfFR8h3zCfPl9Nn1zfaB9s33Bffh+EH43 +flR+bX6Bfsx+4n8nf2x/cn+zf8B/8YASgE+AZYCggMeBEoFjgZCB54ISgiyCcYKkgvGDB4MjgzCDaYOw +g/OEToR5hLCEvITvhUyFrYXJhgSGEIYZhlCGV4bEhtqHD4fGh+CIGYgtiEqJP4l4iYSJw4nmiheKSIpz +iviLMYuAi4yLoYvBi82MAIxHjLCM340kjV2Nmo2zjdyOBY4Mjj+OiI7RjxqPLo+VkBKQGZAtkFqQm5DM +kNeRJpHVkhySYZKOkqKSw5L4kxaTO5NclFmUYpRllHCUcpR1lHiUe5R9lIaUiZSUlJaUmZSclJ+UoZSq +lK2WXpZglmKWZJZnlmqWbZZwlnKWdZZ4lnqWfZZ/loKWhZaIloqWjZaQlpKWlZaYlpuWnpahlqOWppao +lqqWrJavlrGWtJa3lrmWvJa+lsCWw5bGlsmWzJbPltKW1JbXltmW3JbeluGW5JbnlumW65btlu+W8pb1 +lviW+pb9lwCXApcFlwiXC5cOlxGXFJcXlxqXHZcflyGXJJcnlyqXLJcvlzGXM5c2lzmXO5c+l0GXQ5dF +l0eXSZdLl06XUZdUl1eXWpddl2CXY5dll2eXaZdrl26XcJdzl3aXeZd7l32Xf5eCl4WXiJeLl42Xj5eS +l5WXmJebl56XoJejl6aXqJeql62Xr5eyl7WXuJe6l72Xv5fBl8OXxpfJl8uXzpfQl9OX1pfYl9uX3Zfg +l+OX5Zfnl+qX7Zfwl/OX9pf5l/yX/pgBmASYB5gKmAyYD5gRmBOYFpgZmByYH5ghmCSYJ5gpmCyYLpgw +mDOYNpg5mDyYP5hBmEOYRphJmEuYTZhQmFOYVphZmFyYX5himGWYaJhrmG6YcZh0mHeYeph8mH+YgZiD +mIWYiJiKmI2YkJiSmJSYnZigmlGaVJpXmlqaXZpgmmOaZpppmmyab5pymnWaeJp7mn6agZqEmoeaipqN +mpCak5qWmpmanJqfmqKapZqomquarpqxmrSat5q6mr2awJrDmsaayZrMms+a0prVmtia25remuGa5Jrn +muqa7ZrwmvOa9pr5mvya/5sCmwWbCJsLmw6bEZsUmxebGpsdmyCbI5smmymbLJsvmzKbNZs4mzubPptB +m0SbR5tKm02bUJtTm1abWZtcm1+bYptlm2iba5tum3GbdJt3m3qbfZuAm4ObhpuJm4ybj5uSm5WbmJub +m56boZukm6ebqputm7Cbs5u2m7mbvJu/m8KbxZvIm8ubzpvRm9Sb15vam92b4Jvjm+ab6Zvsm++b8pv1 +m/ib+5v+nAGcBJwHnAqcDZwQnBOcFpwZnBycH5winCWcKJwrnC6cMZw0nDecOpw9nECcQ5xGnEmcTJxP +nFKcVZxYnFucXpxhnGScZ5xqnG2ccJxznHaceZx8nH+cgpyFnIici5yOnJGclJyXnJqcnZygnKOcppyp +nKycr5yynLWcuJy7nL6cwZzEnMecypzNnNCc05zWnNuc3ZzgnOWc6pzvnPKc9Zz4nPqc/Z0AnQWdCJ0N +nQ+dEp0UnRmdHJ0fnSGdJJ0nnSmdLp0znTidO50+nUCdQ51InUqdTZ1PnVGdVJ1ZnVydX51inWedap1v +nXKddZ14nXudfp2AnYOdhZ2HnYydkZ2WnZudoJ2lnaqdrZ2wnbKdtZ24nbudvp3BncOdyJ3RndSd1p3Y +ndud4J3lneid653tnfCd9Z33nfqd/54CngWeB54JngyeD54SnhWeGp4dnh+eIZ4jniieK54tnjKeNZ44 +nj2eP55CnkWeSJ5LnlCeVZ5Ynl2eYp5nnmqebZ5ynnWeeJ57nn2egJ6FnoqejJ6PnpKelJ6Wnpuenp6h +nqSeqZ6snrGetJ63nryewZ7DnsaeyZ7OntOe1p7Zntye357knume7p7wnvWe+J77nwCfAp8FnwifCp8P +nxKfF58anxyfIZ8knyafK58wnzWfOJ87nz6fQZ9En0mfTp9Rn1afWJ9dn2CfY59mn2mfbp9xn3Sfd598 +n3+fgZ+Gn4mfi5+Qn5Wfmp+dn6CfpZ+qn6+fsp+1n76fwJ/Bn8qfzZ/On9ef2p/bn+Sf6QAAAAAAAAIC +AAAAAAAAEJAAAAAAAAAAAAAAAAAAAJ/4A + + + diff --git a/Danish.lproj/InfoPlist.strings b/Danish.lproj/InfoPlist.strings new file mode 100644 index 0000000..aef8634 Binary files /dev/null and b/Danish.lproj/InfoPlist.strings differ diff --git a/Danish.lproj/Localizable.strings b/Danish.lproj/Localizable.strings new file mode 100644 index 0000000..807dcbe Binary files /dev/null and b/Danish.lproj/Localizable.strings differ diff --git a/Danish.lproj/MainMenuApp.nib/classes.nib b/Danish.lproj/MainMenuApp.nib/classes.nib new file mode 100644 index 0000000..8eab51a --- /dev/null +++ b/Danish.lproj/MainMenuApp.nib/classes.nib @@ -0,0 +1,38 @@ + + + + + IBClasses + + + CLASS + TPPrefPaneAppController + LANGUAGE + ObjC + OUTLETS + + window + NSWindow + + SUPERCLASS + NSObject + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Danish.lproj/MainMenuApp.nib/info.nib b/Danish.lproj/MainMenuApp.nib/info.nib new file mode 100644 index 0000000..21ddea8 --- /dev/null +++ b/Danish.lproj/MainMenuApp.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 629 + IBLastKnownRelativeProjectPath + ../teleport.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 265 + + IBSystem Version + 9A569 + targetFramework + IBCocoaFramework + + diff --git a/Danish.lproj/MainMenuApp.nib/keyedobjects.nib b/Danish.lproj/MainMenuApp.nib/keyedobjects.nib new file mode 100644 index 0000000..0d5ee09 Binary files /dev/null and b/Danish.lproj/MainMenuApp.nib/keyedobjects.nib differ diff --git a/Danish.lproj/teleportPref.xib b/Danish.lproj/teleportPref.xib new file mode 100644 index 0000000..64e98f2 --- /dev/null +++ b/Danish.lproj/teleportPref.xib @@ -0,0 +1,5882 @@ + + + + 1050 + 9C31 + 629 + 949.26 + 352.00 + + YES + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + TPPreferencePane + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{517, 450}, {595, 429}} + 1081606144 + PDwgZG8gbm90IGxvY2FsaXplID4+A + NSWindow + + View + + + + 256 + + YES + + + 274 + + YES + + + 268 + {{156, 387}, {115, 18}} + + + YES + + 604110336 + 0 + Del denne Mac + + LucidaGrande + 1.300000e+01 + 1044 + + + 1211912703 + 2 + + NSSwitch + + + + 200 + 25 + + + + + 265 + {{545, 379}, {32, 32}} + + + YES + + 67239424 + 134217728 + QWJvdXTigKY + + + 138690815 + 130 + + NSImage + icon-about + + + + + + 200 + 25 + + + + + 268 + {{18, 387}, {126, 18}} + + + YES + + 67239424 + 0 + Aktiver teleport + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 274 + + YES + + + 256 + + YES + + + 274 + {{20, 17}, {511, 296}} + + + TPLayoutView + NSView + + + + 290 + {{17, -13}, {434, 28}} + + + YES + + 67239424 + 4325376 + QXJyYW5nZXIgZGUgZGVsdGUgTWFjcyBmcmEgdG9wcGVuLCBvbWtyaW5nIGRpbiBza8Omcm0gZm9yIGF0 +IGtvbnRyb2xsZXJlIGRlbS4 + + + 1.100000e+01 + 3100 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2OQA + + + + 6 + + controlTextColor + + 3 + MAA + + + + + + {{10, 33}, {549, 319}} + + + + + {{1, 10}, {569, 365}} + + + + YES + + layout + + Layout + + + + + + + 256 + + YES + + + 301 + + YES + + + 292 + {{182, 101}, {306, 14}} + + YES + + 67239424 + 272760832 + Rm9yIGF0IHNsZXR0ZSBlbiB2w6ZydCwgbWFya2VyIG9nIHRyeWsgYmFja3NwYWNlA + + + + + + + + + -2147483380 + {{179, 250}, {167, 32}} + + YES + + 67239424 + 134217728 + VsOmbGcgY2VydGlmaWthdC4uLg + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{179, 250}, {140, 32}} + + YES + + 67239424 + 134217728 + Vis certifikat + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{183, 286}, {135, 18}} + + YES + + 67239424 + 0 + U2zDpSBrcnlwdGVyaW5nIHRpbA + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{23, 287}, {148, 17}} + + YES + + 67239424 + 71303168 + Kryptering + + + + + + + + + 292 + {{185, 23}, {299, 58}} + + YES + 3 + 1 + + YES + + -2080244224 + 0 + U3DDuHJnIG1pZyBvbSBlbiB2w6ZydCBlciBiZXRyb2V0A + + + 1211912703 + 0 + + NSRadioButton + + + + + + 200 + 25 + + + 67239424 + 0 + Afvis hvis ikke den allerede er betroet + + 1 + + 1211912703 + 0 + + + + 200 + 25 + + + 67239424 + 0 + Accepter automatisk + + 2 + + 1211912703 + 0 + + + + 400 + 75 + + + {299, 18} + {4, 2} + 1143480320 + NSActionCell + + 67239424 + 0 + Radio + + 1211912703 + 0 + + 400 + 75 + + + + + 3 + MQA + + + + + + 274 + + YES + + + 2304 + + YES + + + 256 + {333, 104} + + YES + + + 256 + {333, 17} + + + + + + 256 + {{-26, 0}, {16, 17}} + + + + YES + + name + 1.490000e+02 + 4.000000e+01 + 1.000000e+03 + + 75628032 + 0 + Navn + + + 3 + MC4zMzMzMzI5OQA + + + 6 + + headerTextColor + + + + + 337772096 + 133120 + Text Cell + + + + 6 + + controlBackgroundColor + + + + + 1 + YES + + + + address + 1.160000e+02 + 8.000000e+00 + 1.000000e+03 + + 75628032 + 0 + Addresse + + + + + + 337772096 + 133120 + + + + + + + 1 + YES + + + + certificate + 5.900000e+01 + 5.857568e+01 + 1.000000e+03 + + 67239424 + 0 + Certifikat + + + 6 + + headerColor + + + + + + 67239424 + 134348800 + View + + + -2034876161 + 36 + + + 400 + 75 + + + + + 3.000000e+00 + 2.000000e+00 + + + 6 + + gridColor + + 3 + MC41AA + + + 1.700000e+01 + 448823296 + 1 + 15 + 0 + YES + + + {{1, 17}, {333, 104}} + + + + + 4 + + + + 256 + {{-100, -100}, {15, 180}} + + + _doScroller: + 9.222222e-01 + + + + 256 + {{-100, -100}, {463, 15}} + + 1 + + + 9.904762e-01 + + + + 2304 + + YES + + + {{1, 0}, {333, 17}} + + + + + 4 + + + + {{185, 116}, {335, 122}} + + + 2 + + + + + + QSAAAEEgAABBmAAAQZgAAA + + + + 292 + {{14, 47}, {157, 34}} + + YES + + 67239424 + 71303168 + Rm9yZXNww7hyZ3NsZXIgb20ga29udHJvbGxlcmluZw + + + + + + + + + 268 + {{8, 221}, {163, 17}} + + YES + + 67239424 + 71303168 + QmV0cm9lZGUgdsOmcnRlcg + + + + + + + + {549, 319} + + + + + {{10, 33}, {549, 319}} + + Sikkerhedsindstillinger + + + + + options + + + 256 + + YES + + + 301 + + YES + + + 268 + {{211, 132}, {153, 18}} + + YES + + 67239424 + 0 + Kun hvis mindre end + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{90, 164}, {81, 34}} + + YES + + 67239424 + 71303168 + T3ZlcmbDuHJzbGVyOg + + + + + + + + + 268 + {{17, 1}, {154, 34}} + + YES + + 67239424 + 71303168 + Version kontrollering: + + + + + + + + + 268 + {{102, 287}, {69, 17}} + + YES + + 67239424 + 71303168 + Skift: + + + + + + + + + 268 + {{183, 74}, {178, 18}} + + YES + + 67239424 + 0 + Vis status i menu baren + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 180}, {224, 18}} + + YES + + -2080244224 + 0 + VHLDpmsgb2cgc2xpcCBmaWxlciBtZWxsZW0gTWFjcw + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{211, 108}, {173, 18}} + + YES + + 67239424 + 0 + Kun hvis tast er trykket: + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 156}, {187, 18}} + + YES + + -2080244224 + 0 + Synkroniser udklipsholder + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 286}, {232, 18}} + + YES + + 67239424 + 0 + Skift kun hvis tast er trykket: + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{387, 103}, {129, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + 4oyYIENvbW1hbmQ + + 1048576 + 2147483647 + 1 + + + NSMenuCheckmark + + + + NSMenuMixedState + + _popUpItemAction: + 20 + + + YES + + + OtherViews + + + YES + + + + 4oylIE9wdGlvbg + + 1048576 + 2147483647 + + + _popUpItemAction: + 19 + + + + + 4oyDIENvbnRyb2w + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + 4oenIFNoaWZ0A + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + 4oeqIENhcHMgTG9jaw + + 1048576 + 2147483647 + + + _popUpItemAction: + 16 + + + + + 3 + YES + YES + 1 + + + + + 268 + {{348, 261}, {33, 17}} + + YES + + 67239424 + 272629760 + Kort + + + + + + + + + 268 + {{183, 214}, {203, 18}} + + YES + + 67239424 + 0 + Rm9yc8O4ZyBhdCB2w6ZrIHNvdmVuZGUgTWFjcw + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 262}, {159, 18}} + + YES + + 67239424 + 0 + Skift med forsinkelse: + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 238}, {204, 18}} + + YES + + 67239424 + 0 + Skift ved dubbeltklik + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{370, 130}, {58, 22}} + + YES + + -1804468671 + 71304192 + + + + YES + + YES + allowsFloats + attributedStringForZero + decimalSeparator + formatterBehavior + groupingSeparator + locale + maximum + minimum + negativeFormat + positiveFormat + textAttributesForNegativeValues + usesGroupingSeparator + + + YES + + + 0 + + YES + + YES + + + YES + + + + wqA + + , + + + + + 0 + 2 + NO + YES + 2 + AABAAAAAAAAAAAAAAAAAAA + + + 0 + 0 + NO + NO + 2 + AAAAAAAAAAAAAAAAAAAAAA + + + 0K + + YES + + YES + + + YES + + + + + + + + + + + + + + + NaN + + + + + + + + NO + NO + YES + + size + + YES + + 6 + + textBackgroundColor + + + + 6 + + textColor + + + + + + + 268 + {{387, 281}, {129, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + 20 + + + YES + + + OtherViews + + + YES + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 19 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 16 + + + + + 3 + YES + YES + 1 + + + + + 268 + {{183, 50}, {294, 18}} + + YES + + -2080244224 + 0 + VmlzIG1lZGRlbGVzZSBuw6VyIGFuZHJlIE1hY3Mga29udHJvbGxlcmVzA + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{384, 7}, {111, 32}} + + YES + + 67239424 + 134217728 + Check Now + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{102, 75}, {69, 17}} + + YES + + 67239424 + 71303168 + Status: + + + + + + + + + 268 + {{386, 262}, {83, 15}} + + YES + + 67239424 + 131072 + + + + + Helvetica + 1.200000e+01 + 16 + + + 1.000000e+00 + 1.000000e-01 + 5.000000e-01 + 0.000000e+00 + 0 + 1 + NO + NO + + + + + 268 + {{474, 261}, {31, 17}} + + YES + + 67239424 + 272629760 + Lang + + + + + + + + + 268 + {{183, 16}, {271, 18}} + + YES + + 67239424 + 0 + Kontroller version ved login + + + 1211912703 + 2 + + + + 200 + 25 + + + + {549, 319} + + + + + {{10, 33}, {549, 319}} + + Indstillinger + + + + + + + 0 + YES + YES + + + {595, 438} + + + + + + {595, 429} + + + + {{0, 0}, {1680, 1028}} + {224.664, 32} + {3.40282e+38, 3.40282e+38} + + + 1 + 2 + {{194, 375}, {295, 307}} + 1886912512 + + TPClosingWindow + + View + + + + 256 + + YES + + + 256 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + CorePasteboardFlavorType 0x504E4766 + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{20, 159}, {255, 128}} + + + YES + + 537001472 + 33554432 + + + icon + + 0 + 0 + 0 + NO + + YES + + + + 256 + {{17, 129}, {261, 22}} + + + YES + + 67239424 + 138412032 + teleport + + LucidaGrande-Bold + 1.800000e+01 + 16 + + + + + + + + + 256 + {{17, 113}, {261, 14}} + + + YES + + 67239424 + 138412032 + PHZlcnNpb24+A + + + + + + + + + 256 + {{12, 43}, {274, 39}} + + + YES + + 67239424 + 4194304 + wqkgMjAwMy0yMDA4IGFieXNzb2Z0CkJhc2VyZXQgcMOlIGVuIGlkZSBhZiBEYXZlIFNhZyBvZyBKZXJl +bXkgUXVpbm4uClRhayB0aWwgTWFudTAyLCBKdWxpZW4gb2cgUGF1bC4 + + + 1.000000e+01 + 2843 + + + + + + + + + 258 + {{12, 8}, {59, 17}} + + + YES + + 69336577 + 272629760 + 4p6kIHdlYnNpdGU + + + 1.100000e+01 + 16 + + http://teleport.abyssoft.com + + YES + + + + + + + 258 + {{119, 8}, {56, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIGZvcnVtcw + + http://www.abyssoft.com/software/teleport/forums/ + + + + + + + + 258 + {{228, 8}, {55, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIGRvbmVyA + + aHR0cHM6Ly93d3cucGF5cGFsLmNvbS9jZ2ktYmluL3dlYnNjcj9jbWQ9X3hjbGljayZidXNpbmVzcz1q +dWwlNDBtYWMlMmVjb20maXRlbV9uYW1lPXRlbGVwb3J0Jm5vX3NoaXBwaW5nPTAmbm9fbm90ZT0wJnRh +eD0wJmN1cnJlbmN5X2NvZGU9VVNEJmNoYXJzZXQ9VVRGJTJkOCZjaGFyc2V0PVVURiUyZDg + + + + + + + {295, 307} + + + + {{0, 0}, {1680, 1028}} + {213, 129} + {3.40282e+38, 3.40282e+38} + + + + YES + prefs.allowControl + prefs.sharePasteboard + prefs.requireKey + prefs.delayedSwitch + prefs.showStatusItem + prefs.limitPasteboardSize + prefs.maxPasterboardSize + active + prefs.autocheckVersion + prefs.rejectAuthenticationRequests + prefs.enableEncryption + prefs.switchKeyTag + prefs.copyFiles + prefs.requirePasteboardKey + prefs.pasteboardKeyTag + prefs.trustRequestBehavior + prefs.wakeOnLAN + prefs.switchDelay + localHost.supportDragNDrop + prefs.maxPasteboardSize + prefs.hideControlBezel + prefs.switchWithDoubleTap + + YES + + + + + YES + numberOfSelectedRows + + NSTableView + + + + + + YES + + + _window + + + + 26 + + + + layoutView + + + + 145 + + + + allowControlCheckbox + + + + 156 + + + + aboutWindow + + + + 170 + + + + delegate + + + + 171 + + + + showAboutSheet: + + + + 173 + + + + activationCheckbox + + + + 175 + + + + versionTextField + + + + 183 + + + + + + + + 184 + + + + + + + + 200 + + + + view + + + + 202 + + + + sharePasteboardCheckbox + + + + 205 + + + + requireKeyCheckbox + + + + 206 + + + + _firstKeyView + + + + 232 + + + + _initialKeyView + + + + 233 + + + + _lastKeyView + + + + 234 + + + + dataSource + + + + 258 + + + + + + + + 259 + + + + trustedHostsTableView + + + + 260 + + + + statusCheckbox + + + + 266 + + + + delayedSwitchCheckbox + + + + 272 + + + + content + + + + 298 + + + + value: selection.prefs.allowControl + + + + + + + value + selection.prefs.allowControl + 2 + + + 320 + + + + value: selection.prefs.sharePasteboard + + + + + + + + selection.prefs.sharePasteboard + 2 + + + 321 + + + + value: selection.prefs.requireKey + + + + + + + + selection.prefs.requireKey + 2 + + + 322 + + + + value: selection.prefs.delayedSwitch + + + + + + + + selection.prefs.delayedSwitch + 2 + + + 323 + + + + value: selection.prefs.showStatusItem + + + + + + + + selection.prefs.showStatusItem + 2 + + + 324 + + + + value: selection.prefs.limitPasteboardSize + + + + + + + + selection.prefs.limitPasteboardSize + 2 + + + 329 + + + + enabled: selection.prefs.sharePasteboard + + + + + + + enabled + + 2 + + + 330 + + + + + + + + + + + + + 2 + + + 331 + + + + checkVersion: + + + + 337 + + + + value: selection.active + + + + + + + + selection.active + 2 + + + 342 + + + + enabled: selection.active + + + + + + + + + 2 + + + 343 + + + + + + + + + + + + + 2 + + + 344 + + + + enabled2: selection.active + + + + + + + enabled2 + + + YES + + YES + NSMultipleValuesPlaceholder + NSNoSelectionPlaceholder + NSNotApplicablePlaceholder + NSNullPlaceholder + + + YES + + + + + + + + 2 + + + 345 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 346 + + + + enabled3: selection.prefs.limitPasteboardSize + + + + + + + enabled3 + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 347 + + + + + + + + + + + + + 2 + + + 348 + + + + + + + + + + + + + 2 + + + 349 + + + + + + + + + + + + + 2 + + + 350 + + + + + + + + + + + + + 2 + + + 352 + + + + value: selection.prefs.autocheckVersion + + + + + + + + selection.prefs.autocheckVersion + 2 + + + 355 + + + + + + + + + + + + + 2 + + + 356 + + + + + + + + + + + + + 2 + + + 383 + + + + value: selection.prefs.enableEncryption + + + + + + + + selection.prefs.enableEncryption + 2 + + + 384 + + + + enabled: selection.prefs.enableEncryption + + + + + + + + + 2 + + + 386 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 387 + + + + enabled: selection.prefs.requireKey + + + + + + + + + 2 + + + 399 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 400 + + + + selectedTag: selection.prefs.switchKeyTag + + + + + + + selectedTag + selection.prefs.switchKeyTag + 2 + + + 401 + + + + + + + + + + + + + 2 + + + 403 + + + + value: selection.prefs.copyFiles + + + + + + + + selection.prefs.copyFiles + 2 + + + 405 + + + + + + + + 407 + + + + + + + + + + + + + 2 + + + 419 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 420 + + + + value: selection.prefs.requirePasteboardKey + + + + + + + + selection.prefs.requirePasteboardKey + 2 + + + 421 + + + + enabled: selection.prefs.requirePasteboardKey + + + + + + + + + 2 + + + 422 + + + + enabled2: selection.prefs.sharePasteboard + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 423 + + + + enabled3: selection.active + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 424 + + + + selectedTag: selection.prefs.pasteboardKeyTag + + + + + + + + selection.prefs.pasteboardKeyTag + 2 + + + 425 + + + + + + + + + + + + + 2 + + + 431 + + + + + + + + + + + + + 2 + + + 432 + + + + selectedTag: selection.prefs.trustRequestBehavior + + + + + + + + selection.prefs.trustRequestBehavior + 2 + + + 442 + + + + + + + + + + + + + 2 + + + 445 + + + + value: selection.prefs.wakeOnLAN + + + + + + + + selection.prefs.wakeOnLAN + 2 + + + 446 + + + + value: selection.prefs.switchDelay + + + + + + + + selection.prefs.switchDelay + 2 + + + 450 + + + + enabled: selection.prefs.delayedSwitch + + + + + + + + + 2 + + + 451 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 452 + + + + enabled2: selection.localHost.supportDragNDrop + + + + + + + + selection.localHost.supportDragNDrop + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 453 + + + + + + + + + + + + + 2 + + + 454 + + + + value: selection.prefs.maxPasteboardSize + + + + + + + + selection.prefs.maxPasteboardSize + 2 + + + 455 + + + + showCertificateButton + + + + 300281 + + + + chooseCertificateButton + + + + 300282 + + + + showCertificateViewer: + + + + 300283 + + + + showCertificateChooser: + + + + 300284 + + + + + + + + + + + + + 2 + + + 300293 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 300294 + + + + value: selection.prefs.hideControlBezel + + + + + + + + selection.prefs.hideControlBezel + + NSValueTransformerName + NSNegateBoolean + + 2 + + + 300300 + + + + + + + + + + + + + 2 + + + 300301 + + + + value: selection.prefs.switchWithDoubleTap + + + + + + + + selection.prefs.switchWithDoubleTap + 2 + + + 300305 + + + + + + + + + + + + + 2 + + + 300306 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + -3 + + + Application + + + 12 + + + YES + + + + PrefPane + + + 6 + + + YES + + + + + + 201 + + + YES + + + + + + + + + 136 + + + YES + + + + + + 172 + + + YES + + + + + + 174 + + + YES + + + + + + 186 + + + YES + + + + + + + + 187 + + + YES + + + + + + 189 + + + YES + + + + + + + 137 + + + + + 152 + + + YES + + + + + + 188 + + + YES + + + + + + 190 + + + YES + + + + + + 357 + + + YES + + + + + + 358 + + + YES + + + + + + 161 + + + YES + + + + About + + + 162 + + + YES + + + + + + + + + + + + 164 + + + YES + + + + + + 165 + + + YES + + + + + + 166 + + + YES + + + + + + 168 + + + YES + + + + + + 291 + + + YES + + + + + + 292 + + + YES + + + + + + 293 + + + YES + + + + + + 297 + + + PrefPaneController + + + 406 + + + TrustedHostsTable + + + 100136 + + + + + 100172 + + + + + 100174 + + + + + 100152 + + + + + 100164 + + + + + 100165 + + + + + 100166 + + + + + 100168 + + + + + 100291 + + + + + 100292 + + + + + 100293 + + + + + 300315 + + + YES + + + + + + + + + + + + + + 441 + + + YES + + + + + + 100441 + + + + + 300279 + + + YES + + + + + + 300280 + + + + + 385 + + + YES + + + + + + 100385 + + + + + 381 + + + YES + + + + + + 100381 + + + + + 300297 + + + YES + + + + + + 300298 + + + + + 434 + + + YES + + + + + + + + + 100434 + + + + + 439 + + + + + 438 + + + + + 437 + + + + + 246 + + + YES + + + + + + + + + 300246 + + + + + 200246 + + + + + 100246 + + + + + 247 + + + YES + + + + + + + + 360 + + + YES + + + + + + 249 + + + YES + + + + + + 248 + + + YES + + + + + + 100248 + + + + + 100249 + + + + + 361 + + + + + 440 + + + YES + + + + + + 100440 + + + + + 362 + + + YES + + + + + + 100362 + + + + + 300316 + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + 325 + + + YES + + + + + + 100325 + + + + + 363 + + + YES + + + + + + 100363 + + + + + 367 + + + YES + + + + + + 100367 + + + + + 366 + + + YES + + + + + + 100366 + + + + + 264 + + + YES + + + + + + 100264 + + + + + 402 + + + YES + + + + + + 100402 + + + + + 411 + + + YES + + + + + + 100411 + + + + + 191 + + + YES + + + + + + 100191 + + + + + 194 + + + YES + + + + + + 100194 + + + + + 412 + + + YES + + + + + + 100412 + + + YES + + + + + + 413 + + + YES + + + + + + + + + + 414 + + + + + 415 + + + + + 416 + + + + + 417 + + + + + 418 + + + + + 448 + + + YES + + + + + + 100448 + + + + + 443 + + + YES + + + + + + 100443 + + + + + 271 + + + YES + + + + + + 100271 + + + + + 300302 + + + YES + + + + + + 300303 + + + + + 326 + + + YES + + + + + + 100326 + + + YES + + + + + + 334 + + + + + 389 + + + YES + + + + + + 100389 + + + YES + + + + + + 390 + + + YES + + + + + + + + + + 391 + + + + + 392 + + + + + 393 + + + + + 394 + + + + + 395 + + + + + 300295 + + + YES + + + + + + 300296 + + + + + 336 + + + YES + + + + + + 100336 + + + + + 368 + + + YES + + + + + + 100368 + + + + + 447 + + + YES + + + + + + 100447 + + + + + 449 + + + YES + + + + + + 100449 + + + + + 353 + + + YES + + + + + + 100353 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + -3.ImportedFromIB2 + 100246.IBShouldRemoveOnLegacySave + 100248.IBShouldRemoveOnLegacySave + 100249.IBShouldRemoveOnLegacySave + 12.IBPluginDependency + 12.IBWindowTemplateEditedContentRect + 12.ImportedFromIB2 + 12.editorWindowContentRectSynchronizationRect + 12.windowTemplate.hasMaxSize + 12.windowTemplate.hasMinSize + 12.windowTemplate.maxSize + 12.windowTemplate.minSize + 136.IBAttributePlaceholdersKey + 136.IBPluginDependency + 136.ImportedFromIB2 + 137.IBAttributePlaceholdersKey + 137.IBPluginDependency + 137.ImportedFromIB2 + 152.IBPluginDependency + 152.ImportedFromIB2 + 161.IBPluginDependency + 161.IBWindowTemplateEditedContentRect + 161.ImportedFromIB2 + 161.editorWindowContentRectSynchronizationRect + 161.windowTemplate.hasMaxSize + 161.windowTemplate.hasMinSize + 161.windowTemplate.maxSize + 161.windowTemplate.minSize + 162.IBPluginDependency + 162.ImportedFromIB2 + 164.IBPluginDependency + 164.ImportedFromIB2 + 165.IBPluginDependency + 165.ImportedFromIB2 + 166.CustomClassName + 166.IBPluginDependency + 166.ImportedFromIB2 + 168.IBPluginDependency + 168.ImportedFromIB2 + 172.IBAttributePlaceholdersKey + 172.IBPluginDependency + 172.ImportedFromIB2 + 174.IBAttributePlaceholdersKey + 174.IBPluginDependency + 174.ImportedFromIB2 + 186.IBPluginDependency + 186.ImportedFromIB2 + 187.IBPluginDependency + 187.ImportedFromIB2 + 188.IBPluginDependency + 188.ImportedFromIB2 + 189.IBPluginDependency + 189.ImportedFromIB2 + 190.IBPluginDependency + 190.ImportedFromIB2 + 191.IBPluginDependency + 191.ImportedFromIB2 + 194.IBPluginDependency + 194.ImportedFromIB2 + 200246.IBShouldRemoveOnLegacySave + 201.IBPluginDependency + 201.ImportedFromIB2 + 246.IBPluginDependency + 246.ImportedFromIB2 + 247.CustomClassName + 247.IBPluginDependency + 247.ImportedFromIB2 + 248.IBPluginDependency + 248.ImportedFromIB2 + 249.IBPluginDependency + 249.ImportedFromIB2 + 264.IBPluginDependency + 264.ImportedFromIB2 + 271.IBPluginDependency + 271.ImportedFromIB2 + 291.CustomClassName + 291.IBPluginDependency + 291.ImportedFromIB2 + 292.CustomClassName + 292.IBPluginDependency + 292.ImportedFromIB2 + 293.CustomClassName + 293.IBPluginDependency + 293.ImportedFromIB2 + 297.IBPluginDependency + 297.ImportedFromIB2 + 300246.IBShouldRemoveOnLegacySave + 300279.IBPluginDependency + 300279.ImportedFromIB2 + 300295.IBPluginDependency + 300295.ImportedFromIB2 + 300297.IBPluginDependency + 300297.ImportedFromIB2 + 300302.IBAttributePlaceholdersKey + 300302.IBPluginDependency + 300302.ImportedFromIB2 + 325.IBPluginDependency + 325.ImportedFromIB2 + 326.IBPluginDependency + 326.ImportedFromIB2 + 334.IBPluginDependency + 334.ImportedFromIB2 + 336.IBPluginDependency + 336.ImportedFromIB2 + 353.IBPluginDependency + 353.ImportedFromIB2 + 357.IBPluginDependency + 357.ImportedFromIB2 + 358.IBPluginDependency + 358.ImportedFromIB2 + 360.IBPluginDependency + 360.ImportedFromIB2 + 361.IBPluginDependency + 361.ImportedFromIB2 + 362.IBPluginDependency + 362.ImportedFromIB2 + 363.IBPluginDependency + 363.ImportedFromIB2 + 366.IBPluginDependency + 366.ImportedFromIB2 + 367.IBPluginDependency + 367.ImportedFromIB2 + 368.IBPluginDependency + 368.ImportedFromIB2 + 381.IBPluginDependency + 381.ImportedFromIB2 + 385.IBPluginDependency + 385.ImportedFromIB2 + 389.IBPluginDependency + 389.ImportedFromIB2 + 390.IBPluginDependency + 390.ImportedFromIB2 + 391.IBPluginDependency + 391.ImportedFromIB2 + 392.IBPluginDependency + 392.ImportedFromIB2 + 393.IBPluginDependency + 393.ImportedFromIB2 + 394.IBPluginDependency + 394.ImportedFromIB2 + 395.IBPluginDependency + 395.ImportedFromIB2 + 402.IBPluginDependency + 402.ImportedFromIB2 + 406.IBPluginDependency + 406.ImportedFromIB2 + 411.IBPluginDependency + 411.ImportedFromIB2 + 412.IBPluginDependency + 412.ImportedFromIB2 + 413.IBPluginDependency + 413.ImportedFromIB2 + 414.IBPluginDependency + 414.ImportedFromIB2 + 415.IBPluginDependency + 415.ImportedFromIB2 + 416.IBPluginDependency + 416.ImportedFromIB2 + 417.IBPluginDependency + 417.ImportedFromIB2 + 418.IBPluginDependency + 418.ImportedFromIB2 + 434.IBPluginDependency + 434.ImportedFromIB2 + 437.IBPluginDependency + 437.ImportedFromIB2 + 438.IBPluginDependency + 438.ImportedFromIB2 + 439.IBPluginDependency + 439.ImportedFromIB2 + 440.IBPluginDependency + 440.ImportedFromIB2 + 441.IBPluginDependency + 441.ImportedFromIB2 + 443.IBAttributePlaceholdersKey + 443.IBPluginDependency + 443.ImportedFromIB2 + 447.IBPluginDependency + 447.ImportedFromIB2 + 448.IBPluginDependency + 448.ImportedFromIB2 + 449.IBPluginDependency + 449.ImportedFromIB2 + 6.IBPluginDependency + 6.ImportedFromIB2 + + + YES + + + + + + + + + {{205, 417}, {595, 429}} + + + + + {3.40282e+38, 3.40282e+38} + {224.664, 10} + + ToolTip + + + + Check to allow other teleport users to control your Mac. + + + + + + + + + + This is the layout area. Drag the controlled hosts wherever you want. + + + + + + + + {{194, 375}, {295, 307}} + + + + + + {213, 107} + + + + + + + TPVersionTextField + + + + + + + + + + QWJvdXQgdGVsZXBvcnTigKY + + + + + + + + + + Check to activate teleport. + + + + + + + + + + + + + + + + + + + + + + + + TPTableView + + + + + + + + + + + TPLinkTextField + + + + + + + + + + + + + + + + + + + + + + + Double-tapping is when you tap your mouse on the border, go back and tap again. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + T25seSBvbiBob3N0cyBjb25uZWN0ZWQgd2l0aCBFdGhlcm5ldCBhbmQgd2l0aCB0aGUgb3B0aW9uICJX +YWtlIGZvciBFdGhlcm5ldCBuZXR3b3JrIGFkbWluaXN0cmF0b3IgYWNjZXNzIiBjaGVja2VkIGluIHRo +ZSBFbmVyZ3kgU2F2ZXIgcHJlZmVyZW5jZXMgcGFuZWwuA + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 300316 + + + + YES + + + NSPreferencePane + + YES + + YES + addOther: + + closeAddOther: + confirmAddOther: + deleteTrustedHost: + + + + + + YES + id + + + + + + + + + + + YES + + YES + + + + + + + + + + + + + + + YES + + + + + + + + + + + + + + + + IBProjectSource + TPPreferencePane.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBUserSource + + + + + + + + : + + + + YES + + YES + + + YES + + + + + + + + + + NSTextField + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPVersionTextField.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPLinkTextField.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPClosingWindow.h + + + + + + + YES + + YES + + + YES + + + + + + + + + + + + + + NSObject + + YES + + YES + + changeTabPanel: + + + closeTrustedCertificate: + + + + showCertificate: + showTrustedHosts: + + + YES + + + + + + + + + + + + + + YES + + YES + + + + + + + addOtherAddressField + addOtherHeightField + addOtherNameField + addOtherSheet + addOtherWidthField + + certificateView + certificateWindow + + + + + + + + + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPHotBorder.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPUDPSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTableView.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPConnectionsManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTransfersManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSecureSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPDaemonManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPPreferencesManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection_Private.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + scroll: + setScaleFactor: + + + YES + + + + + + + + + + + TPLayoutView.h + + + + + 0 + ../../teleport.xcodeproj + 3 + + YnBsaXN0MDDUAAEAAgADAAQABQAGAAkAClgkdmVyc2lvblQkdG9wWSRhcmNoaXZlclgkb2JqZWN0cxIA +AYag0QAHAAhdSUIub2JqZWN0ZGF0YYABXxAPTlNLZXllZEFyY2hpdmVyrxEEBgALAAwAMQA1ADYAPAA9 +AEEARQCfAKcAtQC/AMAA2ADZAOEA4gDlAOYA6QDtAPEA9wD4APwBAQELARQACwEVASABIQEmAScBKAEr +AS0BLgExATYBTQFOAU8BUgALAVoBZAFwAXoBewF8AX0BfgF/AYABgQGCAY0BkQGSAZUBmAGgAaEBqwGs +AbEBsgG8Ab0BvgHDAcUBygHLAc4B0QHUAd8B0wHgAeEB6AHpAe4B8QH5AfoCAgIDAggCEwIUAhUCIgIj +AiYCJwIxAjICOgI7AjwCRgJHAk8CUAJRAlUACwJWAlgCWQJaAlsCXgJfAmQCawJyAnMCfgJ/AoMChAKH +AogCjQKOApMCmwKcAqUCpgKnAqwCrQKyAr4CvwECAsACwgLJAs0C1ALcAt0C5QLmAvYC+gMBAwYDDwMQ +APUDEQMUAx0DHgMmAycDKAMpAy8DOAM5AzoDPQNDA0gDTANYA2ADYQNpA2oDcgNzA3oDewN9A4QDhQON +A44DlQOWA54DnwO5A7oDwAPIA8kDzAPNA88D2APZA+MD5APlAPAD5gPrA+wD7wPyBAcEDwQdBCEEPQQ+ +BD8EQARGBE8EUARTBFgEWQRcBGIEcwR0BHsEfAR/BIQEhQSIBJEEkgSXBJgEmwSkBKUEqwSsBLMEuwS8 +BMIEwwTIBMkBUQTTBNgE2QTcBN0E4ATrBOwE7QTwBPgE+QT9BP4E/wUCBQkFCgURBRIFGQUaBSEFIgMn +BSMFJAUrBSwFMQU1BU4FVQVWBV4FXwVmBWcFbgVvBXYFdwV+BX8FhgWHBY4FjwWWBZcFnwWgBacFqAWw +BbEFuAW5BcEFwgXJBcoF0gXTBdoF2wXwBfIGBQYKBgsGDwYQBhQGFQYWBhgGGwYjBi0GFQYuBjgGFQY5 +BkMGFQZEBk0GFQZOBlAGVAZXBl4GXwZmBmcGbgZvBncGeAZ/BoAGiAaJBpAGkQaZBpoGoQaiBq4G0Abt +Bu4G7wbwBvEG8gbzBvQG9Qb2BvcG+Ab5BwQHBwcIBw0HDgcSBxUHGAccBx0HHgciANAHJQGJByoHKwcu +AOUHMQc1BzYHOQc6Bz8HQAdFB0YHTQdOB1kHWwdkBhUHaAdqB3IGFQd7BhUHhAYVB40GFQeWB50Hngem +B6cHrgevB7YHtwe5B8AHwQfIB8kH0AfRB+UH5wfrB+wH7wfyB/kH+ggBCAIICQgKCBIFIgMnCBMIFAgW +CBcACwJYCBgCWggZCBoIHwggCCUIJggrCCwIMQgyCDcIOAg9CD4IQwhECEkISghPCFQIXAhgBD4IYQhj +CGUIaQhqCG8IcAh1CHoIfwiYCJkImgibCJwInQieCJ8IoAihCKIIowikCKUIpginCKgIqQiqCKsIrAit +CK4IsAi1CLYIuwi8CMEIwgjHCMgIzQjOCNMI2AjZCOMI5AjlCOYI6QjwCPEI8gjzCPoJAQkICQkJCgkR +CRIJEwkaCRsJHAkjCSQJJQksCS0JNAk1CTYJPQk+CT8JRglNCU4JTwlaCVsJXAloCWkJaglrCWwJbQl0 +CXUJdgl/CYsJlAmVCZYJlwmjCaoJqwmyCbMJtAm1CbwJvQm+CcUJxgnNCc4JzwnWCdcJ4AnsCfMJ+goB +CgIKCQoKChMKHwomCi0KNgpCCkkKUgpTClQKYApnCm4KdQp2CncKfgp/CogKiQqVCp4KnwqrCrMKtAq1 +CrsKvAq9CsQKzQrZCuAK4QriCukK8Ar5CwULDAsTCxQLFQscCyMLKgsxCzILMws6CzsLPAs/C0QLRQtK +C0sLUAtRC1YLVwtcC10L3wviC+ML5QxnDOoNbQ1uDW8NcA1xDXINcw10DXUNdg13DXgNeQ16DXsNfA19 +DX4Nfw2ADYENgg2DDYQNhQ2GDYcNiA2JDYoNiw2MDY0Njg2PDZANkQ2SDZMNlA2VDZYNlw2YDZkNmg2b +DZwNnQ2eDZ8NoA2hDaINow2kDaUNpg2nDagNqQ2qDasNrA2tDa4Nrw2wDbENsg2zDbQNtQ22DbcNuA25 +DboNuw28Db0Nvg2/DcANwQ3CDcMNxA3FDcYNxw3IDckNyg3LDcwNzQ3ODc8N0A3RDdIN0w3UDdUN1g3X +DdgN2Q3aDdsN3A3dDd4N3w3gDeEN4g3jDeQN5Q3mDecN6A3pDeoN6w3sDfQN/A7WD7APsQ+yD7MPtA+1 +D7YPtw+4D7kPug+7D7wPvQ++D78PwA/BD8IPww/ED8UPxg/HD8gPyQPiD8oPyw/MD80Pzg/PD9AP0Q/S +D9MP1A/VD9YP1w/YD9kP2g/bD9wP3Q/eD98P4A/hD+IP4w/kD+UP5g/nD+gP6Q/qD+sP7A/tD+4P7w/w +D/EP8g/zA10P9A/1D/YP9wG5D/gP+Q/6D/sP/A/9D/4P/xAAEAEQAhADEAQCDhAFEAYQBxAIEAkQChAL +EAwQDRAOEA8QEBAREBIQExAUEBUQFhAXEBgQGRAaEBsQHBAdEB4QHxAgECEQIhAjECQQJRAmECcQKBAp +ECoQKxAsEC0QLhAvEDAQMQDVEDIQMxA0EDUQNhA3EDgQORA6EDsQPBA9ED4QPxBAEEEQQhBDEEQQRRBG +EEcQSBBJEEoQSxBMEE0QThBPEFAQURBSEFMQVBBVEFYQVxBYEFkQWhBbEFwQXRBeEF8QYBBhEGIQYxBk +EGUQZhBnEGgQaRBqEGsQbBBtEG4QbxBwEHEQchBzEHQQdRB2EHcQeBB5EHoQexB8EH0QfhB/EIAQgRCC +EIUQiBCLVSRudWxs3xASAA0ADgAPABAAEQASABMAFAAVABYAFwAYABkAGgAbABwAHQAeAB8AIAAhACIA +IwAkACUAJgAnACgAKQAqACsALAAtAC4ALwAwVk5TUm9vdFYkY2xhc3NdTlNPYmplY3RzS2V5c18QD05T +Q2xhc3Nlc1ZhbHVlc18QGU5TQWNjZXNzaWJpbGl0eU9pZHNWYWx1ZXNdTlNDb25uZWN0aW9uc1tOU05h +bWVzS2V5c1tOU0ZyYW1ld29ya11OU0NsYXNzZXNLZXlzWk5TT2lkc0tleXNdTlNOYW1lc1ZhbHVlc18Q +GU5TQWNjZXNzaWJpbGl0eUNvbm5lY3RvcnNdTlNGb250TWFuYWdlcl8QEE5TVmlzaWJsZVdpbmRvd3Nf +EA9OU09iamVjdHNWYWx1ZXNfEBdOU0FjY2Vzc2liaWxpdHlPaWRzS2V5c1lOU05leHRPaWRcTlNPaWRz +VmFsdWVzgAKBBAWBAqGBAyiBBASACIECpoAFgQMngQMpgQKngQQCgACABoECpYEEAxIABJUkgQMq0gAO +ADIAMwA0W05TQ2xhc3NOYW1lgASAA18QEFRQUHJlZmVyZW5jZVBhbmXSADcAOAA5ADpYJGNsYXNzZXNa +JGNsYXNzbmFtZaIAOgA7Xk5TQ3VzdG9tT2JqZWN0WE5TT2JqZWN0XxAQSUJDb2NvYUZyYW1ld29ya9IA +DgA+AD8AQFpOUy5vYmplY3RzgAeg0gA3ADgAQgBDowBDAEQAO1xOU011dGFibGVTZXRVTlNTZXTSAA4A +PgBGAEeAbK8QVwBIAEkASgBLAEwATQBOAE8AUABRAFIAUwBUAFUAVgBXAFgAWQBaAFsAXABdAF4AXwBg +AGEAYgBjAGQAZQBmAGcAaABpAGoAawBsAG0AbgBvAHAAcQByAHMAdAB1AHYAdwB4AHkAegB7AHwAfQB+ +AH8AgACBAIIAgwCEAIUAhgCHAIgAiQCKAIsAjACNAI4AjwCQAJEAkgCTAJQAlQCWAJcAmACZAJoAmwCc +AJ0AnoAJgBmAJ4B1gH+AgYCHgImBAc+BAdGBAdOBAdWBAdeBAdmBAduBAd2BAd+BAeCBAemBAeuBAeyB +AgaBAgiBAgqBAgyBAg6BAhCBAhGBAhOBAhiBAhyBAh2BAh6BAiGBAiSBAieBAiqBAiyBAi+BAjKBAjOB +AjaBAj+BAkKBAkSBAkmBAkuBAk+BAlKBAlSBAleBAlmBAluBAlyBAl2BAl+BAmGBAmOBAmSBAmWBAmeB +AmiBAmyBAm2BAm6BAnGBAnOBAnaBAnmBAn+BAoCBAoKBAoWBAoaBAoeBAomBAoqBAo2BAo6BAo+BApCB +ApOBApeBApmBApuBAp2BAp/UAA4AoAChAKIAowCkAB8Apl1OU0Rlc3RpbmF0aW9uWE5TU291cmNlV05T +TGFiZWyAGIAKgAKAF9cAqAAOAKkAqgCrAKwArQCuAK8AsACxALIAswCuXxAPTlNOZXh0UmVzcG9uZGVy +V05TRnJhbWVWTlNDZWxsWE5TdkZsYWdzWU5TRW5hYmxlZFtOU1N1cGVydmlld4ALgBaADIANEQEMCYAL +1wCoAA4AtgCrALcAMgCtALgAuQC6ALsAvAC9ALhaTlNTdWJ2aWV3c1tOU0ZyYW1lU2l6ZYCrgJ6ArREB +LYEBE4CdgKtfEBd7ezE3OSwgMjUwfSwgezE0MCwgMzJ9fdwAwQAOAMIAwwDEAMUAxgDHAMgAyQDKAMsA +zADNAM4AzwDQANEA0gDTAKQA1QDWANdbTlNDZWxsRmxhZ3NfEBNOU0FsdGVybmF0ZUNvbnRlbnRzXxAS +TlNQZXJpb2RpY0ludGVydmFsXk5TQnV0dG9uRmxhZ3MyXxAPTlNLZXlFcXVpdmFsZW50Wk5TQ29udGVu +dHNZTlNTdXBwb3J0XU5TQ29udHJvbFZpZXdfEA9OU1BlcmlvZGljRGVsYXlcTlNDZWxsRmxhZ3MyXU5T +QnV0dG9uRmxhZ3MSBAH+AIAVgBIQGRABgBOADoAPgAoQyBIIAAAAE/////+GgkD/XlZpcyBjZXJ0aWZp +a2F01AAOANoA2wDcAN0A3gDfAOBWTlNTaXplVk5TTmFtZVhOU2ZGbGFnc4ARI0AqAAAAAAAAgBARBBRc +THVjaWRhR3JhbmRl0gA3ADgA4wDkogDkADtWTlNGb250UNIADgDnAOgA5VlOUy5zdHJpbmeAFNIANwA4 +AOoA66MA6wDsADtfEA9OU011dGFibGVTdHJpbmdYTlNTdHJpbmfSADcAOADuAO+kAO8A8ACqADtcTlNC +dXR0b25DZWxsXE5TQWN0aW9uQ2VsbNIANwA4APIA86UA8wD0APUA9gA7WE5TQnV0dG9uWU5TQ29udHJv +bFZOU1ZpZXdbTlNSZXNwb25kZXJfEBVzaG93Q2VydGlmaWNhdGVCdXR0b27SADcAOAD5APqjAPoA+wA7 +XxAUTlNOaWJPdXRsZXRDb25uZWN0b3JeTlNOaWJDb25uZWN0b3LUAA4AoAChAKIA/QAfAP8BAIAmgAKA +GoAl2ACoAA4AqQCqAKsArAECAK0BAwCvAQUBBgEHALMBCQEDWE5TV2luZG93gBuAFoAcgB4RAQkJgB2A +G9gAqAAOALYAqwC3AQIAMgCtAQwAuQEOAQ8BEAEJAL0BDICPgJ6AkREBEoEByIAdgJ2Aj18QFnt7NTQ1 +LCAzNzl9LCB7MzIsIDMyfX3dAMEADgDCARYAwwDEAMUAxgDHAMgAyQDKAMsAzADNAM4BGQDPARoBGwEc +ANMA/wDVANYBH11OU05vcm1hbEltYWdlgBWAEoAgEIKAJIAfgA+AGhIIRED/ZgBBAGIAbwB1AHQgJtMA +DgAyASIBIwEkASVeTlNSZXNvdXJjZU5hbWWAI4AhgCJXTlNJbWFnZVppY29uLWFib3V00gA3ADgBKQEq +ogEqADtfEBBOU0N1c3RvbVJlc291cmNl0gAOAOcA6ADlgBRfEA9zaG93QWJvdXRTaGVldDrSADcAOAEv +ATCjATAA+wA7XxAVTlNOaWJDb250cm9sQ29ubmVjdG9y1AAOAKAAoQCiAKMAHwE0ATWAGIACgCiAdNwB +NwAOATgBOQE6ATsBPAE9AT4BPwFAAUEBQgFDAUQAzgFGAUcBSAFJAUoA0AFLAUxcTlNXaW5kb3dWaWV3 +XE5TU2NyZWVuUmVjdF1OU1dpbmRvd1RpdGxlWU5TV1RGbGFnc11OU1dpbmRvd0NsYXNzXE5TV2luZG93 +UmVjdFlOU01heFNpemVfEA9OU1dpbmRvd0JhY2tpbmdfEBFOU1dpbmRvd1N0eWxlTWFza1lOU01pblNp +emVbTlNWaWV3Q2xhc3OALIBzgHCAEhJweAAAgCqAKYByEAKAcYArXxAYe3sxOTQsIDM3NX0sIHsyOTUs +IDMwN319XxAPVFBDbG9zaW5nV2luZG930gAOAOcA6AFRgBRUVmlld9cAqAAOALYAqwC3AQIArQFTAVQB +VQFWAVcBUwFZgC2Ab4AuEQEAgG2ALYBu0gAOAD4ARgFcgGynAV0BXgFfAWABYQFiAWOAL4A+gE6AVoBb +gGKAZ9oAqAAOAKkBZQCqAWYAqwCsAQIArQFCAWgBaQCzAWsBbAFWALMBUwFCWk5TRWRpdGFibGVbTlNE +cmFnVHlwZXOALIA9gDgJgDmAMAmALYAs0gAOAD4APwFygAenAXMBdAF1AXYBdwF4AXmAMYAygDOANIA1 +gDaAN18QGUFwcGxlIFBERiBwYXN0ZWJvYXJkIHR5cGVfEBlBcHBsZSBQTkcgcGFzdGVib2FyZCB0eXBl +XxAjQ29yZVBhc3RlYm9hcmRGbGF2b3JUeXBlIDB4NTA0RTQ3NjZfEBVOU0ZpbGVuYW1lc1Bib2FyZFR5 +cGVfEDFOZVhUIEVuY2Fwc3VsYXRlZCBQb3N0U2NyaXB0IHYxLjIgcGFzdGVib2FyZCB0eXBlXxAeTmVY +VCBUSUZGIHY0LjAgcGFzdGVib2FyZCB0eXBlXxAaQXBwbGUgUElDVCBwYXN0ZWJvYXJkIHR5cGVfEBd7 +ezIwLCAxNTl9LCB7MjU1LCAxMjh9fdgAwQAOAYMAxgGEAYUAygGGAYcBiAGJAYoBiQGJAYsBjFdOU1N0 +eWxlV05TQWxpZ25XTlNTY2FsZVpOU0FuaW1hdGVzEiAB/gCAPBAAgDoSAgAAAAjTAA4AMgEiASMBJAGQ +gCOAIYA7VGljb27SADcAOAGTAZSjAZQAqgA7W05TSW1hZ2VDZWxs0gA3ADgBlgGXpQGXAPQA9QD2ADtb +TlNJbWFnZVZpZXfYAKgADgCpAKoAqwCsAQIArQFCAZoBmwGcAVYAswFTAUKALIBNgD+AQAmALYAsXxAW +e3sxNywgMTI5fSwgezI2MSwgMjJ9fdgAwQAOAaIAxgDHAMgAygGjAMwBpAGlAaYBpwFeAakBql8QEU5T +QmFja2dyb3VuZENvbG9yW05TVGV4dENvbG9ygEyARIBBgEKAPhIIQAAAgElYdGVsZXBvcnTUAA4A2gDb +ANwA3QGuAa8BsIARI0AyAAAAAAAAgEMQEF8QEUx1Y2lkYUdyYW5kZS1Cb2xk1QAOAbMBtAG1AbYBtwG4 +AbkBugG7V05TQ29sb3JcTlNDb2xvclNwYWNlW05TQ29sb3JOYW1lXU5TQ2F0YWxvZ05hbWWASIBHEAaA +RoBFVlN5c3RlbVxjb250cm9sQ29sb3LTAA4BtAG/AbcBwQHCV05TV2hpdGWASBADSzAuNjY2NjY2NjkA +0gA3ADgBxAGzogGzADvVAA4BswG0AbUBtgG3AccBuQHIAbuASIBLgEqARV8QEGNvbnRyb2xUZXh0Q29s +b3LTAA4BtAG/AbcBwQHNgEhCMADSADcAOAHPAdCkAdAA8ACqADtfEA9OU1RleHRGaWVsZENlbGzSADcA +OAHSAdOlAdMA9AD1APYAO1tOU1RleHRGaWVsZNoAqAAOAKkB1QCqAKsArAECADIArQFCAdcB2AHZAdoB +VgCzAVMB3QFCXxATTlNPcmlnaW5hbENsYXNzTmFtZYAsgFWAUYBQgFIJgC2AT4AsXxASVFBWZXJzaW9u +VGV4dEZpZWxkXxAWe3sxNywgMTEzfSwgezI2MSwgMTR9fdgAwQAOAaIAxgDHAMgAygGjAMwBpAGlAeQB +5QFfAakBqoBMgESAU4BUgE6ASVk8dmVyc2lvbj7UAA4A2gDbANwA3QHrAN8B7YARI0AmAAAAAAAAgBAR +DBzSADcAOAHvAfCiAfAAO15OU0NsYXNzU3dhcHBlctgAqAAOAKkAqgCrAKwBAgCtAUIBmgH0AfUBVgCz +AVMBQoAsgE2AV4BYCYAtgCxfEBV7ezEyLCA0M30sIHsyNzQsIDM5fX3YAMEADgGiAMYAxwDIAMoBowDM +AaQBpQH9Af4BYAIAAaqATIBEgFmAWoBWEgBAAACASW8QYwCpACAAMgAwADAAMwAtADIAMAAwADgAIABh +AGIAeQBzAHMAbwBmAHQACgBCAGEAcwBlAHIAZQB0ACAAcADlACAAZQBuACAAaQBkAGUAIABhAGYAIABE +AGEAdgBlACAAUwBhAGcAIABvAGcAIABKAGUAcgBlAG0AeQAgAFEAdQBpAG4AbgAuAAoAVABhAGsAIAB0 +AGkAbAAgAE0AYQBuAHUAMAAyACwAIABKAHUAbABpAGUAbgAgAG8AZwAgAFAAYQB1AGwALtQADgDaANsA +3ADdAgUA3wIHgBEjQCQAAAAAAACAEBELG9oAqAAOAKkB1QCqAKsArAECADIArQFCAdcCCwHZAg0CDgCz +AVMCEQFCgCyAVYBdgFCAXhEBAgmALYBcgCxfEA9UUExpbmtUZXh0RmllbGRfEBN7ezEyLCA4fSwgezU5 +LCAxN3192gDBAA4BogDGAMcAyADKAhYCFwGjAhgBpAGlAhsCHAFhAh4AswIgAapfEBFOU0RyYXdzQmFj +a2dyb3VuZF8QE05TUGxhY2Vob2xkZXJTdHJpbmcSBCH+AYBMgESAX4BggFsSEEAAAAmAYYBJaSekACAA +dwBlAGIAcwBpAHQAZdQADgDaANsA3ADdAesA3wGwgBGAEF8QHGh0dHA6Ly90ZWxlcG9ydC5hYnlzc29m +dC5jb23aAKgADgCpAdUAqgCrAKwBAgAyAK0BQgHXAioB2QIsAg4AswFTAhEBQoAsgFWAY4BQgGQJgC2A +XIAsXxAUe3sxMTksIDh9LCB7NTYsIDE3fX3ZAMEADgGiAMYAxwDIAMoCFwGjAMwBpAGlAjUCHAFiAh4C +OAGqgEyARIBlgGCAYoBmgEloJ6QAIABmAG8AcgB1AG0Ac18QMWh0dHA6Ly93d3cuYWJ5c3NvZnQuY29t +L3NvZnR3YXJlL3RlbGVwb3J0L2ZvcnVtcy/aAKgADgCpAdUAqgCrAKwBAgAyAK0BQgHXAj8B2QJBAg4A +swFTAhEBQoAsgFWAaIBQgGkJgC2AXIAsXxAUe3syMjgsIDh9LCB7NTUsIDE3fX3ZAMEADgGiAMYAxwDI +AMoCFwGjAMwBpAGlAkoCHAFjAh4CTQGqgEyARIBqgGCAZ4BrgElnJ6QAIABkAG8AbgBlAHJfEK1odHRw +czovL3d3dy5wYXlwYWwuY29tL2NnaS1iaW4vd2Vic2NyP2NtZD1feGNsaWNrJmJ1c2luZXNzPWp1bCU0 +MG1hYyUyZWNvbSZpdGVtX25hbWU9dGVsZXBvcnQmbm9fc2hpcHBpbmc9MCZub19ub3RlPTAmdGF4PTAm +Y3VycmVuY3lfY29kZT1VU0QmY2hhcnNldD1VVEYlMmQ4JmNoYXJzZXQ9VVRGJTJkONIANwA4AlICU6MC +UwJUADteTlNNdXRhYmxlQXJyYXlXTlNBcnJheVp7Mjk1LCAzMDd90gA3ADgCVwD1owD1APYAO18QFnt7 +MCwgMH0sIHsxNjgwLCAxMDI4fX1aezIxMywgMTI5fV8QGnszLjQwMjgyZSszOCwgMy40MDI4MmUrMzh9 +0gA3ADgCXAJdogJdADtfEBBOU1dpbmRvd1RlbXBsYXRlWGRlbGVnYXRl1AAOAKAAoQCiAKMCYQAfAmOA +GIB2gAKAftcAqAAOAKkAqgCrAKwArQJlAK8CZwJoALIAswJlgHeAFoB4gHkJgHfXAKgADgC2AKsAtwAy +AK0CbAC5Am4AuwJvAL0CbIEBGICegQEagQHEgJ2BARhfEBd7ezE4MywgMTU2fSwgezE4NywgMTh9fd0A +wQAOAMIAwwDEAnQAxQDGAMcAyADJAMoAywJ1AM0AzgDPAUoCeADOAnoA0wJhANUBiQJ9XxAQTlNBbHRl +cm5hdGVJbWFnZRP/////hAH+AIAVgBKAe4ASgHqAD4B2Ekg8Uf9fEBlTeW5rcm9uaXNlciB1ZGtsaXBz +aG9sZGVy0gAOAoACgQKCW05TSW1hZ2VOYW1lgH2AfFhOU1N3aXRjaNIANwA4AoUChqIChgA7XxATTlNC +dXR0b25JbWFnZVNvdXJjZV8QF3NoYXJlUGFzdGVib2FyZENoZWNrYm941AAOAKAAoQCiAKMBNAAfAoyA +GIAogAKAgFthYm91dFdpbmRvd9QADgCgAKEAogCjApAAHwKSgBiAgoACgIbYAKgADgCpAKoAqwCsAQIA +rQEDAK8ClgKXALIAswEJAQOAG4AWgIOAhAmAHYAbXxAXe3sxNTYsIDM4N30sIHsxMTUsIDE4fX3dAMEA +DgDCAMMAxAJ0AMUAxgDHAMgAyQDKAMsCnQDNAM4AzwFKAngAzgKiANMCkADVAYkCfRIkAf4AgBWAEoB7 +gBKAhYAPgIJdRGVsIGRlbm5lIE1hY18QFGFsbG93Q29udHJvbENoZWNrYm941AAOAKAAoQCiAP0AHwCk +AquAJoACgAqAiF8QFnNob3dDZXJ0aWZpY2F0ZVZpZXdlcjrUAA4AoAChAKIAowKvAB8CsYAYgIqAAoEB +ztwBNwAOATgBOQE6ATsBPAE9AT4BPwFAAUEBDAFDArUCtgK3ArgCuQK6AUoCuwK8Ar2Aj4BzgQHLgIwS +QHgAAICNgIuBAc0QD4EBzICOXxAYe3s1MTcsIDQ1MH0sIHs1OTUsIDQyOX19XxAVPDwgZG8gbm90IGxv +Y2FsaXplID4+0gAOAOcA6AFRgBTXAKgADgC2AKsAtwECAK0BCQFUAsUBVgLGAQkCyIAdgG+AkIEByYAd +gQHK0gAOAD4ARgLLgGyhAQOAG9IADgA+AEYCz4BspAKQAP8C0gLTgIKAGoCSgJbYAKgADgCpAKoAqwCs +AQIArQEDAK8C1wLYALIAswEJAQOAG4AWgJOAlAmAHYAbXxAWe3sxOCwgMzg3fSwgezEyNiwgMTh9fd0A +wQAOAMIAwwDEAnQAxQDGAMcAyADJAMoAywDMAM0AzgDPAUoCeADOAuIA0wLSANUBiQJ9gBWAEoB7gBKA +lYAPgJJfEBBBa3RpdmVyIHRlbGVwb3J03QCoAA4C5wLoAKkA5AC2AKsBAgIWAK0C6QLqAQMC7ALtAYkC +7gDTAvABDwEJALMBAwCzAvVeTlNUYWJWaWV3SXRlbXNZTlNUdkZsYWdzXxAWTlNBbGxvd1RydW5jYXRl +ZExhYmVsc18QFU5TU2VsZWN0ZWRUYWJWaWV3SXRlbYAbgQHHgKWApIAPgJeAHQmAGwmAptIADgA+AEYC ++IBsoQL5gJjXAKgADgCpALYAqwECAK0C0wFUAv0C/gFWAQkC04CWgG+Ao4CZgB2AltIADgA+AEYDA4Bs +ogMEAwWAmoCf2ACoAA4AqQCrAQIAMgMHAK0C+QC5AwoBDwEJAwwAvQL5W05TRXh0ZW5zaW9ugJiAnoCb +gB2AnICdgJhfEBZ7ezIwLCAxN30sIHs1MTEsIDI5Nn19XFRQTGF5b3V0Vmlld9IANwA4AxIDE6QDEwD1 +APYAO1xOU0N1c3RvbVZpZXfYAKgADgCpAKoAqwCsAQIArQL5AZoDFwMYAxkAswEJAvmAmIBNgKCAoREB +IgmAHYCYXxAWe3sxNywgLTEzfSwgezQzNCwgMjh9fdgAwQAOAaIAxgDHAMgAygGjAMwBpAGlAyEB5QMF +AyQBqoBMgESAooBUgJ8SAEIAAIBJbxBMAEEAcgByAGEAbgBnAGUAcgAgAGQAZQAgAGQAZQBsAHQAZQAg +AE0AYQBjAHMAIABmAHIAYQAgAHQAbwBwAHAAZQBuACwAIABvAG0AawByAGkAbgBnACAAZABpAG4AIABz +AGsA5gByAG0AIABmAG8AcgAgAGEAdAAgAGsAbwBuAHQAcgBvAGwAbABlAHIAZQAgAGQAZQBtAC5fEBZ7 +ezEwLCAzM30sIHs1NDksIDMxOX19XxAVe3sxLCAxMH0sIHs1NjksIDM2NX190gAOAD4ARgMrgGyjAvUD +LQMugKaAqoEBFtYADgMwAPUDMQGzAKIDMgMzAvkC0wGlAzdcTlNJZGVudGlmaWVyWU5TVGFiVmlld4Cp +gKeAmICWgESAqFZsYXlvdXRWTGF5b3V00gA3ADgDOwM8ogM8ADtdTlNUYWJWaWV3SXRlbdUADgD1AzEB +swCiAzIAuALTAaUDQoCpgKuAloBEgQEV1QCoAA4AqQC2AKsAKwFUA0YDRwFWgACAb4EBFICs0gAOAD4A +RgNKgGyhAK6AC9IADgA+AEYDToBsqQNPA1AApANSA1MDVANVA1YDV4CugLKACoC3gLuAv4DSgQELgQEP +1wCoAA4AqQCqAKsArACtAK4BmgNbA1wDXQCzAK6AC4BNgK+AsBEBJAmAC18QF3t7MTgyLCAxMDF9LCB7 +MzA2LCAxNH192ADBAA4BogDGAMcAyADKAaMAzAGkAaUDZAHlA08DZwGqgEyARICxgFSArhIQQgAAgElv +EC8ARgBvAHIAIABhAHQAIABzAGwAZQB0AHQAZQAgAGUAbgAgAHYA5gByAHQALAAgAG0AYQByAGsAZQBy +ACAAbwBnACAAdAByAHkAawAgAGIAYQBjAGsAcwBwAGEAYwBl1wCoAA4AqQCqAKsArACtAK4ArwNtA24D +bwCzAK6AC4AWgLOAtBP/////gAABDAmAC18QF3t7MTc5LCAyNTB9LCB7MTY3LCAzMn193ADBAA4AwgDD +AMQAxQDGAMcAyADJAMoAywDMAM0AzgDPANADdgN3ANMDUADVANYA14AVgBKAtoC1gA+Asm8QEgBWAOYA +bABnACAAYwBlAHIAdABpAGYAaQBrAGEAdAAuAC4ALtIADgDnAOgA5YAU1wCoAA4AqQCqAKsArACtAK4A +rwOAA4EAsgCzAK6AC4AWgLiAuQmAC18QF3t7MTgzLCAyODZ9LCB7MTM1LCAxOH193QDBAA4AwgDDAMQC +dADFAMYAxwDIAMkAygDLAMwAzQDOAM8BSgJ4AM4DigDTA1IA1QGJAn2AFYASgHuAEoC6gA+At28QEgBT +AGwA5QAgAGsAcgB5AHAAdABlAHIAaQBuAGcAIAB0AGkAbNcAqAAOAKkAqgCrAKwArQCuAZoDkQOSALIA +swCugAuATYC8gL0JgAtfEBZ7ezIzLCAyODd9LCB7MTQ4LCAxN3192ADBAA4BogDGAMcAyADKAaMAzAGk +AaUDmQDTA1MDnAGqgEyARIC+gA+AuxIEQAAAgElaS3J5cHRlcmluZ98QEgCoAA4AqQOgA6EDogGiA6MD +pADkA6UAqwCsA6YArQOnA6gDqQCuA6sDrAOtAcEDrgGlA7ADsQDTA7MDXQCzANAArgO2A7cDuFtOU1By +b3RvQ2VsbFlOU051bVJvd3NeTlNTZWxlY3RlZENlbGxbTlNDZWxsQ2xhc3NfEBVOU0NlbGxCYWNrZ3Jv +dW5kQ29sb3JaTlNDZWxsU2l6ZVlOU051bUNvbHNfEBJOU0ludGVyY2VsbFNwYWNpbmddTlNNYXRyaXhG +bGFnc1dOU0NlbGxzgAuA0YDAgM6AwoBEgM2A0IAPgMsJgAuAzBJEKCAAgMFfEBZ7ezE4NSwgMjN9LCB7 +Mjk5LCA1OH190gAOAD4ARgO8gGyjA64DvgO/gMKAx4DJ3QDBAA4AwgDDAMQCdADFAMYAxwDIAMkAygDL +AnUAzQPCAM8BiQPDA8IDxQDTA1QA1QGJAn2AFYDGgMSAxoDDgA+Av28QHwBTAHAA+AByAGcAIABtAGkA +ZwAgAG8AbQAgAGUAbgAgAHYA5gByAHQAIABlAHIAIABiAGUAdAByAG8AZQB00gAOAoACgQPLgH2AxV1O +U1JhZGlvQnV0dG9u0gAOAOcA6ADlgBTeAMEADgDCAMMAxAJ0AMUAxgDHAMgAyQDKAMsD0ADMAM0DwgDP +AYkDwwPCA9UA0wNUANUBiQJ9ANBVTlNUYWeAFYDGgMSAxoDIgA+Av18QJ0FmdmlzIGh2aXMgaWtrZSBk +ZW4gYWxsZXJlZGUgZXIgYmV0cm9ldN4AwQAOAMIAwwDEAnQAxQDGAMcAyADJAMoAywPQAMwAzQDOA9wB +iQPDAM4D3wDTA1QD4gGJAn0BSoAVgBIQS4DEgBKAyoAPgL8RAZBfEBNBY2NlcHRlciBhdXRvbWF0aXNr +WXsyOTksIDE4fVZ7NCwgMn3aAMEADgDDAMQCdADGAMcAyQDKAMsAzADNA9wBiQPDA+kA0wPiAYkCfYAV +gMSAz4APVVJhZGlv0wAOAbQBvwG3AcED7oBIQjEA0gA3ADgD8APxpQPxAPQA9QD2ADtYTlNNYXRyaXje +AKgD8wAOAKkD9AP1A/YAtgP3AKsArQP4A/kD+gCuA/wD/QP+AUoD/wQABAEEAgEPAK4EBAQFBAVbTlNI +U2Nyb2xsZXJYTlNzRmxhZ3NcTlNDb3JuZXJWaWV3XxAQTlNIZWFkZXJDbGlwVmlld1xOU1Njcm9sbEFt +dHNbTlNWU2Nyb2xsZXJdTlNOZXh0S2V5Vmlld11OU0NvbnRlbnRWaWV3gAuBAQWBAQqBAQmA3oDbgNNP +EBBBIAAAQSAAAEGYAABBmAAAgAuBAQGA1IDU0gAOAD4ARgQJgGylBAUEBAP8BAAD/4DUgQEBgQEFgNuA +3toAqAAOAKkEEAC2AKsEEQQSAK0D+QNVBBQEFQQWBBcEGAQZBBoDVQQZWU5TY3ZGbGFnc1lOU0RvY1Zp +ZXdZTlNCR0NvbG9ygNKBAQCA/xAEgNURCQCA1oDsgNKA1tIADgA+AEYEH4BsoQQZgNbfEBUAqAQiAA4C +6AHVBCMBogQkA/UEJQQmBCcAqwC3AKwAMgQoBCkArQQqBCsEBQGJAdcELgQvBDADsQCzA/8ENADQBDUB +VgQ2ALMEOAQ5ArsEBQQ7BDxfEB9OU0RyYWdnaW5nU291cmNlTWFza0Zvck5vbkxvY2FsXE5TSGVhZGVy +Vmlld18QEk5TQWxsb3dzVHlwZVNlbGVjdF8QF05TSW50ZXJjZWxsU3BhY2luZ1dpZHRoXxAZTlNDb2x1 +bW5BdXRvcmVzaXppbmdTdHlsZV8QGE5TSW50ZXJjZWxsU3BhY2luZ0hlaWdodFtOU0dyaWRDb2xvcl8Q +HE5TRHJhZ2dpbmdTb3VyY2VNYXNrRm9yTG9jYWxeTlNUYWJsZUNvbHVtbnNbTlNSb3dIZWlnaHSA1IBV +EhrAgACA2IDagNAJgN4jQAgAAAAAAAAjQAAAAAAAAACA2QmA14D8gNSA4SNAMQAAAAAAAFtUUFRhYmxl +Vmlld1tOU1RhYmxlVmlld1p7MzMzLCAxMDR91gCoAA4AqwC3AK0EPgQABEIBVgRDBAAEGYDbgN2A3IDb +gNbaAKgADgCpBBAAtgCrBBEEEgCtA/kDVQQUBEkEFgRKBBgEMAQaA1UEMIDSgQEAgQEIgQEHgNqA7IDS +gNpZezMzMywgMTd90gA3ADgEUQRSpARSAPUA9gA7XxARTlNUYWJsZUhlYWRlclZpZXfVAKgADgCpAKsA +rQNVBFUEVgFWA1WA0oDggN+A0l8QFHt7LTI2LCAwfSwgezE2LCAxN3190gA3ADgEWgRbpARbAPUA9gA7 +XV9OU0Nvcm5lclZpZXfSAA4APgBGBF6AbKMEXwRgBGGA4oDvgPTaBGMADgRkAzAEZQRmBGcEaARpBD4A +swRrBGwEbQRuBG8A0ARwBHEEGV5OU0lzUmVzaXplYWJsZVxOU0hlYWRlckNlbGxXTlNXaWR0aFpOU0Rh +dGFDZWxsXk5TUmVzaXppbmdNYXNrWk5TTWluV2lkdGhaTlNNYXhXaWR0aAmA7oDkgOMjQGKgAAAAAACA +6iNARAAAAAAAACNAj0AAAAAAAIDWVG5hbWXXAMEADgGiAMYAxwDKAaMEdQR2BHcEeAHlAYkEehIEgf4A +gOmA5oDlgFSA51ROYXZu0wAOAbQBvwG3AcEEfoBISzAuMzMzMzMyOTkA1QAOAbMBtAG1AbYBtwHHAbkE +ggG7gEiAS4DogEVfEA9oZWFkZXJUZXh0Q29sb3LSADcAOASGBIelBIcB0ADwAKoAO18QEU5TVGFibGVI +ZWFkZXJDZWxs2ADBAA4BogDGAMcAyADKAaMEiQGkBBoEjAHlBBkEjwGqEhQh/kCATIDsgOuAVIDWEgAC +CACASVlUZXh0IENlbGzVAA4BswG0AbUBtgG3AbgBuQSVAbuASIBHgO2ARV8QFmNvbnRyb2xCYWNrZ3Jv +dW5kQ29sb3LSADcAOASZBJqiBJoAO11OU1RhYmxlQ29sdW1u2gRjAA4EZAMwBGUEZgRnBGgEaQQ+ALME +awSeBJ8EoAShANAEogRxBBkJgO6A8YDwI0BdAAAAAAAAgPMjQCAAAAAAAACA1ldhZGRyZXNz1wDBAA4B +ogDGAMcAygGjBHUEdgR3BKgB5QGJBHqA6YDmgPKAVIDnWEFkZHJlc3Nl2ADBAA4BogDGAMcAyADKAaME +iQGkBBoEjAHlBBkEjwGqgEyA7IDrgFSA1oBJ2AAOBGQDMARlBGYEaARpBD4EawS1BLYEtwS4BLkEcQQZ +gO6A9oD1I0BNgAAAAAAAgPojQE1Jr+AAAACA1ltjZXJ0aWZpY2F0ZdcAwQAOAaIAxgDHAMoBowDMBHYE +vgS/AeUBiQR6gOmA+ID3gFSA51pDZXJ0aWZpa2F01QAOAbMBtAG1AbYBtwOxAbkExgG7gEiA0ID5gEVb +aGVhZGVyQ29sb3LcAMEADgDCAMMAxADFAMYAxwDIAMkAygDLAMwAzQDOA9wEzADOBM4B5QQZA+IE0QTS +gBWAEhAkgBKA+4BUgNYSCAIAABP/////hrZA/9UADgGzAbQBtQG2AbcE1QG5BNYBu4BIgP6A/YBFWWdy +aWRDb2xvctMADgG0Ab8BtwHBBNuASEQwLjUAXxAVe3sxLCAxN30sIHszMzMsIDEwNH190gA3ADgE3gTf +pATfAPUA9gA7Wk5TQ2xpcFZpZXfYAKgE4QAOAKkAqwTiAK0E4wNVA1UE5gTnAVYE6ANVBOpYTlNUYXJn +ZXRYTlNBY3Rpb25ZTlNQZXJjZW50gNKA0oEBBIEBAoEBA4DSIz/tgtggAAAAXxAZe3stMTAwLCAtMTAw +fSwgezE1LCAxODB9fVxfZG9TY3JvbGxlcjrSADcAOATuBO+lBO8A9AD1APYAO1pOU1Njcm9sbGVy2QCo +BOEADgCpA/QAqwTiAK0E4wNVA1UE5gT0ANABVgToA1UE94DSgNKBAQSBAQaBAQOA0iM/77H7IAAAAF8Q +GXt7LTEwMCwgLTEwMH0sIHs0NjMsIDE1fX3SAA4APgBGBPuAbKEEMIDaXxATe3sxLCAwfSwgezMzMywg +MTd9fV8QGHt7MTg1LCAxMTZ9LCB7MzM1LCAxMjJ9fdIANwA4BQAFAaQFAQD1APYAO1xOU1Njcm9sbFZp +ZXfXAKgADgCpAKoAqwCsAK0ArgGaBQUFBgNdALMAroALgE2BAQyBAQ0JgAtfEBV7ezE0LCA0N30sIHsx +NTcsIDM0fX3YAMEADgGiAMYAxwDIAMoBowDMAaQBpQUNANMDVgOcAaqATIBEgQEOgA+BAQuASW8QHgBG +AG8AcgBlAHMAcAD4AHIAZwBzAGwAZQByACAAbwBtACAAawBvAG4AdAByAG8AbABsAGUAcgBpAG4AZ9cA +qAAOAKkAqgCrAKwArQCuAZoFFQUWALIAswCugAuATYEBEIEBEQmAC18QFXt7OCwgMjIxfSwgezE2Mywg +MTd9fdgAwQAOAaIAxgDHAMgAygGjAMwBpAGlBR0A0wNXA5wBqoBMgESBARKAD4EBD4BJbxAPAEIAZQB0 +AHIAbwBlAGQAZQAgAHYA5gByAHQAZQByWns1NDksIDMxOX1fEBdTaWtrZXJoZWRzaW5kc3RpbGxpbmdl +ctYADgMwAPUDMQGzAKIDMgUmAmwC0wGlBSqAqYEBF4EBGICWgESBAcZXb3B0aW9uc9UAqAAOAKkAtgCr +ACsBVAUvBTABVoAAgG+BAcWBARnSAA4APgBGBTOAbKECZYB30gAOAD4ARgU3gGyvEBYFOAU5BToFOwU8 +BT0FPgJhBUAFQQVCBUMFRAVFBUYFRwVIBUkFSgVLBUwFTYEBG4EBH4EBI4EBJ4EBK4EBL4EBM4B2gQE3 +gQE7gQFZgQFdgQFhgQFlgQFpgQGWgQGngQGrgQGwgQG0gQG8gQHA1wCoAA4AqQCqAKsArACtAmUArwVR +BVIAsgCzAmWAd4AWgQEcgQEdCYB3XxAXe3syMTEsIDEzMn0sIHsxNTMsIDE4fX3dAMEADgDCAMMAxAJ0 +AMUAxgDHAMgAyQDKAMsAzADNAM4AzwFKAngAzgVbANMFOADVAYkCfYAVgBKAe4ASgQEegA+BARtfEBNL +dW4gaHZpcyBtaW5kcmUgZW5k1wCoAA4AqQCqAKsArACtAmUBmgViBWMAsgCzAmWAd4BNgQEggQEhCYB3 +XxAVe3s5MCwgMTY0fSwgezgxLCAzNH192ADBAA4BogDGAMcAyADKAaMAzAGkAaUFagDTBTkDnAGqgEyA +RIEBIoAPgQEfgElsAE8AdgBlAHIAZgD4AHIAcwBsAGUAcgA61wCoAA4AqQCqAKsArACtAmUBmgVyBXMA +sgCzAmWAd4BNgQEkgQElCYB3XxAUe3sxNywgMX0sIHsxNTQsIDM0fX3YAMEADgGiAMYAxwDIAMoBowDM +AaQBpQV6ANMFOgOcAaqATIBEgQEmgA+BASOASV8QFlZlcnNpb24ga29udHJvbGxlcmluZzrXAKgADgCp +AKoAqwCsAK0CZQGaBYIFgwCyALMCZYB3gE2BASiBASkJgHdfEBZ7ezEwMiwgMjg3fSwgezY5LCAxN319 +2ADBAA4BogDGAMcAyADKAaMAzAGkAaUFigDTBTsDnAGqgEyARIEBKoAPgQEngElWU2tpZnQ61wCoAA4A +qQCqAKsArACtAmUArwWSBZMAsgCzAmWAd4AWgQEsgQEtCYB3XxAWe3sxODMsIDc0fSwgezE3OCwgMTh9 +fd0AwQAOAMIAwwDEAnQAxQDGAMcAyADJAMoAywDMAM0AzgDPAUoCeADOBZwA0wU8ANUBiQJ9gBWAEoB7 +gBKBAS6AD4EBK18QF1ZpcyBzdGF0dXMgaSBtZW51IGJhcmVu1wCoAA4AqQCqAKsArACtAmUArwWjBaQA +sgCzAmWAd4AWgQEwgQExCYB3XxAXe3sxODMsIDE4MH0sIHsyMjQsIDE4fX3dAMEADgDCAMMAxAJ0AMUA +xgDHAMgAyQDKAMsCdQDNAM4AzwFKAngAzgWtANMFPQDVAYkCfYAVgBKAe4ASgQEygA+BAS9vEB4AVABy +AOYAawAgAG8AZwAgAHMAbABpAHAAIABmAGkAbABlAHIAIABtAGUAbABsAGUAbQAgAE0AYQBjAHPXAKgA +DgCpAKoAqwCsAK0CZQCvBbQFtQCyALMCZYB3gBaBATSBATUJgHdfEBd7ezIxMSwgMTA4fSwgezE3Mywg +MTh9fd0AwQAOAMIAwwDEAnQAxQDGAMcAyADJAMoAywDMAM0AzgDPAUoCeADOBb4A0wU+ANUBiQJ9gBWA +EoB7gBKBATaAD4EBM18QGUt1biBodmlzIHRhc3QgZXIgdHJ5a2tldDrXAKgADgCpAKoAqwCsAK0CZQCv +BcUFxgCyALMCZYB3gBaBATiBATkJgHdfEBd7ezE4MywgMjg2fSwgezIzMiwgMTh9fd0AwQAOAMIAwwDE +AnQAxQDGAMcAyADJAMoAywDMAM0AzgDPAUoCeADOBc8A0wVAANUBiQJ9gBWAEoB7gBKBATqAD4EBN18Q +H1NraWZ0IGt1biBodmlzIHRhc3QgZXIgdHJ5a2tldDrXAKgADgCpAKoAqwCsAK0CZQXVBdYF1wCyALMC +ZYB3gQFYgQE8gQE9CYB3XxAXe3szODcsIDEwM30sIHsxMjksIDI2fX3fEBIAwQXcBd0AwgDDAA4AxADF +AMcF3gDIBd8F4AXhAMkAygXiAMsF4wCzANAAzgPcBeYA0AXnANMF6QVBAcEAswCzA+IF7QXuBe9fEBpO +U01lbnVJdGVtUmVzcGVjdEFsaWdubWVudF8QD05TQXJyb3dQb3NpdGlvblpOU01lbnVJdGVtXxAPTlNQ +cmVmZXJyZWRFZGdlXxASTlNVc2VzSXRlbUZyb21NZW51XU5TQWx0ZXJzU3RhdGVWTlNNZW51E/////+E +Qf5ACYASgQFXgQE+gA+BAT+BATsJCREIAIEBQBIGgkD/0gAOAOcA6ADlgBTcBOEADgXzBfQF9QX2BfcF ++AXiBOID0AX5BdcF+wX8Bf0AzgX/BgAGAQXuBgMGBADQV05TVGl0bGVfEBFOU0tleUVxdWl2TW9kTWFz +a1pOU0tleUVxdWl2XU5TTW5lbW9uaWNMb2NZTlNPbkltYWdlXE5TTWl4ZWRJbWFnZVdOU1N0YXRlgQE9 +gQFHgQFBEgAQAACAEhJ/////gQFCgQFEgQFAgQFGEBTTAA4F8wYGBgcGCAYJW05TTWVudUl0ZW1zgQFW +gQFIgQFJaSMYACAAQwBvAG0AbQBhAG4AZNMADgAyASIBIwEkBg6AI4AhgQFDXxAPTlNNZW51Q2hlY2tt +YXJr0wAOADIBIgEjASQGE4AjgCGBAUVfEBBOU01lbnVNaXhlZFN0YXRlXxARX3BvcFVwSXRlbUFjdGlv +bjrSADcAOAYXBd6iBd4AO9IADgDnAOgGGoAUWk90aGVyVmlld3PSAA4APgBGBh2AbKUF6QYfBiAGIQYi +gQE/gQFKgQFNgQFQgQFT2wThAA4F8wX0BfUF9gX3BfgF4gTiA9AF1wX7BiYF/QDOBf8GAAYBBe4GKwYs +gQE9gQFHgQFLgBKBAUKBAUSBAUCBAUwQE2gjJQAgAE8AcAB0AGkAbwBu2wThAA4F8wX0BfUF9gX3BfgF +4gTiA9AF1wX7BjEF/QDOBf8GAAYBBe4GNgY3gQE9gQFHgQFOgBKBAUKBAUSBAUCBAU8QEmkjAwAgAEMA +bwBuAHQAcgBvAGzbBOEADgXzBfQF9QX2BfcF+AXiBOID0AXXBfsGPAX9AM4F/wYABgEF7gZBBkKBAT2B +AUeBAVGAEoEBQoEBRIEBQIEBUhARZyHnACAAUwBoAGkAZgB02wThAA4F8wX0BfUF9gX3BfgF4gTiA9AF +1wX7BkcF/QDOBf8GAAYBBe4GTAGwgQE9gQFHgQFUgBKBAUKBAUSBAUCBAVVrIeoAIABDAGEAcABzACAA +TABvAGMAa9IANwA4Bk8F4qIF4gA70gA3ADgGUQZSpgZSBlMA7wDwAKoAO18QEU5TUG9wVXBCdXR0b25D +ZWxsXk5TTWVudUl0ZW1DZWxs0gA3ADgGVQZWpgZWAPMA9AD1APYAO11OU1BvcFVwQnV0dG9u1wCoAA4A +qQCqAKsArACtAmUBmgZaBlsAsgCzAmWAd4BNgQFagQFbCYB3XxAWe3szNDgsIDI2MX0sIHszMywgMTd9 +fdgAwQAOAaIAxgDHAMgAygGjAMwBpAGlBmIB5QVCAh4BqoBMgESBAVyAVIEBWYBJVEtvcnTXAKgADgCp +AKoAqwCsAK0CZQCvBmoGawCyALMCZYB3gBaBAV6BAV8JgHdfEBd7ezE4MywgMjE0fSwgezIwMywgMTh9 +fd0AwQAOAMIAwwDEAnQAxQDGAMcAyADJAMoAywDMAM0AzgDPAUoCeADOBnQA0wVDANUBiQJ9gBWAEoB7 +gBKBAWCAD4EBXW8QGgBGAG8AcgBzAPgAZwAgAGEAdAAgAHYA5gBrACAAcwBvAHYAZQBuAGQAZQAgAE0A +YQBjAHPXAKgADgCpAKoAqwCsAK0CZQCvBnsGfACyALMCZYB3gBaBAWKBAWMJgHdfEBd7ezE4MywgMjYy +fSwgezE1OSwgMTh9fd0AwQAOAMIAwwDEAnQAxQDGAMcAyADJAMoAywDMAM0AzgDPAUoCeADOBoUA0wVE +ANUBiQJ9gBWAEoB7gBKBAWSAD4EBYV8QFlNraWZ0IG1lZCBmb3JzaW5rZWxzZTrXAKgADgCpAKoAqwCs +AK0CZQCvBowGjQCyALMCZYB3gBaBAWaBAWcJgHdfEBd7ezE4MywgMjM4fSwgezIwNCwgMTh9fd0AwQAO +AMIAwwDEAnQAxQDGAMcAyADJAMoAywDMAM0AzgDPAUoCeADOBpYA0wVFANUBiQJ9gBWAEoB7gBKBAWiA +D4EBZV8QFVNraWZ0IHZlZCBkdWJiZWx0a2xpa9cAqAAOAKkAqgCrAKwArQJlAZoGnQaeALIAswJlgHeA +TYEBaoEBawmAd18QFnt7MzcwLCAxMzB9LCB7NTgsIDIyfX3aAMEADgGiAMcAyAajAMoCFgIXAaMGpAGk +BqYA0wVGBqkGqgCzBqwGrVtOU0Zvcm1hdHRlchP/////lHH+QYBMgQGSgA+BAWmBAWwSBEAEAAmBAZGB +AZTfEBEADgavBrAGsQayBrMGtAa1BrYGtwa4BrkGuga7BrwGvQa+Br8GwAbBBsIAKwbEBsUGxgbHBsgA +swbKACsGzAbNAYwBjFZOUy5uaWxaTlMuZGVjaW1hbFZOUy5uYW5bTlMucm91bmRpbmdXTlMuemVyb18Q +EE5TLm5lZ2F0aXZlYXR0cnNWTlMubWF4XU5TLmF0dHJpYnV0ZXNfEBFOUy5wb3NpdGl2ZWZvcm1hdF8Q +D05TLmFsbG93c2Zsb2F0c18QEU5TLm5lZ2F0aXZlZm9ybWF0XxAQTlMucG9zaXRpdmVhdHRyc1tOUy50 +aG91c2FuZFZOUy5taW5cTlMubG9jYWxpemVkXxAPTlMuaGFzdGhvdXNhbmRzgQGQgQGMgQGDgQGOgACB +AX2BAYKBAYiBAW2BAXwJgQF+gACBAYqBAXoICNMADgbRAD4G0gbTBuBXTlMua2V5c4EBi6wG1AbVBtYG +1wbYBtkG2gbbBtwG3QbeBt+BAW6BAW+BAXCBAXGBAXKBAXOBAXSBAXWBAXaBAXeBAXiBAXmsBs0GyAbE +BsUGwQbmBucG6AbGBuoGygbMgQF6gQF8gQF9gQGCgQGDgQGEgQGFgQGHgQGIgQGJgQF+gQGKV21pbmlt +dW1ecG9zaXRpdmVGb3JtYXRfEBdhdHRyaWJ1dGVkU3RyaW5nRm9yWmVyb18QH3RleHRBdHRyaWJ1dGVz +Rm9yTmVnYXRpdmVWYWx1ZXNfEBBkZWNpbWFsU2VwYXJhdG9yXxARZm9ybWF0dGVyQmVoYXZpb3JWbG9j +YWxlXGFsbG93c0Zsb2F0c1dtYXhpbXVtXxAVdXNlc0dyb3VwaW5nU2VwYXJhdG9yXm5lZ2F0aXZlRm9y +bWF0XxARZ3JvdXBpbmdTZXBhcmF0b3LXAA4G+gb7BvwG/Qb+Bv8HAAGMAYkBSgGJBwIBjFpOUy5jb21w +YWN0W05TLmV4cG9uZW50Xk5TLm1hbnRpc3NhLmJvWU5TLmxlbmd0aFtOUy5tYW50aXNzYVtOUy5uZWdh +dGl2ZYEBewhPEBAAAAAAAAAAAAAAAAAAAAAACNIANwA4BwUHBqIHBgA7XxAaTlNEZWNpbWFsTnVtYmVy +UGxhY2Vob2xkZXJSMEvTAA4HCQDsBwoHCwbKXE5TQXR0cmlidXRlc4EBgYEBf4EBflEw0wAOBtEAPgcP +BxAHEYEBgKCg0gA3ADgHEwcUogcUADtcTlNEaWN0aW9uYXJ50gA3ADgHFgcXogcXADtfEBJOU0F0dHJp +YnV0ZWRTdHJpbmfTAA4G0QA+Bw8HGgcbgQGAoKBhAKARA+jSAA4HHwcgAM5dTlMuaWRlbnRpZmllcoEB +hoAS0gA3ADgHIwckogckADtYTlNMb2NhbGXXAA4G+gb7BvwG/Qb+Bv8HAACzAYkBSgFKBygBjIEBewlP +EBAAAEAAAAAAAAAAAAAAAAAACFEs0gA3ADgHLActowctBxQAO18QE05TTXV0YWJsZURpY3Rpb25hcnnS +AA4A7AcKBzCBAYGBAY3TAA4HCQDsBwoHCwc0gQGBgQF/gQGPU05hTtIANwA4BzcHOKMHOAajADtfEBFO +U051bWJlckZvcm1hdHRlclRzaXpl1QAOAbMBtAG1AbYBtwOxAbkHPQG7gEiA0IEBk4BFXxATdGV4dEJh +Y2tncm91bmRDb2xvctUADgGzAbQBtQG2AbcBxwG5B0MBu4BIgEuBAZWARVl0ZXh0Q29sb3LXAKgADgCp +AKoAqwCsAK0CZQXVB0kHSgCyALMCZYB3gQFYgQGXgQGYCYB3XxAXe3szODcsIDI4MX0sIHsxMjksIDI2 +fX3fEBIAwQXcBd0AwgDDAA4AxADFAMcF3gDIBd8F4AXhAMkAygXiAMsF4wCzANAAzgPcBeYA0AdSANMH +VAVHAcEAswCzA+IF7QdYBe8JgBKBAVeBAZmAD4EBmoEBlgkJgQGb0gAOAOcA6ADlgBTcBOEADgXzBfQF +9QX2BfcF+AXiBOID0AX5B0oF+wX8Bf0AzgX/BgAGAQdYB2MGBADQgQGYgQFHgQFBgBKBAUKBAUSBAZuB +AZzTAA4F8wYGBgcHZgdngQFWgQGdgQGe0gAOAOcA6AYagBTSAA4APgBGB2yAbKUHVAduB28HcAdxgQGa +gQGfgQGhgQGjgQGl2wThAA4F8wX0BfUF9gX3BfgF4gTiA9AHSgX7BiYF/QDOBf8GAAYBB1gHegYsgQGY +gQFHgQFLgBKBAUKBAUSBAZuBAaDbBOEADgXzBfQF9QX2BfcF+AXiBOID0AdKBfsGMQX9AM4F/wYABgEH +WAeDBjeBAZiBAUeBAU6AEoEBQoEBRIEBm4EBotsE4QAOBfMF9AX1BfYF9wX4BeIE4gPQB0oF+wY8Bf0A +zgX/BgAGAQdYB4wGQoEBmIEBR4EBUYASgQFCgQFEgQGbgQGk2wThAA4F8wX0BfUF9gX3BfgF4gTiA9AH +SgX7BkcF/QDOBf8GAAYBB1gHlQGwgQGYgQFHgQFUgBKBAUKBAUSBAZuBAabXAKgADgCpAKoAqwCsAK0C +ZQCvB5kHmgCyALMCZYB3gBaBAaiBAakJgHdfEBZ7ezE4MywgNTB9LCB7Mjk0LCAxOH193QDBAA4AwgDD +AMQCdADFAMYAxwDIAMkAygDLAnUAzQDOAM8BSgJ4AM4HowDTBUgA1QGJAn2AFYASgHuAEoEBqoAPgQGn +bxApAFYAaQBzACAAbQBlAGQAZABlAGwAZQBzAGUAIABuAOUAcgAgAGEAbgBkAHIAZQAgAE0AYQBjAHMA +IABrAG8AbgB0AHIAbwBsAGwAZQByAGUAc9cAqAAOAKkAqgCrAKwArQJlAK8HqgerALIAswJlgHeAFoEB +rIEBrQmAd18QFXt7Mzg0LCA3fSwgezExMSwgMzJ9fdwAwQAOAMIAwwDEAMUAxgDHAMgAyQDKAMsAzADN +AM4AzwDQB7IHswDTBUkA1QDWANeAFYASgQGvgQGugA+BAatZQ2hlY2sgTm930gAOAOcA6ADlgBTXAKgA +DgCpAKoAqwCsAK0CZQGaB7wHvQCyALMCZYB3gE2BAbGBAbIJgHdfEBV7ezEwMiwgNzV9LCB7NjksIDE3 +fX3YAMEADgGiAMYAxwDIAMoBowDMAaQBpQfEANMFSgOcAaqATIBEgQGzgA+BAbCASVdTdGF0dXM61wCo +AA4AqQCqAKsArACtAmUHywfMB80AsgCzAmWAd4EBu4EBtYEBtgmAd18QFnt7Mzg2LCAyNjJ9LCB7ODMs +IDE1fX3eB9IAwQAOB9MH1ADGAMcAyAfVB9YAygfXB9gH2QfaAMwH2wGJANAH3AfdBUsH3wfgB+EBjAfj +AYxXTlNWYWx1ZV8QE05TTnVtYmVyT2ZUaWNrTWFya3NfEBJOU1RpY2tNYXJrUG9zaXRpb25aTlNNYXhW +YWx1ZVpOU01pblZhbHVlWk5TVmVydGljYWxdTlNBbHRJbmNWYWx1ZV8QGk5TQWxsb3dzVGlja01hcmtW +YWx1ZXNPbmx5Iz/gAAAAAAAAgQG6gQG3gQG4gQG0Iz/wAAAAAAAAIz+5mZmZmZmaEgACAAAIIwAAAAAA +AAAACNIADgDnAOgA5YAU1AAOANoA2wDcAN0H6QfqAbCAESNAKAAAAAAAAIEBuVlIZWx2ZXRpY2HSADcA +OAftB+6kB+4A8ACqADtcTlNTbGlkZXJDZWxs0gA3ADgH8AfxpQfxAPQA9QD2ADtYTlNTbGlkZXLXAKgA +DgCpAKoAqwCsAK0CZQGaB/UH9gCyALMCZYB3gE2BAb2BAb4JgHdfEBZ7ezQ3NCwgMjYxfSwgezMxLCAx +N3192ADBAA4BogDGAMcAyADKAaMAzAGkAaUH/QHlBUwCHgGqgEyARIEBv4BUgQG8gElUTGFuZ9cAqAAO +AKkAqgCrAKwArQJlAK8IBQgGALIAswJlgHeAFoEBwYEBwgmAd18QFnt7MTgzLCAxNn0sIHsyNzEsIDE4 +fX3dAMEADgDCAMMAxAJ0AMUAxgDHAMgAyQDKAMsAzADNAM4AzwFKAngAzggPANMFTQDVAYkCfYAVgBKA +e4ASgQHDgA+BAcBfEBxLb250cm9sbGVyIHZlcnNpb24gdmVkIGxvZ2luXUluZHN0aWxsaW5nZXLSADcA +OAgVAzGkAzEA9QD2ADtaezU5NSwgNDM4fVp7NTk1LCA0Mjl9XXsyMjQuNjY0LCAzMn1XX3dpbmRvd9QA +DgCgAKEAogCjAwQAHwgegBiAmoACgQHQXxAPX2luaXRpYWxLZXlWaWV31AAOAKAAoQCiAKMFPAAfCCSA +GIEBK4ACgQHSXnN0YXR1c0NoZWNrYm941AAOAKAAoQCiAKMBAwAfCCqAGIAbgAKBAdRUdmlld9QADgCg +AKEAogCjA1AAHwgwgBiAsoACgQHWXxAXY2hvb3NlQ2VydGlmaWNhdGVCdXR0b27UAA4AoAChAKIA/QAf +BUkINoAmgAKBAauBAdhdY2hlY2tWZXJzaW9uOtQADgCgAKEAogCjBUAAHwg8gBiBATeAAoEB2l8QEnJl +cXVpcmVLZXlDaGVja2JveNQADgCgAKEAogCjBUQAHwhCgBiBAWGAAoEB3F8QFWRlbGF5ZWRTd2l0Y2hD +aGVja2JveNQADgCgAKEAogD9AB8DUAhIgCaAAoCygQHeXxAXc2hvd0NlcnRpZmljYXRlQ2hvb3NlcjrU +AA4AoAChAKIAowAfAwQBNYAYgAKAmoB01AAOAKAAoQCiAKMEGQhSCFOAGIDWgQHhgQHo1AAOCFUIVghX +CFgIWQhaCFtfEA9fTlNNYW5hZ2VkUHJveHlfEBFOU09iamVjdENsYXNzTmFtZV5OU0RlY2xhcmVkS2V5 +c4EB54EB5YEB5IEB4tIADgA+AEYIXoBsoQhfgQHjXxAUbnVtYmVyT2ZTZWxlY3RlZFJvd3PRAA4IYoEB +5tIANwA4CGQIVaIIVQA70gA3ADgIZghnowhnCGgAO18QEk5TT2JqZWN0Q29udHJvbGxlclxOU0NvbnRy +b2xsZXJXY29udGVudNQADgCgAKEAogCjAwQAHwhugBiAmoACgQHqXV9maXJzdEtleVZpZXfUAA4AoACh +AKIAowAfAtMBNYAYgAKAloB01AAOAKAAoQCiAKMAHwh4CFOAGIACgQHtgQHo1AAOCFUBZQhXCFgIfACz +CH6BAeeBAgUJgQHu0gAOAD4ARgiBgGyvEBYIggiDCIQIhQiGCIcIiAiJCIoIiwiMCI0IjgiPCJAIkQiS +CJMIlAiVCJYIl4EB74EB8IEB8YEB8oEB84EB9IEB9YEB9oEB94EB+IEB+YEB+oEB+4EB/IEB/YEB/oEB +/4ECAIECAYECAoECA4ECBF8QEnByZWZzLmFsbG93Q29udHJvbF8QFXByZWZzLnNoYXJlUGFzdGVib2Fy +ZF8QEHByZWZzLnJlcXVpcmVLZXlfEBNwcmVmcy5kZWxheWVkU3dpdGNoXxAUcHJlZnMuc2hvd1N0YXR1 +c0l0ZW1fEBlwcmVmcy5saW1pdFBhc3RlYm9hcmRTaXplXxAYcHJlZnMubWF4UGFzdGVyYm9hcmRTaXpl +VmFjdGl2ZV8QFnByZWZzLmF1dG9jaGVja1ZlcnNpb25fECJwcmVmcy5yZWplY3RBdXRoZW50aWNhdGlv +blJlcXVlc3RzXxAWcHJlZnMuZW5hYmxlRW5jcnlwdGlvbl8QEnByZWZzLnN3aXRjaEtleVRhZ18QD3By +ZWZzLmNvcHlGaWxlc18QGnByZWZzLnJlcXVpcmVQYXN0ZWJvYXJkS2V5XxAWcHJlZnMucGFzdGVib2Fy +ZEtleVRhZ18QGnByZWZzLnRydXN0UmVxdWVzdEJlaGF2aW9yXxAPcHJlZnMud2FrZU9uTEFOXxARcHJl +ZnMuc3dpdGNoRGVsYXlfEBpsb2NhbEhvc3Quc3VwcG9ydERyYWdORHJvcF8QF3ByZWZzLm1heFBhc3Rl +Ym9hcmRTaXplXxAWcHJlZnMuaGlkZUNvbnRyb2xCZXplbF8QGXByZWZzLnN3aXRjaFdpdGhEb3VibGVU +YXDRAA4IYoEB5tQADgCgAKEAogCjAtIAHwi0gBiAkoACgQIHXxASYWN0aXZhdGlvbkNoZWNrYm941AAO +AKAAoQCiAKMDBAAfCLqAGICagAKBAglabGF5b3V0Vmlld9QADgCgAKEAogCjAwQAHwjAgBiAmoACgQIL +XF9sYXN0S2V5Vmlld9QADgCgAKEAogCjAB8EGQjGgBiAAoDWgQINWmRhdGFTb3VyY2XUAA4AoAChAKIA +owFfAB8IzIAYgE6AAoECD18QEHZlcnNpb25UZXh0RmllbGTUAA4AoAChAKIAowAfBBkBNYAYgAKA1oB0 +1AAOAKAAoQCiAKMEGQAfCNeAGIDWgAKBAhJfEBV0cnVzdGVkSG9zdHNUYWJsZVZpZXfXAA4AoAjaCNsA +oQCiCNwI3Qh4CN8I4AVICOIBSllOU0tleVBhdGhZTlNCaW5kaW5nXxAcTlNOaWJCaW5kaW5nQ29ubmVj +dG9yVmVyc2lvboECF4EB7YECFoECFYEBp4ECFF8QGWVuYWJsZWQ6IHNlbGVjdGlvbi5hY3RpdmVXZW5h +YmxlZF8QEHNlbGVjdGlvbi5hY3RpdmXSADcAOAjnCOijCOgA+wA7XxAVTlNOaWJCaW5kaW5nQ29ubmVj +dG9y1wAOAKAI2gjbAKEAogjcCN0IeAjsCO0FQAjvAUqBAheBAe2BAhuBAhqBATeBAhlfECF2YWx1ZTog +c2VsZWN0aW9uLnByZWZzLnJlcXVpcmVLZXlVdmFsdWVfEBpzZWxlY3Rpb24ucHJlZnMucmVxdWlyZUtl +edcADgCgCNoI2wChAKII3AjdCHgI3wjgAwUI4gFKgQIXgQHtgQIWgQIVgJ+BAhTXAA4AoAjaCNsAoQCi +CNwI3Qh4CN8I4AJhCOIBSoECF4EB7YECFoECFYB2gQIU1wAOAKAI2gjbAKEAogjcCN0IeAkECO0FPgkH +AUqBAheBAe2BAiCBAhqBATOBAh9fECt2YWx1ZTogc2VsZWN0aW9uLnByZWZzLnJlcXVpcmVQYXN0ZWJv +YXJkS2V5XxAkc2VsZWN0aW9uLnByZWZzLnJlcXVpcmVQYXN0ZWJvYXJkS2V51wAOAKAI2gjbAKEAogjc +CN0IeAkNCO0FTQkQAUqBAheBAe2BAiOBAhqBAcCBAiJfECd2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmF1 +dG9jaGVja1ZlcnNpb25fECBzZWxlY3Rpb24ucHJlZnMuYXV0b2NoZWNrVmVyc2lvbtcADgCgCNoI2wCh +AKII3AjdCHgJFgjtBUMJGQFKgQIXgQHtgQImgQIagQFdgQIlXxAgdmFsdWU6IHNlbGVjdGlvbi5wcmVm +cy53YWtlT25MQU5fEBlzZWxlY3Rpb24ucHJlZnMud2FrZU9uTEFO1wAOAKAI2gjbAKEAogjcCN0IeAkf +CO0FRAkiAUqBAheBAe2BAimBAhqBAWGBAihfECR2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmRlbGF5ZWRT +d2l0Y2hfEB1zZWxlY3Rpb24ucHJlZnMuZGVsYXllZFN3aXRjaNcADgCgCNoI2wChAKII3AjdCHgI3wjt +AtIJKwFKgQIXgQHtgQIWgQIagJKBAitfEBd2YWx1ZTogc2VsZWN0aW9uLmFjdGl2ZdcADgCgCNoI2wCh +AKII3AjdCHgJMAjtBUsJMwFKgQIXgQHtgQIugQIagQG0gQItXxAidmFsdWU6IHNlbGVjdGlvbi5wcmVm +cy5zd2l0Y2hEZWxheV8QG3NlbGVjdGlvbi5wcmVmcy5zd2l0Y2hEZWxhedcADgCgCNoI2wChAKII3Ajd +CHgJOQjtBTwJPAFKgQIXgQHtgQIxgQIagQErgQIwXxAldmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5zaG93 +U3RhdHVzSXRlbV8QHnNlbGVjdGlvbi5wcmVmcy5zaG93U3RhdHVzSXRlbdcADgCgCNoI2wChAKII3Ajd +CHgI3wjgA1II4gFKgQIXgQHtgQIWgQIVgLeBAhTXAA4AoAjaCNsAoQCiCNwI3Qh4CUkI4ACkCUwBSoEC +F4EB7YECNYECFYAKgQI0XxApZW5hYmxlZDogc2VsZWN0aW9uLnByZWZzLmVuYWJsZUVuY3J5cHRpb25f +ECBzZWxlY3Rpb24ucHJlZnMuZW5hYmxlRW5jcnlwdGlvbtkADgCgCNoI2wlQAKEAoglRCNwI3Qh4CN8J +VQBwAKQJWAlZAUpfEBNOU1ByZXZpb3VzQ29ubmVjdG9yWU5TT3B0aW9uc4ECF4EB7YECFoECOIECM4AK +gQI3gQI5XxAaZW5hYmxlZDI6IHNlbGVjdGlvbi5hY3RpdmVYZW5hYmxlZDLTAA4G0QA+Bw8JXgljgQGA +pAlfCWAJYQligQI6gQI7gQI8gQI9pAlkCWQJZAlkgQI+gQI+gQI+gQI+XxARTlNOdWxsUGxhY2Vob2xk +ZXJfEBpOU05vdEFwcGxpY2FibGVQbGFjZWhvbGRlcl8QGE5TTm9TZWxlY3Rpb25QbGFjZWhvbGRlcl8Q +G05TTXVsdGlwbGVWYWx1ZXNQbGFjZWhvbGRlchP//////////9cADgCgCNoI2wChAKII3AjdCHgJcAjg +BUYJcwFKgQIXgQHtgQJBgQIVgQFpgQJAXxAoZW5hYmxlZDogc2VsZWN0aW9uLnByZWZzLnNoYXJlUGFz +dGVib2FyZF8QH3NlbGVjdGlvbi5wcmVmcy5zaGFyZVBhc3RlYm9hcmTZAA4AoAjaCNsJUAChAKIJUQjc +CN0IeAjfCVUAcgVGCVgJfgFKgQIXgQHtgQIWgQI4gQI/gQFpgQI3gQJD0wAOBtEAPgcPCYEJhoEBgKQJ +XwlgCWEJYoECOoECO4ECPIECPaQJZAlkCWQJZIECPoECPoECPoECPtkADgCgCNoI2wlQAKEAoglRCNwI +3Qh4CY4JjwBzBUYJkgmTAUqBAheBAe2BAkeBAkaBAkKBAWmBAkWBAkhfEC1lbmFibGVkMzogc2VsZWN0 +aW9uLnByZWZzLmxpbWl0UGFzdGVib2FyZFNpemVYZW5hYmxlZDNfECNzZWxlY3Rpb24ucHJlZnMubGlt +aXRQYXN0ZWJvYXJkU2l6ZdMADgbRAD4HDwmZCZ6BAYCkCV8JYAlhCWKBAjqBAjuBAjyBAj2kCWQJZAlk +CWSBAj6BAj6BAj6BAj7XAA4AoAjaCNsAoQCiCNwI3Qh4CUkI7QNSCakBSoECF4EB7YECNYECGoC3gQJK +XxAndmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5lbmFibGVFbmNyeXB0aW9u1wAOAKAI2gjbAKEAogjcCN0I +eAmuCa8DVAmxAUqBAheBAe2BAk6BAk2Av4ECTF8QMXNlbGVjdGVkVGFnOiBzZWxlY3Rpb24ucHJlZnMu +dHJ1c3RSZXF1ZXN0QmVoYXZpb3Jbc2VsZWN0ZWRUYWdfECRzZWxlY3Rpb24ucHJlZnMudHJ1c3RSZXF1 +ZXN0QmVoYXZpb3LXAA4AoAjaCNsAoQCiCNwI3Qh4CbgJrwVHCbsBSoECF4EB7YECUYECTYEBloECUF8Q +KXNlbGVjdGVkVGFnOiBzZWxlY3Rpb24ucHJlZnMuc3dpdGNoS2V5VGFnXxAcc2VsZWN0aW9uLnByZWZz +LnN3aXRjaEtleVRhZ9cADgCgCNoI2wChAKII3AjdCHgJjgjtBTgJxAFKgQIXgQHtgQJHgQIagQEbgQJT +XxAqdmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5saW1pdFBhc3RlYm9hcmRTaXpl1wAOAKAI2gjbAKEAogjc +CN0IeAnJCa8FQQnMAUqBAheBAe2BAlaBAk2BATuBAlVfEC1zZWxlY3RlZFRhZzogc2VsZWN0aW9uLnBy +ZWZzLnBhc3RlYm9hcmRLZXlUYWdfECBzZWxlY3Rpb24ucHJlZnMucGFzdGVib2FyZEtleVRhZ9cADgCg +CNoI2wChAKII3AjdCHgI7AjgBUcJ1QFKgQIXgQHtgQIbgQIVgQGWgQJYXxAjZW5hYmxlZDogc2VsZWN0 +aW9uLnByZWZzLnJlcXVpcmVLZXnZAA4AoAjaCNsJUAChAKIJUQjcCN0IeAjfCVUAegVHCVgJ3wFKgQIX +gQHtgQIWgQI4gQJXgQGWgQI3gQJa0wAOBtEAPgcPCeIJ54EBgKQJXwlgCWEJYoECOoECO4ECPIECPaQJ +ZAlkCWQJZIECPoECPoECPoECPtcADgCgCNoI2wChAKII3AjdCHgI3wjgBTwI4gFKgQIXgQHtgQIWgQIV +gQErgQIU1wAOAKAI2gjbAKEAogjcCN0IeAjfCOADVAjiAUqBAheBAe2BAhaBAhWAv4ECFNcADgCgCNoI +2wChAKII3AjdCHgJcAjtAmEKAAFKgQIXgQHtgQJBgQIagHaBAl5fECZ2YWx1ZTogc2VsZWN0aW9uLnBy +ZWZzLnNoYXJlUGFzdGVib2FyZNcADgCgCNoI2wChAKII3AjdCHgJHwjgBUsKCAFKgQIXgQHtgQIpgQIV +gQG0gQJgXxAmZW5hYmxlZDogc2VsZWN0aW9uLnByZWZzLmRlbGF5ZWRTd2l0Y2jZAA4AoAjaCNsJUACh +AKIJUQjcCN0IeAjfCVUAfwVLCVgKEgFKgQIXgQHtgQIWgQI4gQJfgQG0gQI3gQJi0wAOBtEAPgcPChUK +GoEBgKQJXwlgCWEJYoECOoECO4ECPIECPaQJZAlkCWQJZIECPoECPoECPoECPtcADgCgCNoI2wChAKII +3AjdCHgI3wjgBUQI4gFKgQIXgQHtgQIWgQIVgQFhgQIU1wAOAKAI2gjbAKEAogjcCN0IeAlJCOADUAlM +AUqBAheBAe2BAjWBAhWAsoECNNkADgCgCNoI2wlQAKEAoglRCNwI3Qh4CN8JVQCCA1AJWAo1AUqBAheB +Ae2BAhaBAjiBAmSAsoECN4ECZtMADgbRAD4HDwo4Cj2BAYCkCV8JYAlhCWKBAjqBAjuBAjyBAj2kCWQJ +ZAlkCWSBAj6BAj6BAj6BAj7XAA4AoAjaCNsAoQCiCNwI3Qh4CN8I4AU9COIBSoECF4EB7YECFoECFYEB +L4ECFNkADgCgCNoI2wlQAKEAoglRCNwI3Qh4CkwJVQCEBT0KUApRAUqBAheBAe2BAmqBAjiBAmeBAS+B +AmmBAmtfEC5lbmFibGVkMjogc2VsZWN0aW9uLmxvY2FsSG9zdC5zdXBwb3J0RHJhZ05Ecm9wXxAkc2Vs +ZWN0aW9uLmxvY2FsSG9zdC5zdXBwb3J0RHJhZ05Ecm9w0wAOBtEAPgcPClYKW4EBgKQJXwlgCWEJYoEC +OoECO4ECPIECPaQJZAlkCWQJZIECPoECPoECPoECPtcADgCgCNoI2wChAKII3AjdCHgI3wjgBU0I4gFK +gQIXgQHtgQIWgQIVgQHAgQIU1wAOAKAI2gjbAKEAogjcCN0IeAjfCOAFQwjiAUqBAheBAe2BAhaBAhWB +AV2BAhTXAA4AoAjaCNsAoQCiCNwI3Qh4CnEI7QVGCnQBSoECF4EB7YECcIECGoEBaYECb18QKHZhbHVl +OiBzZWxlY3Rpb24ucHJlZnMubWF4UGFzdGVib2FyZFNpemVfECFzZWxlY3Rpb24ucHJlZnMubWF4UGFz +dGVib2FyZFNpemXXAA4AoAjaCNsAoQCiCNwI3Qh4CQQI4AVBCn0BSoECF4EB7YECIIECFYEBO4ECcl8Q +LWVuYWJsZWQ6IHNlbGVjdGlvbi5wcmVmcy5yZXF1aXJlUGFzdGVib2FyZEtledkADgCgCNoI2wlQAKEA +oglRCNwI3Qh4CXAJVQCJBUEKhgqHAUqBAheBAe2BAkGBAjiBAnGBATuBAnSBAnVfECllbmFibGVkMjog +c2VsZWN0aW9uLnByZWZzLnNoYXJlUGFzdGVib2FyZNMADgbRAD4HDwqLCpCBAYCkCV8JYAlhCWKBAjqB +AjuBAjyBAj2kCWQJZAlkCWSBAj6BAj6BAj6BAj7ZAA4AoAjaCNsJUAChAKIJUQjcCN0IeAjfCY8AigVB +CpwKnQFKgQIXgQHtgQIWgQJGgQJzgQE7gQJ3gQJ4XxAaZW5hYmxlZDM6IHNlbGVjdGlvbi5hY3RpdmXT +AA4G0QA+Bw8KoQqmgQGApAlfCWAJYQligQI6gQI7gQI8gQI9pAlkCWQJZAlkgQI+gQI+gQI+gQI+2AAO +AKAI2gjbAKEAoglRCNwI3Qh4Cq4I7QVICrEKsgFKgQIXgQHtgQJ7gQIagQGngQJ6gQJ8XxAndmFsdWU6 +IHNlbGVjdGlvbi5wcmVmcy5oaWRlQ29udHJvbEJlemVsXxAgc2VsZWN0aW9uLnByZWZzLmhpZGVDb250 +cm9sQmV6ZWzTAA4G0QA+Bw8Ktwq5gQGAoQq4gQJ9oQq6gQJ+XxAWTlNWYWx1ZVRyYW5zZm9ybWVyTmFt +ZV8QD05TTmVnYXRlQm9vbGVhbtcADgCgCNoI2wChAKII3AjdCHgJcAjgBT4JcwFKgQIXgQHtgQJBgQIV +gQEzgQJA2QAOAKAI2gjbCVAAoQCiCVEI3AjdCHgI3wlVAI0FPglYCswBSoECF4EB7YECFoECOIECf4EB +M4ECN4ECgdMADgbRAD4HDwrPCtSBAYCkCV8JYAlhCWKBAjqBAjuBAjyBAj2kCWQJZAlkCWSBAj6BAj6B +Aj6BAj7XAA4AoAjaCNsAoQCiCNwI3Qh4CtwI7QU9Ct8BSoECF4EB7YEChIECGoEBL4ECg18QIHZhbHVl +OiBzZWxlY3Rpb24ucHJlZnMuY29weUZpbGVzXxAZc2VsZWN0aW9uLnByZWZzLmNvcHlGaWxlc9cADgCg +CNoI2wChAKII3AjdCHgI3wjgBUAI4gFKgQIXgQHtgQIWgQIVgQE3gQIU1wAOAKAI2gjbAKEAogjcCN0I +eAlwCOAFOAlzAUqBAheBAe2BAkGBAhWBARuBAkDZAA4AoAjaCNsJUAChAKIJUQjcCN0IeAjfCVUAkQU4 +CVgK+AFKgQIXgQHtgQIWgQI4gQKGgQEbgQI3gQKI0wAOBtEAPgcPCvsLAIEBgKQJXwlgCWEJYoECOoEC +O4ECPIECPaQJZAlkCWQJZIECPoECPoECPoECPtcADgCgCNoI2wChAKII3AjdCHgI3wjgBUkI4gFKgQIX +gQHtgQIWgQIVgQGrgQIU1wAOAKAI2gjbAKEAogjcCN0IeAsPCO0FRQsSAUqBAheBAe2BAoyBAhqBAWWB +AotfECp2YWx1ZTogc2VsZWN0aW9uLnByZWZzLnN3aXRjaFdpdGhEb3VibGVUYXBfECNzZWxlY3Rpb24u +cHJlZnMuc3dpdGNoV2l0aERvdWJsZVRhcNcADgCgCNoI2wChAKII3AjdCHgI3wjgApAI4gFKgQIXgQHt +gQIWgQIVgIKBAhTXAA4AoAjaCNsAoQCiCNwI3Qh4CN8I4AQZCOIBSoECF4EB7YECFoECFYDWgQIU1wAO +AKAI2gjbAKEAogjcCN0IeAjfCOAFRQjiAUqBAheBAe2BAhaBAhWBAWWBAhTXAA4AoAjaCNsAoQCiCNwI +3Qh4Cy0I7QKQCzABSoECF4EB7YECkoECGoCCgQKRXxAjdmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5hbGxv +d0NvbnRyb2xfEBxzZWxlY3Rpb24ucHJlZnMuYWxsb3dDb250cm9s1AAOAKALNAs1CzYCkAs4CzlYTlNN +YXJrZXJWTlNGaWxlgQKWgIKBApWBApRfEBBOU1Rvb2xUaXBIZWxwS2V5XxA4Q2hlY2sgdG8gYWxsb3cg +b3RoZXIgdGVsZXBvcnQgdXNlcnMgdG8gY29udHJvbCB5b3VyIE1hYy7SADcAOAs9Cz6iCz4AO18QEU5T +SUJIZWxwQ29ubmVjdG9y1AAOAKALNAs1CzYA/wtCCzmBApaAGoECmIEClG8QDwBBAGIAbwB1AHQAIAB0 +AGUAbABlAHAAbwByAHQgJtQADgCgCzQLNQs2AtILSAs5gQKWgJKBApqBApRfEBtDaGVjayB0byBhY3Rp +dmF0ZSB0ZWxlcG9ydC7UAA4AoAs0CzULNgMEC04LOYECloCagQKcgQKUXxBFVGhpcyBpcyB0aGUgbGF5 +b3V0IGFyZWEuIERyYWcgdGhlIGNvbnRyb2xsZWQgaG9zdHMgd2hlcmV2ZXIgeW91IHdhbnQu1AAOAKAL +NAs1CzYFQwtUCzmBApaBAV2BAp6BApRfEJlPbmx5IG9uIGhvc3RzIGNvbm5lY3RlZCB3aXRoIEV0aGVy +bmV0IGFuZCB3aXRoIHRoZSBvcHRpb24gIldha2UgZm9yIEV0aGVybmV0IG5ldHdvcmsgYWRtaW5pc3Ry +YXRvciBhY2Nlc3MiIGNoZWNrZWQgaW4gdGhlIEVuZXJneSBTYXZlciBwcmVmZXJlbmNlcyBwYW5lbC7U +AA4AoAs0CzULNgVFC1oLOYECloEBZYECoIEClF8QT0RvdWJsZS10YXBwaW5nIGlzIHdoZW4geW91IHRh +cCB5b3VyIG1vdXNlIG9uIHRoZSBib3JkZXIsIGdvIGJhY2sgYW5kIHRhcCBhZ2Fpbi7SAA4APgteC1+B +AqSvEH8DBQShB0oFtQRhA1cCaARgCHgF1wNcAWsIUgVEBUIArgS4BXMFTQVIA78FQAWkAV0GawVGAdoB +BgNuAV4HVAP8BVIFPAdxAKQDUgFfAmwEMANVA1MFxgVHAWECLAKQAZwGngOuApcA/wYhBUUDBAgGBTsF +7gNQAkEDGAe9BUEGHwNWBlsCZQapBiIFSwNPB3AFgwL5AmEFPgU9AtgC9QLTBo0EGQVKAg0BYgKvAQwF +BgVjBTgBAwdvB6sF6QZ8C78DkgC4BF8BYAU5A4EEBAFCBG8DLgVJBRYFkwf2B24FTAMtB5oFOgYgB1gD +VAFjB80FQwE0A74C0gCxAfUDrYCfgPOBAZiBATWA9IEBD4B5gO+BAe2BAT2AsIA5gQHhgQFhgQFZgAuA ++oEBJYEBwIEBp4DJgQE3gQExgC+BAV+BAWmAUoAegLSAPoEBmoEBBYEBHYEBK4EBpYAKgLeAToEBGIDa +gNKAu4EBOYEBloBbgGSAgoBAgQFrgMKAhIAagQFQgQFlgJqBAcKBASeBAUCAsoBpgKGBAbKBATuBAUqB +AQuBAVuAd4EBbIEBU4EBtICugQGjgQEpgJiAdoEBM4EBL4CUgKaAloEBZ4DWgQGwgF6AYoCKgI+BAQ2B +ASGBARuAG4EBoYEBrYEBP4EBY4ECooC9gKuA4oBWgQEfgLmBAQGALIDqgQEWgQGrgQERgQEtgQG+gQGf +gQG8gKqBAamBASOBAU2BAZuAv4BngQG2gQFdgCiAx4CSgA2AWIDO0gAOADIAMwvhgASBAqNdTlNBcHBs +aWNhdGlvbtIANwA4C+QCVKICVAA70gAOAD4LXgvngQKkrxB/AvkEYAVHBT4EGQCuAmEEGQAfBUEDTwFd +AB8CZQJlALgEYQU6AmUCZQNUAmUFPQFCBUMCZQFfAP8DUAFCB1gDVQU4AmUHWACuAK4BQgMuA1UArgCu +BUACZQFCAWIBAwFeBUYDVAKQAQMF7gJlAvkFTQJlBdcArgFjAwUFSgJlBe4ArgVCAmwGngXuAmUArgdY +BTsC9QJlAmUCZQLSAtMBAwVFA1UCZQFhAUIAHwKvA1YFOQJlAQwHWAVJBe4FRAAfA1MDLQQZAUICZQNS +A1UBNARfAtMCZQNXBTwFTAdYAmUC0wVIAmUF7gdKAK4BQgVLAmUAHwNUAQMApAFgA1SAmIDvgQGWgQEz +gNaAC4B2gNaAAoEBO4CugC+AAoB3gHeAq4D0gQEjgHeAd4C/gHeBAS+ALIEBXYB3gE6AGoCygCyBAZuA +0oEBG4B3gQGbgAuAC4AsgQEWgNKAC4ALgQE3gHeALIBigBuAPoEBaYC/gIKAG4EBQIB3gJiBAcCAd4EB +PYALgGeAn4EBsIB3gQFAgAuBAVmBARiBAWuBAUCAd4ALgQGbgQEngKaAd4B3gHeAkoCWgBuBAWWA0oB3 +gFuALIACgIqBAQuBAR+Ad4CPgQGbgQGrgQFAgQFhgAKAu4CqgNaALIB3gLeA0oAogOKAloB3gQEPgQEr +gQG8gQGbgHeAloEBp4B3gQFAgQGYgAuALIEBtIB3gAKAv4AbgAqAVoC/0gAOAD4LXgxpgQKkrxCAAwUE +oQdKBbUEYQNXAmgEYAh4BdcDXAFrCFIFRAVCAK4EuAVzBU0FSAO/BUAFpAFdBmsFRgHaAQYDbgFeA/wH +VAU8BVIHcQCkA1IBXwJsBDADVQNTBcYBYQVHAiwCkAGcBp4DrgKXAP8FRQYhAwQIBgU7AkEDUAXuAxgH +vQVBBh8DVgZbAmUFSwapA08GIgdwBYMC+QJhBT4CDQL1AtgC0wU9Bo0FSgFiBBkCrwEMBQYFYwU4AQMH +qwdvBnwF6Qu/A5IAuAFgBF8FOQOBBAQBQgRvAy4FSQUWBZMAHwf2BUwHbgMtB5oFOgYgAWMDVAdYB80F +QwE0A74C0gH1ALEDrYCfgPOBAZiBATWA9IEBD4B5gO+BAe2BAT2AsIA5gQHhgQFhgQFZgAuA+oEBJYEB +wIEBp4DJgQE3gQExgC+BAV+BAWmAUoAegLSAPoEBBYEBmoEBK4EBHYEBpYAKgLeAToEBGIDagNKAu4EB +OYBbgQGWgGSAgoBAgQFrgMKAhIAagQFlgQFQgJqBAcKBASeAaYCygQFAgKGBAbKBATuBAUqBAQuBAVuA +d4EBtIEBbICugQFTgQGjgQEpgJiAdoEBM4BegKaAlICWgQEvgQFngQGwgGKA1oCKgI+BAQ2BASGBARuA +G4EBrYEBoYEBY4EBP4ECooC9gKuAVoDigQEfgLmBAQGALIDqgQEWgQGrgQERgQEtgAKBAb6BAbyBAZ+A +qoEBqYEBI4EBTYBngL+BAZuBAbaBAV2AKIDHgJKAWIANgM7SAA4APgteDOyBAqSvEIAM7QzuDO8M8Azx +DPIM8wz0DPUM9gz3DPgM+Qz6DPsM/Az9DP4M/w0ADQENAg0DDQQNBQ0GDQcNCA0JDQoNCw0MDQ0NDg0P +DRANEQ0SDRMNFA0VDRYNFw0YDRkNGg0bDRwNHQ0eDR8NIA0hDSINIw0kDSUNJg0nDSgNKQ0qDSsNLA0t +DS4NLw0wDTENMg0zDTQNNQTODTcNOA05DToNOw08DT0NPg0/DUANQQ1CDUMNRA1FDUYNRw1IDUkNSg1L +DUwNTQ1ODU8NUA1RDVINUw1UDVUNVg1XDVgNWQ1aDVsNXA1dDV4NXw1gDWENYg1jDWQNZQ1mDWcNaA1p +DWoNaw1sgQKogQKpgQKqgQKrgQKsgQKtgQKugQKvgQKwgQKxgQKygQKzgQK0gQK1gQK2gQK3gQK4gQK5 +gQK6gQK7gQK8gQK9gQK+gQK/gQLAgQLBgQLCgQLDgQLEgQLFgQLGgQLHgQLIgQLJgQLKgQLLgQLMgQLN +gQLOgQLPgQLQgQLRgQLSgQLTgQLUgQLVgQLWgQLXgQLYgQLZgQLagQLbgQLcgQLdgQLegQLfgQLggQLh +gQLigQLjgQLkgQLlgQLmgQLngQLogQLpgQLqgQLrgQLsgQLtgQLugQLvgQLwgPuBAvGBAvKBAvOBAvSB +AvWBAvaBAveBAviBAvmBAvqBAvuBAvyBAv2BAv6BAv+BAwCBAwGBAwKBAwOBAwSBAwWBAwaBAweBAwiB +AwmBAwqBAwuBAwyBAw2BAw6BAw+BAxCBAxGBAxKBAxOBAxSBAxWBAxaBAxeBAxiBAxmBAxqBAxuBAxyB +Ax2BAx6BAx+BAyCBAyGBAyKBAyOBAySBAyWBAyZvEFoAUwB0AGEAdABpAGMAIABUAGUAeAB0ACAAKABB +AHIAcgBhAG4AZwBlAHIAIABkAGUAIABkAGUAbAB0AGUAIABNAGEAYwBzACAAZgByAGEAIAB0AG8AcABw +AGUAbgAsACAAbwBtAGsAcgBpAG4AZwAgAGQAaQBuACAAcwBrAOYAcgBtACAAZgBvAHIAIABhAHQAIABr +AG8AbgB0AHIAbwBsAGwAZQByAGUAIABkAGUAbQAuAClfEBtUZXh0IEZpZWxkIENlbGwgKFRleHQgQ2Vs +bClvEB4AUABvAHAAIABVAHAAIABCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoIxgAIABDAG8AbQBtAGEA +bgBkAClfECdCdXR0b24gQ2VsbCAoS3VuIGh2aXMgdGFzdCBlciB0cnlra2V0OilfEBpUYWJsZSBDb2x1 +bW4gKGNlcnRpZmljYXRlKW8QHQBTAHQAYQB0AGkAYwAgAFQAZQB4AHQAIAAoAEIAZQB0AHIAbwBlAGQA +ZQAgAHYA5gByAHQAZQByAClfECdCdXR0b24gQ2VsbCAoU3lua3JvbmlzZXIgdWRrbGlwc2hvbGRlcilf +EBZUYWJsZSBDb2x1bW4gKGFkZHJlc3MpXxASUHJlZlBhbmVDb250cm9sbGVybxAgAFAAbwBwACAAVQBw +ACAAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKCMYACAAQwBvAG0AbQBhAG4AZAApAC0AMW8QQQBUAGUA +eAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgARgBvAHIAIABhAHQAIABzAGwAZQB0AHQAZQAgAGUA +bgAgAHYA5gByAHQALAAgAG0AYQByAGsAZQByACAAbwBnACAAdAByAHkAawAgAGIAYQBjAGsAcwBwAGEA +YwBlAClfEBFJbWFnZSBDZWxsIChpY29uKV8QEVRydXN0ZWRIb3N0c1RhYmxlXxAiQ2hlY2sgQm94IChT +a2lmdCBtZWQgZm9yc2lua2Vsc2U6KV8QElN0YXRpYyBUZXh0IChLb3J0KV1DdXN0b20gVmlldy0yXxAS +QnV0dG9uIENlbGwgKFZpZXcpXxAoVGV4dCBGaWVsZCBDZWxsIChWZXJzaW9uIGtvbnRyb2xsZXJpbmc6 +KV8QKENoZWNrIEJveCAoS29udHJvbGxlciB2ZXJzaW9uIHZlZCBsb2dpbilvEDUAQwBoAGUAYwBrACAA +QgBvAHgAIAAoAFYAaQBzACAAbQBlAGQAZABlAGwAZQBzAGUAIABuAOUAcgAgAGEAbgBkAHIAZQAgAE0A +YQBjAHMAIABrAG8AbgB0AHIAbwBsAGwAZQByAGUAcwApXxAhQnV0dG9uIENlbGwgKEFjY2VwdGVyIGF1 +dG9tYXRpc2spXxArQ2hlY2sgQm94IChTa2lmdCBrdW4gaHZpcyB0YXN0IGVyIHRyeWtrZXQ6KW8QLABC +AHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAFQAcgDmAGsAIABvAGcAIABzAGwAaQBwACAAZgBpAGwAZQBy +ACAAbQBlAGwAbABlAG0AIABNAGEAYwBzAClfEBFJbWFnZSBWaWV3IChpY29uKW8QKABCAHUAdAB0AG8A +bgAgAEMAZQBsAGwAIAAoAEYAbwByAHMA+ABnACAAYQB0ACAAdgDmAGsAIABzAG8AdgBlAG4AZABlACAA +TQBhAGMAcwApWlRleHQgRmllbGRfEBtUZXh0IEZpZWxkIENlbGwgKDx2ZXJzaW9uPilvEBQAQgB1AHQA +dABvAG4AIABDAGUAbABsACAAKABBAGIAbwB1AHQgJgApbxAgAEIAdQB0AHQAbwBuACAAQwBlAGwAbAAg +ACgAVgDmAGwAZwAgAGMAZQByAHQAaQBmAGkAawBhAHQALgAuAC4AKV8QFlN0YXRpYyBUZXh0ICh0ZWxl +cG9ydClfEBNIb3Jpem9udGFsIFNjcm9sbGVybxAVAE0AZQBuAHUAIABJAHQAZQBtACAAKCMYACAAQwBv +AG0AbQBhAG4AZAApXxAjQ2hlY2sgQm94IChWaXMgc3RhdHVzIGkgbWVudSBiYXJlbilfECFCdXR0b24g +Q2VsbCAoS3VuIGh2aXMgbWluZHJlIGVuZClvEBcATQBlAG4AdQAgAEkAdABlAG0AIAAoIeoAIABDAGEA +cABzACAATABvAGMAawApXxAcUHVzaCBCdXR0b24gKFZpcyBjZXJ0aWZpa2F0KW8QHgBDAGgAZQBjAGsA +IABCAG8AeAAgACgAUwBsAOUAIABrAHIAeQBwAHQAZQByAGkAbgBnACAAdABpAGwAKV8QHlZlcnNpb24g +VGV4dCBGaWVsZCAoPHZlcnNpb24+KVZWaWV3LTFfEBFUYWJsZSBIZWFkZXIgVmlld1tTY3JvbGwgVmll +d18QGFN0YXRpYyBUZXh0IChLcnlwdGVyaW5nKV8QLUJ1dHRvbiBDZWxsIChTa2lmdCBrdW4gaHZpcyB0 +YXN0IGVyIHRyeWtrZXQ6KW8QGwBMAGkAbgBrACAAVABlAHgAdAAgAEYAaQBlAGwAZAAgACgnpAAgAHcA +ZQBiAHMAaQB0AGUAKW8QGABQAG8AcAB1AHAAIABCAHUAdAB0AG8AbgAgACgjGAAgAEMAbwBtAG0AYQBu +AGQAKW8QGgBUAGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgnpAAgAGYAbwByAHUAbQBzAClf +EBlDaGVjayBCb3ggKERlbCBkZW5uZSBNYWMpXxAaVGV4dCBGaWVsZCBDZWxsICh0ZWxlcG9ydClfEA9U +ZXh0IEZpZWxkIENlbGxvEC0AQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABTAHAA+AByAGcAIABtAGkA +ZwAgAG8AbQAgAGUAbgAgAHYA5gByAHQAIABlAHIAIABiAGUAdAByAG8AZQB0AClfEBtCdXR0b24gQ2Vs +bCAoRGVsIGRlbm5lIE1hYylvEBUAQgBlAHYAZQBsACAAQgB1AHQAdABvAG4AIAAoAEEAYgBvAHUAdCAm +AClfECFDaGVjayBCb3ggKFNraWZ0IHZlZCBkdWJiZWx0a2xpaylvEBUATQBlAG4AdQAgAEkAdABlAG0A +IAAoIecAIABTAGgAaQBmAHQAKQAtADFbTGF5b3V0IFZpZXdfECpCdXR0b24gQ2VsbCAoS29udHJvbGxl +ciB2ZXJzaW9uIHZlZCBsb2dpbilfEBRTdGF0aWMgVGV4dCAoU2tpZnQ6KW8QGQBUAGUAeAB0ACAARgBp +AGUAbABkACAAQwBlAGwAbAAgACgnpAAgAGQAbwBuAGUAcgApbxAgAFAAdQBzAGgAIABCAHUAdAB0AG8A +bgAgACgAVgDmAGwAZwAgAGMAZQByAHQAaQBmAGkAawBhAHQALgAuAC4AKV8QE01lbnUgKE90aGVyVmll +d3MpLTFvEF4AVABlAHgAdAAgAEYAaQBlAGwAZAAgAEMAZQBsAGwAIAAoAEEAcgByAGEAbgBnAGUAcgAg +AGQAZQAgAGQAZQBsAHQAZQAgAE0AYQBjAHMAIABmAHIAYQAgAHQAbwBwAHAAZQBuACwAIABvAG0AawBy +AGkAbgBnACAAZABpAG4AIABzAGsA5gByAG0AIABmAG8AcgAgAGEAdAAgAGsAbwBuAHQAcgBvAGwAbABl +AHIAZQAgAGQAZQBtAC4AKV8QGVRleHQgRmllbGQgQ2VsbCAoU3RhdHVzOilvEBoAUABvAHAAdQBwACAA +QgB1AHQAdABvAG4AIAAoIxgAIABDAG8AbQBtAGEAbgBkACkALQAxbxAWAE0AZQBuAHUAIABJAHQAZQBt +ACAAKCMlACAATwBwAHQAaQBvAG4AKQAtADFvECwAUwB0AGEAdABpAGMAIABUAGUAeAB0ACAAKABGAG8A +cgBlAHMAcAD4AHIAZwBzAGwAZQByACAAbwBtACAAawBvAG4AdAByAG8AbABsAGUAcgBpAG4AZwApXxAW +VGV4dCBGaWVsZCBDZWxsIChLb3J0KV1DdXN0b20gVmlldy0xXxARSG9yaXpvbnRhbCBTbGlkZXJfEBBO +dW1iZXIgRm9ybWF0dGVybxA9AFMAdABhAHQAaQBjACAAVABlAHgAdAAgACgARgBvAHIAIABhAHQAIABz +AGwAZQB0AHQAZQAgAGUAbgAgAHYA5gByAHQALAAgAG0AYQByAGsAZQByACAAbwBnACAAdAByAHkAawAg +AGIAYQBjAGsAcwBwAGEAYwBlAClvEBkATQBlAG4AdQAgAEkAdABlAG0AIAAoIeoAIABDAGEAcABzACAA +TABvAGMAawApAC0AMW8QEwBNAGUAbgB1ACAASQB0AGUAbQAgACgh5wAgAFMAaABpAGYAdAApXxAYVGV4 +dCBGaWVsZCBDZWxsIChTa2lmdDopXxAlQ2hlY2sgQm94IChTeW5rcm9uaXNlciB1ZGtsaXBzaG9sZGVy +KV8QJUNoZWNrIEJveCAoS3VuIGh2aXMgdGFzdCBlciB0cnlra2V0OilvEBsAVABlAHgAdAAgAEYAaQBl +AGwAZAAgAEMAZQBsAGwAIAAoJ6QAIAB3AGUAYgBzAGkAdABlAClfEBZUYWIgVmlldyBJdGVtIChMYXlv +dXQpXxAeQnV0dG9uIENlbGwgKEFrdGl2ZXIgdGVsZXBvcnQpXxA9VG9wIFRhYiBWaWV3IChMYXlvdXQs +IFNpa2tlcmhlZHNpbmRzdGlsbGluZ2VyLCBJbmRzdGlsbGluZ2VyKW8QKgBDAGgAZQBjAGsAIABCAG8A +eAAgACgAVAByAOYAawAgAG8AZwAgAHMAbABpAHAAIABmAGkAbABlAHIAIABtAGUAbABsAGUAbQAgAE0A +YQBjAHMAKV8QI0J1dHRvbiBDZWxsIChTa2lmdCB2ZWQgZHViYmVsdGtsaWspXxAVU3RhdGljIFRleHQg +KFN0YXR1czopbxAaAEwAaQBuAGsAIABUAGUAeAB0ACAARgBpAGUAbABkACAAKCekACAAZgBvAHIAdQBt +AHMAKV8QJ1RhYmxlIFZpZXcgKE5hdm4sIEFkZHJlc3NlLCBDZXJ0aWZpa2F0KVhQcmVmUGFuZVxDb250 +ZW50IFZpZXdvEDAAVABlAHgAdAAgAEYAaQBlAGwAZAAgAEMAZQBsAGwAIAAoAEYAbwByAGUAcwBwAPgA +cgBnAHMAbABlAHIAIABvAG0AIABrAG8AbgB0AHIAbwBsAGwAZQByAGkAbgBnAClvEB4AVABlAHgAdAAg +AEYAaQBlAGwAZAAgAEMAZQBsAGwAIAAoAE8AdgBlAHIAZgD4AHIAcwBsAGUAcgA6AClfEB9DaGVjayBC +b3ggKEt1biBodmlzIG1pbmRyZSBlbmQpW0N1c3RvbSBWaWV3XxAXQnV0dG9uIENlbGwgKENoZWNrIE5v +dylvEBUATQBlAG4AdQAgAEkAdABlAG0AIAAoIwMAIABDAG8AbgB0AHIAbwBsAClfECRCdXR0b24gQ2Vs +bCAoU2tpZnQgbWVkIGZvcnNpbmtlbHNlOilvEBcATQBlAG4AdQAgAEkAdABlAG0AIAAoIxgAIABDAG8A +bQBtAGEAbgBkACkALQAxW0FwcGxpY2F0aW9uXxAcVGV4dCBGaWVsZCBDZWxsIChLcnlwdGVyaW5nKVZW +aWV3LTJvEHEAUwB0AGEAdABpAGMAIABUAGUAeAB0ACAAKACpACAAMgAwADAAMwAtADIAMAAwADgAIABh +AGIAeQBzAHMAbwBmAHQACgBCAGEAcwBlAHIAZQB0ACAAcADlACAAZQBuACAAaQBkAGUAIABhAGYAIABE +AGEAdgBlACAAUwBhAGcAIABvAGcAIABKAGUAcgBlAG0AeQAgAFEAdQBpAG4AbgAuAAoAVABhAGsAIAB0 +AGkAbAAgAE0AYQBuAHUAMAAyACwAIABKAHUAbABpAGUAbgAgAG8AZwAgAFAAYQB1AGwALgApXxATVGFi +bGUgQ29sdW1uIChuYW1lKW8QGgBTAHQAYQB0AGkAYwAgAFQAZQB4AHQAIAAoAE8AdgBlAHIAZgD4AHIA +cwBsAGUAcgA6AClvECAAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABTAGwA5QAgAGsAcgB5AHAAdABl +AHIAaQBuAGcAIAB0AGkAbAApXxARVmVydGljYWwgU2Nyb2xsZXJeQ29udGVudCBWaWV3LTFfEB1UZXh0 +IEZpZWxkIENlbGwgKFRleHQgQ2VsbCktMV8QHVRhYiBWaWV3IEl0ZW0gKEluZHN0aWxsaW5nZXIpXxAX +UHVzaCBCdXR0b24gKENoZWNrIE5vdylvECEAVABlAHgAdAAgAEYAaQBlAGwAZAAgAEMAZQBsAGwAIAAo +AEIAZQB0AHIAbwBlAGQAZQAgAHYA5gByAHQAZQByAClfECVCdXR0b24gQ2VsbCAoVmlzIHN0YXR1cyBp +IG1lbnUgYmFyZW4pXEZpbGUncyBPd25lcl8QFlRleHQgRmllbGQgQ2VsbCAoTGFuZylfEBJTdGF0aWMg +VGV4dCAoTGFuZylvEBQATQBlAG4AdQAgAEkAdABlAG0AIAAoIyUAIABPAHAAdABpAG8AbgApXxAnVGFi +IFZpZXcgSXRlbSAoU2lra2VyaGVkc2luZHN0aWxsaW5nZXIpbxA3AEIAdQB0AHQAbwBuACAAQwBlAGwA +bAAgACgAVgBpAHMAIABtAGUAZABkAGUAbABlAHMAZQAgAG4A5QByACAAYQBuAGQAcgBlACAATQBhAGMA +cwAgAGsAbwBuAHQAcgBvAGwAbABlAHIAZQBzAClfECRTdGF0aWMgVGV4dCAoVmVyc2lvbiBrb250cm9s +bGVyaW5nOilvEBcATQBlAG4AdQAgAEkAdABlAG0AIAAoIwMAIABDAG8AbgB0AHIAbwBsACkALQAxbxAZ +AEwAaQBuAGsAIABUAGUAeAB0ACAARgBpAGUAbABkACAAKCekACAAZABvAG4AZQByAClWTWF0cml4XxAR +TWVudSAoT3RoZXJWaWV3cylbU2xpZGVyIENlbGxvECYAQwBoAGUAYwBrACAAQgBvAHgAIAAoAEYAbwBy +AHMA+ABnACAAYQB0ACAAdgDmAGsAIABzAG8AdgBlAG4AZABlACAATQBhAGMAcwApVUFib3V0XxA1QnV0 +dG9uIENlbGwgKEFmdmlzIGh2aXMgaWtrZSBkZW4gYWxsZXJlZGUgZXIgYmV0cm9ldClfEBxDaGVjayBC +b3ggKEFrdGl2ZXIgdGVsZXBvcnQpbxB1AFQAZQB4AHQAIABGAGkAZQBsAGQAIABDAGUAbABsACAAKACp +ACAAMgAwADAAMwAtADIAMAAwADgAIABhAGIAeQBzAHMAbwBmAHQACgBCAGEAcwBlAHIAZQB0ACAAcADl +ACAAZQBuACAAaQBkAGUAIABhAGYAIABEAGEAdgBlACAAUwBhAGcAIABvAGcAIABKAGUAcgBlAG0AeQAg +AFEAdQBpAG4AbgAuAAoAVABhAGsAIAB0AGkAbAAgAE0AYQBuAHUAMAAyACwAIABKAHUAbABpAGUAbgAg +AG8AZwAgAFAAYQB1AGwALgApXxAcQnV0dG9uIENlbGwgKFZpcyBjZXJ0aWZpa2F0KV8QJ1Byb3RvdHlw +ZSBQcm90b3R5cGUgQnV0dG9uIENlbGwgKFJhZGlvKdIADgA+C14N7oECpKUBYgQZAV8BYwFhgGKA1oBO +gGeAW9IADgA+C14N9oECpKUCEQQ4Ad0CEQIRgFyA14BPgFyAXNIADgA+C14N/oECpK8Q1wMFAFAHSgW1 +AGIAlANcAWsIUgCPAFkAjAVCAK4EuAB/BUgAhACDAV0GawBaAQYATAFeA/wAewU8B3EAiQFfAmwAdQBw +BcYASwFhAGwAggaeA64AagKXCAYFOwBnAGUDUACXAGQAmAYfA1YGWwJlBqkGIgNPBYMAnQJhBT4FPQLY +AvUC0waNAIsAegFiAE4AVgKvAIYBDAUGB6sAiAEDAJsDkgCAALgAeQOBAHgAYQCKAGAEbwVJBZMAHwBt +AFIASAduBUwHmgBKBToGIAdYA1QBYwBzAGgDvgCeAtIB9QCNBKEAnACVBGEDVwBTAmgEYAh4BdcAkAVE +AFEFcwVNA78AhwCFAJEFQACBBaQAWwVGAdoAfgNuAHwAfQdUAE8FUgCkA1IAdwB2BDAAcQNVAG8DUwBu +BUcAawIsApABnAD/BiEFRQMEBe4CQQMYAFUHvQCWBUEAkwVLAJIHcABJAvkAjgQZAg0ATQVKAFwFYwU4 +B28AVwXpBnwAXgu/AGMEXwFgBTkEBAFCAHQAcgBUAy4FFgCZB/YDLQBYAGkAXwfNBUMBNABmAJoAsQBd +A62An4EBz4EBmIEBNYECEIECioCwgDmBAeGBAoKBAeCBAnmBAVmAC4D6gQJfgQGngQJngQJlgC+BAV+B +AemAHoB/gD6BAQWBAlmBASuBAaWBAnGAToEBGIECSYECM4EBOYB1gFuBAiqBAmSBAWuAwoECJICEgQHC +gQEngQIdgQIYgLKBAo+BAhOBApCBAUqBAQuBAVuAd4EBbIEBU4CugQEpgQKdgHaBATOBAS+AlICmgJaB +AWeBAnaBAleAYoCHgQHbgIqBAmyAj4EBDYEBrYECboAbgQKZgL2BAmGAq4ECVIC5gQJSgQIOgQJzgQIM +gOqBAauBAS2AAoECLIEB04AJgQGfgQG8gQGpgCeBASOBAU2BAZuAv4BngQJCgQIegMeBAp+AkoBYgQJ/ +gPOBApuBAo2A9IEBD4EB1YB5gO+BAe2BAT2BAoWBAWGBAdGBASWBAcCAyYECbYECaIEChoEBN4ECY4EB +MYEB64EBaYBSgQJdgLSBAluBAlyBAZqAiYEBHYAKgLeBAk+BAkuA2oECNoDSgQIygLuBAi+BAZaBAieA +ZICCgECAGoEBUIEBZYCagQFAgGmAoYEB2YEBsoECjoEBO4ECiYEBtIECh4EBo4AZgJiBAoCA1oBegIGB +AbCBAeyBASGBARuBAaGBAd2BAT+BAWOBAgiBAqKBAhGA4oBWgQEfgQEBgCyBAkSBAj+BAdeBARaBARGB +ApOBAb6AqoEB34ECIYECCoEBtoEBXYAogQIcgQKXgA2BAgaAztIADgA+C14O2IECpK8Q1w7ZDtoO2w7c +Dt0O3g7fDuAO4Q7iDuMO5A7lDuYO5w7oDukO6g7rDuwO7Q7uDu8O8A7xDvIO8w70DvUO9g73DvgO+Q76 +DvsO/A79Dv4O/w8ADwEPAg8DDwQPBQ8GDwcPCA8JDwoPCw8MDw0PDg8PDxAPEQ8SDxMPFA8VDxYPFw8Y +DxkPGg8bDxwPHQ8eDx8PIA8hDyIPIw8kDyUPJg8nDygPKQ8qDysPLA8tDy4PLw8wDzEPMg8zDzQPNQ82 +DzcPOA85DzoPOw88Dz0PPg8/D0APQQ9CD0MPRA9FD0YPRw9ID0kPSg9LD0wPTQ9OD08PUA9RD1IPUw9U +D1UPVg9XD1gPWQ9aD1sPXA9dD14PXw9gD2EPYg9jD2QPZQ9mD2cPaA9pD2oPaw9sD20Pbg9vD3APcQ9y +D3MPdA91D3YPdw94D3kPeg97D3wPfQ9+D38PgA+BD4IPgw+ED4UPhg+HD4gPiQ+KD4sPjA+ND44Pjw+Q +D5EPkg+TD5QPlQ+WD5cPmA+ZD5oPmw+cD50Png+fD6APoQ+iD6MPpA+lD6YPpw+oD6kPqg+rD6wPrQ+u +D6+BAyuBAyyBAy2BAy6BAy+BAzCBAzGBAzKBAzOBAzSBAzWBAzaBAzeBAziBAzmBAzqBAzuBAzyBAz2B +Az6BAz+BA0CBA0GBA0KBA0OBA0SBA0WBA0aBA0eBA0iBA0mBA0qBA0uBA0yBA02BA06BA0+BA1CBA1GB +A1KBA1OBA1SBA1WBA1aBA1eBA1iBA1mBA1qBA1uBA1yBA12BA16BA1+BA2CBA2GBA2KBA2OBA2SBA2WB +A2aBA2eBA2iBA2mBA2qBA2uBA2yBA22BA26BA2+BA3CBA3GBA3KBA3OBA3SBA3WBA3aBA3eBA3iBA3mB +A3qBA3uBA3yBA32BA36BA3+BA4CBA4GBA4KBA4OBA4SBA4WBA4aBA4eBA4iBA4mBA4qBA4uBA4yBA42B +A46BA4+BA5CBA5GBA5KBA5OBA5SBA5WBA5aBA5eBA5iBA5mBA5qBA5uBA5yBA52BA56BA5+BA6CBA6GB +A6KBA6OBA6SBA6WBA6aBA6eBA6iBA6mBA6qBA6uBA6yBA62BA66BA6+BA7CBA7GBA7KBA7OBA7SBA7WB +A7aBA7eBA7iBA7mBA7qBA7uBA7yBA72BA76BA7+BA8CBA8GBA8KBA8OBA8SBA8WBA8aBA8eBA8iBA8mB +A8qBA8uBA8yBA82BA86BA8+BA9CBA9GBA9KBA9OBA9SBA9WBA9aBA9eBA9iBA9mBA9qBA9uBA9yBA92B +A96BA9+BA+CBA+GBA+KBA+OBA+SBA+WBA+aBA+eBA+iBA+mBA+qBA+uBA+yBA+2BA+6BA++BA/CBA/GB +A/KBA/OBA/SBA/WBA/aBA/eBA/iBA/mBA/qBA/uBA/yBA/2BA/6BA/+BBACBBAEQmBDpEgABiCUSAAGI +OxEBAxIABJUREgABiFkSAAGHRBEBlhEBlREBlxIABJUMEQHAEgAElRsRAWkRAcMSAASVBxEBkxIABJUG +EKQSAAGIWxDoEgABh0wQqhClEgADDjYRAQgRAYoRAaYQphC+EQGAEQGCEgABh2IQzREBIxEBVhIABJUF +EgABh+YRAbURAb4SAAGHKBIAAYgBEQFuEQFYEQFCEgAElPcSAASVEhIABJUNEQFAEQGeEQG4EgABiGAS +AASVHBEBThEBnxEBuRIAAYgOEgAElSIQvxEBmxEBkhIAAYdOELsQuhIABJUPEQGoEQGPEgAElPsRARAQ +DBEBZBIAAYhYEgABh/ARAccQyRIABJUgEgAElQoRAcQRAWYRAakSAAGIHREBSRC3EQGnEgABh5gRAVAS +AAGHqBIABJUdEQHCEMoSAASU+REBiREBwRIABJUIEKsRAW8RAaARAYYRAbIRASURAVoRAaURAbYSAASV +IxCuEgABh0gRAaMSAAGHmRIABJUhEQFXEQFoEQFqEgAElPoSAAGHXxD5EQEpEgABiDwRAVwRAQ8RAQoS +AAGIDxEBYREBtxEBvREBxREBShDCEQFdEgABiDIRAUYSAAGHRhEBQRIABJT4EQFeEQHGEQGIEBoSAAGH +5REBgREBfREBkREBuhIABJTWEQGDEPYRAX8SAASVCREBRBEBhREBQxIAAYfEEIgSAAGHRRCsEQGiEgAE +lQ4QiREBnRIAAYfFEgABhzgQzhIAAYgQEQGvEQGcEQFgEQG/EQFZEQGLEK0QvREBpBD3EgABh8MQnBEB +cBEBKhIAAYgLEQFFEQGHEgAElPwRAaESAAGHrxCRE//////////9EQEEEPgQqBEBaxIAAYeWEKIRAVsR +AUsRAVEQvBIAAYgKEgAElR4SAAGIYREBZRC4EQFjEOoSAAGIXxEBuxChEQGwEgAElR8SAAGIIRCvEgAB +iFLSAA4APgBGEISAbKDSAA4APgteEIeBAqSg0gAOAD4LXhCKgQKkoNIANwA4EIwQjaIQjQA7Xk5TSUJP +YmplY3REYXRhAAgAGQAiACcAMQA6AD8ARABSAFQAZgh2CHwIxwjOCNUI4wj1CREJHwkrCTcJRQlQCV4J +egmICZsJrQnHCdEJ3gngCeMJ5gnpCewJ7gnxCfMJ9gn5CfwJ/woBCgMKBgoJCg4KEQoaCiYKKAoqCj0K +RgpPCloKXwpuCncKigqTCp4KoAqhCqoKsQq+CsQKzQrPC4ALgguEC4YLiAuKC4wLjguQC5MLlguZC5wL +nwuiC6ULqAurC64LsQu0C7cLugu9C8ALwwvGC8kLzAvPC9IL1QvYC9sL3gvhC+QL5wvqC+0L8AvzC/YL ++Qv8C/8MAgwFDAgMCwwODBEMFAwXDBoMHQwgDCMMJgwpDCwMLwwyDDUMOAw7DD4MQQxEDEcMSgxNDFAM +UwxWDFkMXAxfDGIMZQxoDGsMbgxxDHQMdwx6DH0MjgycDKUMrQyvDLEMswy1DNIM5AzsDPMM/A0GDRIN +FA0WDRgNGg0dDR4NIA09DUgNVA1WDVgNWg1dDWANYg1kDX4Nrw27DdEN5g31DgcOEg4cDioOPA5JDlcO +XA5eDmAOYg5kDmYOaA5qDmwObg5zDnwOiw6cDqMOqg6zDrUOvg7ADsMO0A7ZDt4O5Q7mDu8O+Q77DwQP +Cw8dDyYPLw84D0UPUg9bD2YPbw95D4APjA+kD60PtA/LD9oP6w/tD+8P8Q/zEBQQHRAfECEQIxAlECgQ +KRArEC0QThBQEFIQVBBXEFoQXBBeEGAQeRCuELwQvhDAEMIQxBDGEMgQyhDMENEQ3hDrEPoQ/BD+EQAR +CBETERwRIRE0ET0RPxFREVoRYRF5EYoRjBGOEZARkhHDEdAR3RHrEfUSAxIQEhoSLBJAEkoSVhJYEloS +XBJeEmMSZRJnEmkSaxJtEm8SihKcEqUSpxKsEskSyxLNEs8S0hLUEtYS2BLhEuMS8hL0EvYS+BL6EvwS +/hMAEykTNBNAE0ITRBNGE0cTSRNLE0wTThNQE1kTWxNqE2wTbhNwE3ITdBN2E3gTlBOwE9YT7hQiFEMU +YBR6FJsUoxSrFLMUvhTDFMUUxxTJFM4UzxTcFN4U4BTiFOcU8BT3FQMVDBUXFSMVRBVGFUgVShVMFU0V +TxVRFWoVixWfFasVrRWvFbEVsxW1FboVvBXFFdYV2BXhFeMV5RX5Fg4WFhYjFi8WPRY/FkEWQxZFFkcW +ThZbFmgWcBZyFnQWgBaJFo4WoxalFqcWqRarFr4WyxbNFtAW2RbiFvQW/RcIFxQXPRdTF1UXVxdZF1sX +XRdeF2AXYhdkF3kXkhezF7UXtxe5F7sXvRe/F8kX2hfcF+UX5xfqF/MX+BgHGCgYKhgsGC4YMBgxGDMY +NRhNGG4YcBhyGHQYdhh4GH0YfxlIGVkZWxlkGWYZaRmSGZQZlhmYGZoZnBmfGaAZohmkGaYZuBnOGfca +CxohGiYaKBoqGiwaLhowGjUaNho4GjoaTRpeGmAaYhqBGqoarBquGrAashq0GrUatxq5Grsa0hr3Gvka ++xr9Gv8bARsDGwUbFhtKG3MbdRt3G3kbext9G34bgBuCG4QbmxvAG8IbxBvGG8gbyhvMG84b3RyNHJYc +nRysHLQcvxzIHM8c6BzzHRAdGR0eHTEdOh1LHU0dTx1RHVMdcB1yHXQddh14HXkdex2YHZsdnR2gHaMd +pR2oHcId9x4KHhMeFR4XHhkeGx4dHh8eIR4mHkIeSx5XHlkeWx5kHm0ech6IHqIesx61HrceuR67Hsce +2B7aHtwe3h7gHwEfAx8FHwcfCR8KHwwfDh8oH10fYh9kH2YfaB9qH2wfbh9wH34flR+mH6gfqh+sH64f +xx/YH9of3B/eH+EgEiAUIBYgGSAbICAgIiAkICcgKSAsIC4gSSBhIGogbCCJIIsgjSCPIJIglCCXIKAg +oiClIKcgsCCyILsgvSC/IMEgwyDkIOYg6CDqIOwg7SDvIPEhCiE/IUEhQyFFIUchSSFLIU0hYCGVIaQh +riHHId8h4SHkIeYh6CHqIewh7iHvIfEh8iH0If0h/yICIgQiISIjIiUiJyIpIisiLSI2IjgiPSI/IkEi +YiJuInAiciJ0InYieCJ6InwilSKiIqsitCLBIuIi5CLmIugi6iLtIu4i8CLyIwsjLCMuIzAjMiM0IzYj +OyM9I9gj8SQJJBIkFCQbJB0kHyQiJDskSCRSJFQkViRYJFokXCReJGUkbCR1JHokiCSdJJ8koSSjJKUk +qCS9JL8kwSTEJMYkzyTRJNQk1iTfJOEk9CT2JPgk+iT8JP4lACUCJQUlCCUlJSclKSUrJS0lMCUxJTMl +TSVuJXAlciV0JXYleCV9JX8l4CX9Jf8mASYDJgUmDiYPJhEmKyZcJl4mYCZiJmQmZiZoJo8mmCaaJrcm +uSa7Jr0mvybAJsIm3CcRJxMnFScXJxknGycdJx8nRidjJ2UnZydpJ2snbCduJ4cnqCeqJ6wnriewJ7In +tye5J8QoDygbKCUoNChAKFgoYyhtKIIokCiYKJoonCieKKAooiikKKYoqCiqKKworSivKLEotii4KNEo +2ijcKOMo5SjnKOkpHikgKSIpJCkmKSgpKiksKW0pdil4KXopiCmRKZMpzCnSKdQp1inYKdop3CneKeAq +CipDKkUqRypJKksqTSpPKlEqUypWKmwqdip9KqYqqCqqKqwqriq0KsEqwyrGKs8q2irjKxwrKCsxKz4r +USteK2oreCuGK4griyuOK5ErkyuVK5crqiusK68rsSuzK7wrvivJK8srzivRK9Mr1Sv+LAgsEiwcLB4s +ISwjLCUsJywqLCwsLiwwLDIsOyw9LEAsQiyZLLssyCzdLPctEy0uLTotWS1oLXQtdi14LX0tfy2BLYMt +hC2GLY8tmC2aLZstnS2fLaEtoy2sLbgtxC3PLegt6i3sLe4t8C3yLhsuHS4gLiMuJi4oLiouLC4uLjgu +QS5KLl4ucy51LncueS57LpIumy6kLrIuuy69LsQuxi7ILsou8y8CLw8vFy8iLzEvPC9HL0gvSi9ML04v +Vy9ZL2Ivay9tL3Ivjy+UL5YvmC+aL5wvni+jL7Avsi++L9Mv1S/XL9kv2y/tL/YwATAVMDYwOzA9MD8w +QTBDMEUwSjBMMFYwazBtMG8wcTBzMIwwlTCaMKgw0TDSMNQw1jDYMOEw4zDsMO4w9jETMRUxFzEZMRsx +HTEmMUcxSTFLMU0xTzFRMVMxdDF2MXgxejGDMYUxjjGQMZwxuTG7Mb0xvzHBMcMxzjHjMeUx5zHpMesx +9zIoMioyLDIuMjAyMjI0MjYyOzJEMlkyWzJdMl8yYTJrMngyejJ/MpcyoDKpMrQy1TLeMucy8TLzMvUy ++DL7Mv4zADMJMyUzMjM7M0YzUTN2M3gzejN9M4AzgzOFM44zqjOzM7UzuDO6M9Az6zP0M/00CjQnNCk0 +KzQuNDE0MjQ0NEw0bTRvNHE0dDR2NHk0ezS6NNc02TTbNN404TTiNOQ0/DUdNR81ITUkNSY1KTUrNUw1 +VzVxNYo1jDWPNZI1lDWWNZk1oTW2Nbg1ujW9NcA1yTXLNc410DXZNds2CjYNNhA2EzYWNhk2HDYfNiE2 +JDYnNio2LTYwNjM2NjY5Njw2PzZCNkU2SDZLNmg2ajZsNm82cjZzNnU2jzbENsY2yDbKNsw2zzbRNtQ2 +6jcHNwk3CzcONxE3EjcUNyw3TTdPN1E3VDdWN1k3Wzd0N5E3kzeVN5g3mzecN543tTfWN9g32jfdN983 +4jfkN/04GjgcOB44ITgkOCU4JzhAOGE4YzhlOGg4ajhtOG84djiTOJU4lziaOJ04njigOLk47jjwOPI4 +9Dj2OPk4+zj+ORg5NTk3OTk5PDk/OUA5QjlcOZE5kzmVOZc5mTmcOZ45oTngOf05/zoBOgQ6BzoIOgo6 +JDpZOls6XTpfOmE6ZDpmOmk6hTqiOqQ6pjqpOqw6rTqvOsk6/jsAOwI7BDsGOwk7CzsOOzA7TTtPO1I7 +VTtYO1k7Wzt1O8A73TvvO/o8DDwhPC88Njw/PEA8QjxFPEg8SjxNPFA8UTxSPFU8WDxdPGY8aDyZPKE8 +tTzAPM482DzlPO088DzzPPY8+zz9PQI9BT0IPQs9Dj0QPR09KT0sPS89Mj1FPVI9VD1WPVk9az14PXo9 +fD1/PZI9pj2vPbQ9vT2/Pco90z3VPeA94z3mPek97D3vPhw+Hz4iPiU+Jz4qPi0+MD4zPjU+Rj5zPnY+ +eT58Pn4+gT6EPoc+ij6MPp8+zD7PPtI+1T7XPto+3T7gPuM+5T70PyE/JD8nPyo/LD8vPzI/NT84P08/ +WD9dP2Y/cz+HP5Y/nz+sP7o/1z/ZP9s/3j/hP+I/5D/9QB5AIEAiQCVAJ0AqQCxAMUBOQFBAUkBVQFhA +WUBbQHVAqkCsQK5AsECyQLVAt0C6QPFBDkEQQRJBFUEYQRlBG0E1QWpBbEFuQXBBckF1QXdBekGTQbBB +skG0QbdBukG7Qb1B10IMQg5CEEISQhRCF0IZQhxCNEJRQlNCVUJYQltCXEJeQndCoEKsQrVCt0K6QrxC +v0LCQsdCyELLQs5DFUMcQydDLkM6Q0JDVUNcQ2pDfkOQQ6RDt0PDQ8pD10PpQ+xD70PyQ/VD90P6Q/1E +AEQDRAZEB0QKRAxED0QSRBNEFEQhRClELERFREhES0RORFFEVERXRFpEXURgRGNEZkRpRIJEhUSIRItE +jkSRRJREl0SaRJ1EoESjRKZErkS9RNdE+UUMRSBFJ0U0RTxFVEVjRXdFlEWfRatFukXERdBF3EXfReBF +80X0Rf1GAkYfRiJGL0Y8Rj9GQkZFRkdGVEZXRlhGWUZiRmdGdEZ9RoJGl0akRqdGqEapRqxGr0a4RsZG +yUbLRtRG2UbiRv9HAkcDRxZHF0cZRyJHKUc/R0hHS0dOR1tHXkdhR2RHaEdxR3hHjEeRR6ZHqEeqR61H +r0fFR9pH3EfeR+FH40ftSApIDEgPSBJIFUgWSBhIMkh9SH5IgEiDSIZIiEiLSI5Ij0iQSJNInEieSM9I +0kjVSNhI2kjdSOBI40jmSPNI9kj5SPxJBUkHSRBJEkkdSSBJI0kmSSlJLElZSVxJX0liSWRJZ0lqSW1J +cEmdSaBJo0mmSahJq0muSbFJtEnhSeRJ50nqSexJ70nySfVJ+EolSihKK0ouSjBKM0o2SjlKPEpZSltK +XUpgSmNKZEpmSn9KtEq2SrhKukq8Sr9KwUrESxlLNks4SzpLPUtAS0FLQ0tbS4xLjkuQS5NLlkuYS5tL +pUuuS7BLzUvPS9FL1EvXS9hL2kvyTBNMFUwXTBpMHEwfTCFMKUxGTEhMS0xOTFFMUkxUTG1MpkyuTMRM +2UzkTO9M+k0ITSVNLk0xTTRNN006TUNNTE1RTVJNW01cTWVNZ014TXpNg02GTZBNmU2iTa9NuE3DTcxN +6U3rTe1N8E3zTfRN9k4PTjBOMk40TjdOOU48Tj5OQ05gTmJOZE5nTmpOa05tToZOu069Tr9OwU7DTsZO +yE7LTupO+E8BTwpPFU8gTy5PNk9HT0lPS09NT1BPYk9zT3VPeE96T31PjE+dT59PoU+jT6ZPq0+8T75P +wE/CT8VP30/wT/JP9E/3T/pQCFAZUBtQHlAgUCNQOFBJUEtQTlBQUFNQa1B8UH5QgFCCUIVQn1CwULJQ +tFC2ULhQyVDLUM1Q0FDTUORQ9lEKURlRHFEfUSJRJVEuUTBRM1E2UU1RUlFVUV5RY1FsUXNRiFGVUZ1R +rlGwUbJRtFG3UcVR1lHYUdpR3FHeUe9R8VHzUfZR+VIKUg1SEFIRUhRSHVIfUk5SUVJUUldSWlJdUmBS +Y1JmUmlSbFJvUnJSdVJ4UntSflKBUoRSh1KKUo1SkFKlUr1S0FLmUv1TGVM0UztTVFN5U5JTp1O5U9ZT +71QMVB5UMlRPVGlUglSeVKNUplS3VLlUu1S9VMBU1VTmVOhU6lTsVO9U+lULVQ1VD1URVRRVIVUyVTRV +NlU4VTtVRlVXVVlVW1VdVWBVc1WEVYZViFWKVYxVnVWfVaFVo1WmVb5V21XlVe9WDlYRVhRWF1YaVh1W +IFY8VkRWV1ZgVmdWf1acVp9WolalVqhWq1auVtJW2Fb1VxJXFVcYVxtXHlcgVyNXQFdDV0ZXSVdMV05X +UVduV3FXdFd3V3pXfVeAV65X1VfyV/VX+Ff7V/5YAVgEWC5YUVhuWHFYdFh3WHpYfViAWKNYv1jcWN9Y +4ljlWOhY61juWRVZNVlSWVVZWFlbWV5ZYFljWX1ZmlmdWaBZo1mmWalZrFnRWe9aDFoPWhJaFVoYWhta +HlpGWmdahFqHWopajVqQWpJalVqyWrVauFq7Wr5awFrDWu9bEls3W01bV1taW11bYFtjW2ZbaFtrW25b +i1uUW6FbpFutW7Bbs1u2W7lbwlvFW8hby1vOW+Jb/1waXDhcQVxeXGFcZFxnXGpcbVxwXJtcvVziXOVc +6FzrXO5c8Vz0XPdc+l0HXQpdE10WXRldHF0fXShdK10uXTFdNF1ZXVxdX11iXWVdaF1rXW5dcV2hXapd +0F3dXeBd6V3sXe9d8l31Xf5eAV4EXgdeCl4nXipeLV4wXjNeNV44XmJef16CXoVeiF6LXo1ekF7EXtBe +918UXxdfGl8dXyBfI18mX1JfcV+OX5FflF+XX5pfnV+gX81f6l/tX/Bf81/2X/lf/GAsYE9gbGBvYHJg +dWB4YHtgfmCkYMlgzGDPYNJg1WDYYNtg3mDhYO5g8WD6YP1hAGEDYQZhD2ESYRVhGGEbYThhO2E+YUFh +RGFHYUphZ2FqYW1hcGFzYXVheGGVYZhhm2GeYaFho2GmYc9h7GHvYfJh9WH4Yfth/mInYkxiT2JSYlVi +WGJbYl5iYWJkYnFidGJ9YoBig2KGYolikmKVYphim2KeYrtivmLBYsRix2LKYs1i6mLtYvBi82L2Yvhi ++2MgYyNjJmMpYyxjL2MxYzRjN2NEY0djUGNTY1ZjWWNcY2VjaGNrY25jcWOOY5FjlGOXY5pjnWOgY8Vj +yGPLY85j0WPUY9dj2mPdZA5kNWRCZEVkTmRRZFRkV2RaZGNkZmRpZGxkb2SMZI9kkmSVZJhkm2SeZLtk +vmTBZMRkx2TKZM1k6mTtZPBk82T2ZPlk/GUnZUtlaGVrZW5lcWV0ZXdlemWqZc9l0mXVZdhl22XeZeFl +5GXnZhNmIGYjZixmL2YyZjVmOGZBZkRmR2ZKZk1mcmZ1Znhme2Z+ZoFmhGaHZopmp2a0ZrdmwGbDZsZm +yWbMZtVm2GbbZt5m4WcCZwVnCGcLZw5nEWcUZxdnQWdkZ3FndGd3Z3pnfWeAZ5lnq2fIZ8tnzmfRZ9Rn +12faZ/9oAmgFaAhoC2gOaBFoFGgXaCRoJ2gwaDNoNmg5aDxoRWhIaEtoTmhRaG5ocWh0aHdoemh9aIBo +o2i/aNxo32jiaOVo6GjraO5pC2kOaRFpFGkXaRppHWlCaUVpSGlLaU5pUWlUaVdpWmlnaWppc2l2aXlp +fGl/aYhpi2mOaZFplGmxabRpt2m6ab1pwGnDaeBp42nmaelp7GnvafJqH2pFamJqZWpoamtqbmpwanNq +kGqTapZqmWqcap5qoWq+asFqxGrHaspqzWrQau1q8GrzavZq+Wr7av5rJGtDa1RrXWtka2draWtsa29r +gmu9a8Zry2vfa/Br82v1a/hr+2wcbC1sMGwybDVsOGxWbGdsamxsbG9scmy6bMtszmzRbNRs121zbYRt +h22KbY1tkG3ibett7m7vbvFu8272bvlu+27+bwBvAm8FbwhvCm8Mbw9vEm8VbxdvGW8cbx9vIm8kbydv +Km8sby9vMm80bzZvOG86bz1vQG9Db0ZvSW9Lb01vT29Sb1RvVm9Yb1tvXm9gb2JvZG9mb2lva29tb29v +cm91b3dvem99b4Bvgm+Eb4ZviW+Mb49vkm+Vb5dvmm+db6Bvom+lb6hvqm+sb69vsm+0b7ZvuG+7b71v +wG/Cb8Rvxm/Ib8tvzm/Rb9Nv1m/Zb9xv32/ib+Rv5m/ob+pv7W/vb/Jv9G/2b/lv/G//cAJwBXAIcAtw +DXAQcBNwFnAZcBtwHXAgcCNwJXAncClwK3AtcC9wOHA6cD1wS3BUcFlwYnBlcWZxaHFqcW1xcHFycXRx +dnF4cXpxfXF/cYFxg3GFcYdxiXGLcY5xkHGScZRxlnGZcZtxnnGgcaJxpHGmcahxq3GtcbBxsnG1cbdx +uXG7cb5xwHHCccRxx3HJcctxzXHPcdFx1HHWcdhx2nHdcd9x4XHkceZx6XHrce1x73HycfRx93H5cfxx +/3ICcgVyB3IJcgxyD3IRchNyFXIXchlyG3IdciByInIkciZyKHIqcixyL3IycjRyNnI5cjxyP3JCckRy +RnJIckpyTHJOclByUnJUclZyWHJacl1yYHJjcmZyaHJqcm1yb3JycnVyd3J5cnxyfnKAcoJyhHKGcohy +inKTcpZzmXObc51zoHOjc6VzqHOqc6xzr3Oyc7RztnO5c7xzv3PBc8NzxnPJc8xzznPRc9Rz1nPZc9xz +3nPgc+Jz5HPnc+pz7XPwc/Nz9XP3c/lz/HP+dAB0AnQFdAd0CnQMdA50EHQTdBV0F3QZdBx0H3QhdCR0 +J3QpdCt0LnQwdDN0NnQ5dDx0P3RBdER0R3RJdEx0T3RSdFR0VnRZdFt0XXRfdGF0ZHRndGp0bHRudHB0 +cnR1dHh0e3R9dIB0g3SGdIl0jHSOdJB0knSUdJd0mXScdJ50oHSjdKZ0qXSsdK50sXS0dLd0uXS8dL90 +wnTEdMZ0yXTMdM900XTTdNV013TZdNt05HTndep17XXwdfN19nX5dfx1/3YCdgV2CHYLdg52EXYUdhd2 +GnYddiB2I3Ymdil2LHYvdjJ2NXY4djt2PnZBdkR2R3ZKdk12UHZTdlZ2WXZcdl92YnZldmh2a3ZudnF2 +dHZ3dnp2fXaAdoN2hnaJdox2j3aSdpV2mHabdp52oXakdqd2qnatdrB2s3a2drl2vHa/dsJ2xXbHdsp2 +zXbQdtN21nbZdtx233biduV26Hbrdu528Xb0dvd2+nb9dwB3A3cGdwl3DHcPdxJ3FXcYdxt3HnchdyR3 +J3cqdy13MHczdzZ3OXc8dz93QndFd0h3S3dOd1F3VHdXd1p3XXdgd2N3ZndpeCB4Pnh9eKd4xHkBeSt5 +RHlZeZx6IXo1ekl6bnqDepF6pnrRevx7aXuNe7t8FnwqfH18iHymfNF9FH0tfUN9cH2Wfbp9634Kfkl+ +an5xfoV+kX6sftx/FX9If39/m3+4f8qAJ4BFgHKAloDDgM+A/IETgUiBi4GhgmCCfIKzguKDPYNWg2SD +eIOLhAiEPYRmhIGEqYTRhQqFI4VEhYSF24YBhhmGUIZ6hoOGkIbzhzKHVIdgh3qHp4fOh/+IC4gqiDGJ +FoksiWOJpom6icmJ6YoJiiOKaIqQip2KtorLivaLIIuRi7iL6YwejCWMOYxFjJSMmozSjPGN3o39jieO +MI4zjj6OQI5CjkSORo5IjlGOVI5fjmGOY45ljmeOaY5yjnWQJpAokCuQLpAxkDSQN5A5kDuQPpBBkESQ +R5BKkEyQTpBRkFSQV5BakFyQX5BikGSQZpBokGuQbpBxkHSQd5B5kHyQf5CCkIWQh5CJkIyQj5CSkJSQ +l5CZkJyQn5CikKWQp5CqkK2QsJCzkLaQuZC7kL6QwZDDkMaQyZDLkM6Q0ZDTkNWQ15DakN2Q4JDikOSQ +55DpkOyQ7pDxkPSQ95D5kPyQ/pEBkQORBpEIkQuRDpERkRSRFpEZkRyRHpEhkSSRJpEpkSyRL5ExkTSR +N5E6kTyRPpFBkUSRRpFJkUuRTZFQkVKRVZFYkVqRXZFgkWKRZJFnkWqRbZFwkXORdpF5kXuRfpGBkYSR +h5GKkY2RkJGTkZWRmJGakZ2RoJGjkaWRqJGqkayRr5GykbSRt5G5kbyRvpHBkcSRx5HJkcuRzZHPkdKR +1ZHXkdqR3JHekeGR5JHnkeqR7ZHwkfOR9pH4kfqR/ZH/kgGSA5IGkgmSDJIPkhKSFZIYkhuSHpIhkiSS +JpIokiuSLpIwkjOSNpI5kjySP5JCkkWSR5JKkk2SUJJTklaSWJJbkl6SYJJjkmWSbpJxlCKUJZQolCuU +LpQxlDSUN5Q6lD2UQJRDlEaUSZRMlE+UUpRVlFiUW5RelGGUZJRnlGqUbZRwlHOUdpR5lHyUf5SClIWU +iJSLlI6UkZSUlJeUmpSdlKCUo5SmlKmUrJSvlLKUtZS4lLuUvpTBlMSUx5TKlM2U0JTTlNaU2ZTclN+U +4pTllOiU65TulPGU9JT3lPqU/ZUAlQOVBpUJlQyVD5USlRWVGJUblR6VIZUklSeVKpUtlTCVM5U2lTmV +PJU/lUKVRZVIlUuVTpVRlVSVV5ValV2VYJVjlWaVaZVslW+VcpV1lXiVe5V+lYGVhJWHlYqVjZWQlZOV +lpWZlZyVn5WilaWVqJWrla6VsZW0lbeVupW9lcCVw5XGlcmVzJXPldKV1ZXYlduV3pXhleSV55Xqle2V +8JXzlfaV+ZX8lf+WApYFlgiWC5YOlhGWFJYXlhqWHZYgliOWJpYpliyWL5YyljWWOJY7lj6WQZZElkeW +SpZNllCWU5ZWllmWXJZflmKWZZZolmuWbpZxlnSWd5Z6ln2WgJaDloaWiZaMlo+WkpaVlpiWm5aelqGW +pJanlqmWq5awlrWWuJa9lsKWx5bKls2W0JbVltiW3ZbgluOW6JbrlvCW8pb3lvmW/pcAlwKXB5cKlw2X +EJcSlxSXF5calx+XIZcklyeXLJcxlzSXN5c8l0GXRJdHl0qXT5dUl1mXXJdfl2KXZ5dsl2+Xcpd1l3qX +f5eBl4SXh5eMl46XkJeVl5iXm5egl6OXpZeol62Xspe1l7eXvJfBl8SXx5fKl8+X0pfUl9eX3Jffl+SX +6Zfsl+6X85f2l/mX/pgAmAOYBpgJmAyYD5gSmBWYGJgdmB+YJJgnmCyYMZg0mDeYOpg/mESYRphJmE6Y +UZhUmFeYXJhfmGKYZZhomGuYbZhwmHWYeJh9mICYhZiImIuYjpiQmJWYmJibmJ6YoZimmKmYq5iumLOY +tpi5mLyYwZjDmMiYypjNmNKY1JjXmNyY4ZjjmOiY65jumPGY9Jj3mPqY/Jj+mQGZA5kImQqZDZkQmRWZ +GJkbmSCZI5komSqZM5k2mTiZOpk9mUKZRJlHmUqZTZlPmVSZWZlemWGZY5lmmWiZbZlwmXKZdZl6mX+Z +gZmGmY+ZkZmSmZuZnpmfmaiZq5msmbWZugAAAAAAAAICAAAAAAAAEI4AAAAAAAAAAAAAAAAAAJnJA + + + diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings new file mode 100644 index 0000000..932b7c6 Binary files /dev/null and b/English.lproj/InfoPlist.strings differ diff --git a/English.lproj/Localizable.strings b/English.lproj/Localizable.strings new file mode 100644 index 0000000..a072c17 Binary files /dev/null and b/English.lproj/Localizable.strings differ diff --git a/French.lproj/InfoPlist.strings b/French.lproj/InfoPlist.strings new file mode 100644 index 0000000..3756f14 Binary files /dev/null and b/French.lproj/InfoPlist.strings differ diff --git a/French.lproj/Localizable.strings b/French.lproj/Localizable.strings new file mode 100644 index 0000000..03633ef --- /dev/null +++ b/French.lproj/Localizable.strings @@ -0,0 +1,216 @@ +/* No comment provided by engineer. */ +"%.1fG" = "%.1fGo"; + +/* No comment provided by engineer. */ +"%.1fK" = "%.1fKo"; + +/* No comment provided by engineer. */ +"%.1fM" = "%.1fMo"; + +/* No comment provided by engineer. */ +"< 1K" = "< 1Ko"; + +/* Title of the dialog that appears when dragging to a file that already exists in the Finder */ +"A file already exists with this name." = "Un fichier du même nom existe déjà."; + +/* Explanation text for the dialog that appears when dragging a file to a location where a file with the same name already exists. */ +"A file named \\U201C%@\\U201D already exists in \\U201C%@\\U201D. You can choose to use a unique name for the new file, replace the old file, or cancel the drop." = "Un fichier nommé “%1$@” existe déjà dans “%2$@”. Vous pouvez utiliser un nom unique pour le nouveau fichier, remplacer l'ancien fichier ou annuler la copie."; + +/* No comment provided by engineer. */ +"A new version of teleport is available!" = "Une nouvelle version de teleport est disponible !"; + +/* No comment provided by engineer. */ +"Accept" = "Accepter"; + +/* No comment provided by engineer. */ +"Accept and Reject Others" = "Accepter et rejeter les autres"; + +/* Trust request dialog */ +"Asking host \\U201C%@\\U201D for trust" = "Demande d'authentification à l'hôte “%@”"; + +/* Text in dialog about accessibility */ +"Assistive Device" = "Périphériques d'aide"; + +/* No comment provided by engineer. */ +"Betatester!" = "Bétatesteur !"; + +/* No comment provided by engineer. */ +"Build %@" = "Build %@"; + +/* Generic cancel button in dialog */ +"Cancel" = "Annuler"; + +/* Title in certificate selection panel */ +"Choose certificate" = "Choisir le certificat"; + +/* No comment provided by engineer. */ +"Configure\\U2026" = "Configurer…"; + +/* Title for connection failure */ +"Connection to \\U201C%@\\U201D failed." = "La connexion à “%@” a échoué."; + +/* No comment provided by engineer. */ +"controlled by %@" = "contrôlé par %@"; + +/* No comment provided by engineer. */ +"controlling %@" = "contrôle %@"; + +/* Button in dialog */ +"Deactivate teleport" = "Désactiver teleport"; + +/* No comment provided by engineer. */ +"Delete pairing" = "Retirer"; + +/* Button title */ +"Disable Encryption" = "Désactiver le chiffrement"; + +/* Text in dialog about accessibility */ +"Disabled access for assistive devices" = "Accés pour les périphériques d'aide désactivé"; + +/* No comment provided by engineer. */ +"Don't show again" = "Ne plus afficher"; + +/* No comment provided by engineer. */ +"Drag & Drop files" = "Glisser-déposer des fichiers"; + +/* Button title */ +"Enable" = "Activer"; + +/* Text in dialog for certificate missing error */ +"Encryption requires a certificate" = "Le chiffrement a besoin d'un certificat"; + +/* No comment provided by engineer. */ +"File not sent" = "Fichier non transmis"; + +/* No comment provided by engineer. */ +"File sent (%@)" = "Fichier transmis (%@)"; + +/* Title for control failure */ +"Host \\U201C%@\\U201D rejected control." = "L'hôte “%@” a rejeté le contrôle."; + +/* Reason for trust failure */ +"Host not accepting any more trusted hosts." = "L'hôte n'accepte plus d'autres authentifications."; + +/* Reason for control failure */ +"Host not controllable." = "L'hôte n'est pas controlable."; + +/* Reason for control failure */ +"Host not trusted." = "L'hôte n'est pas authentifié."; + +/* Explanation in dialog for certificate missing error */ +"In order to activate encryption, teleport needs a valid certificate, but couldn't find one in your Keychain.\nYou'll need to create a certificate and re-activate encryption. Note that the certificate algorithm and key size must match between the server and the clients.\n\nWould you like to launch Certificate Assistant to create a new self-signed certificate?\nRecommended settings: RSA 1024 bits, all others to default values." = "Pour activer le chiffrement, teleport a besoin d'un certificat valide, mais ne peut en trouver un dans votre trousseau d'accés.\nVous allez devoir créer un certificat et ré-activer le chiffrement. L'algorithme et la taille des clés du certificate du client et du serveur doivent correspondre.\n\nVoulez-vous lancer l'assistant de certificats pour créer un certificat auto-signé?\nRéglages recommandés: RSA 2048 bits, toutes les autres valeurs étant celles par défaut."; + +/* Button title */ +"Launch Assistant" = "Lancer l'assistant"; + +/* No comment provided by engineer. */ +"No" = "Non"; + +/* No comment provided by engineer. */ +"No shared Mac found on local network. Please check \\U201CShare this Mac\\U201D on the Macs you want to control." = "Aucun Mac partagé n'a été trouvé sur le réseau local. Veuillez cocher “Partager ce Mac” sur les Macs que vous souhaitez contrôler."; + +/* No comment provided by engineer. */ +"OK" = "OK"; + +/* No comment provided by engineer. */ +"Pasteboard not sent" = "Presse-papier non transmis"; + +/* No comment provided by engineer. */ +"Pasteboard sent (%@)" = "Presse-papier transmis (%@)"; + +/* Button title */ +"Re-enable" = "Réactiver"; + +/* No comment provided by engineer. */ +"Reject" = "Rejeter"; + +/* Replace button to replace an existing file. */ +"Replace" = "Remplacer"; + +/* No comment provided by engineer. */ +"Screen %d - %.0fx%.0f" = "Écran %1$d - %2$.0fx%3$.0f"; + +/* No comment provided by engineer. */ +"Screen to share" = "Écran à partager"; + +/* Informative text in certificate selection panel */ +"Select the certificate to be used for encryption. It must have the same encryption algorithm as the one used on the other Macs." = "Sélectionner le certificat à utiliser pour le chiffrement. Il doit utiliser le même algorithme de chiffrement que ceux utilisés sur vos autres Macs."; + +/* No comment provided by engineer. */ +"Share this Mac" = "Partager ce Mac"; + +/* No comment provided by engineer. */ +"Synchronize pasteboard" = "Synchroniser le presse-papier"; + +/* No comment provided by engineer. */ +"teleport %@ is now available.\n%@\n\nWould you like to download the new version now?" = "teleport %1$@ est maintenant disponible.\n%2$@\n\nVoulez-vous télécharger la nouvelle version maintenant?"; + +/* No comment provided by engineer. */ +"teleport is up-to-date." = "teleport est à jour."; + +/* Explanation in dialog for accessibility */ +"The access for assistive devices has been disabled although teleport needs this to operate.\n\nWould you like to re-enable it now or deactivate teleport?" = "L'accés pour les périphériques d'aide a été désactivé mais teleport a besoin de cette option pour fonctionner.\n\nVoulez-vous la réactiver ou désactiver teleport ?"; + +/* No comment provided by engineer. */ +"The computer you tried to control rejected your request. You should try on a computer you own." = "Le Mac que vous voulez contrôler a rejeté votre demande. Vous pouvez contrôler seulement les ordinateurs que vous possédez."; + +/* Title for trust failure */ +"The host \\U201C%@\\U201D rejected your trust request." = "L'hôte “%@” a rejeté votre demande d'authentification."; + +/* No comment provided by engineer. */ +"The host has probably removed this Mac from its trusted hosts list. Do you want to ask for trust again?" = "L'hôte a probablement retiré ce Mac de la liste des hôtes de confiance. Voulez-vous l'ajouter à nouveau ?"; + +/* Invalid protocol error message */ +"The Mac you tried to control is using teleport protocol v%d, but you're using v%d." = "Le Mac que vous essayez de contrôler utilise le protocol v%1$d, alors que vous utilisez la version v%2$d."; + +/* No comment provided by engineer. */ +"The server may be down, or an encryption problem may have occured. If encryption is enabled, please check that the certificate algorithms match." = "Le serveur est peut-être hors ligne, un problème de chiffrement ou un problème de connexion est survenu. Si vous utilisez le chiffrement, veuillez vérifier que les algorithmes des deux certificats correspondent."; + +/* No comment provided by engineer. */ +"This will allow the demanding host to take control of your mouse and keyword. All keystrokes and transfers will be encrypted with this host.\nIf you didn't request the control, you should Reject it." = "Ceci autorisera l'hôte en question à prendre le contrôle de votre souris et clavier. Toutes les frappes et tous les transfers avec cet hôte seront chiffrés.\nSi vous n'avez pas demandé d'authentification, vous devriez rejeter cette requête."; + +/* No comment provided by engineer. */ +"This will allow the demanding host to take control of your mouse and keyword. Warning: the keystrokes and transfers will be sent clear on your network. You should enable encryption on both Macs to encrypt these.\nIf you didn't request the control, you should Reject it." = "Ceci autorisera l'hôte en question à prendre le contrôle de votre souris et clavier. Attention: les frappes et les transfers avec cet hôte seront transmis en clair sur le réseau. Pour plus de sûreté, vous devriez activer le chiffrement sur les deux Macs.\nSi vous n'avez pas demandé d'authentification, vous devriez rejeter cette requête."; + +/* teleport trust request title - certified version */ +"Trust request from certified host \\U201C%@\\U201D." = "Demande d'authentifcation de l'hôte certifié “%@”."; + +/* teleport trust request title - uncertified version */ +"Trust request from uncertified host \\U201C%@\\U201D." = "Demande d'authentifcation de l'hôte non certifié “%@”."; + +/* Unique button in dialog, to make a unique name of an existing filename */ +"Unique" = "Unique"; + +/* Address of unknown trusted host */ +"unknown address" = "adresse inconnue"; + +/* Name of unknown trusted host */ +"unknown name" = "nom inconnu"; + +/* Name for unamed server */ +"unnamed" = "sans nom"; + +/* Reason for trust failure */ +"User rejected your trust request." = "L'utilisateur a rejeté votre demande d'authentification."; + +/* Invalid protocol error title */ +"Wrong protocol version" = "Mauvaise version du protocole."; + +/* No comment provided by engineer. */ +"Yes" = "Oui"; + +/* No comment provided by engineer. */ +"You have the most recent version of teleport." = "Vous avez la dernière version de teleport."; + +/* Explanation in dialog for accessibility */ +"You must enable access for assistive devices to activate teleport. You may need administrator privileges to do this.\n\nWould you like to enable this now?" = "Vous devez activer l'accés pour les périphériques d'aide pour utiliser teleport. Des priviléges administrateurs seront éventuellement requis.\n\nVoulez-vous l'activer maintenant ?"; + +/* Trust request message */ +"You should now grant the trust on your other Mac." = "Vous devez maintenant accepter la demande sur l'autre Mac."; + +/* No comment provided by engineer. */ +"Your build (%d) is newer than the latest available (%d), so you must be a betatester. Congratulations!" = "Votre build (%1$d) est plus récente que la dernière disponible (%2$d), donc vous êtes surement un beta-testeur. Félicitations !"; + +/* No comment provided by engineer. */ +"zero" = "zéro"; + diff --git a/French.lproj/MainMenuApp.nib/classes.nib b/French.lproj/MainMenuApp.nib/classes.nib new file mode 100644 index 0000000..8eab51a --- /dev/null +++ b/French.lproj/MainMenuApp.nib/classes.nib @@ -0,0 +1,38 @@ + + + + + IBClasses + + + CLASS + TPPrefPaneAppController + LANGUAGE + ObjC + OUTLETS + + window + NSWindow + + SUPERCLASS + NSObject + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/French.lproj/MainMenuApp.nib/info.nib b/French.lproj/MainMenuApp.nib/info.nib new file mode 100644 index 0000000..21ddea8 --- /dev/null +++ b/French.lproj/MainMenuApp.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 629 + IBLastKnownRelativeProjectPath + ../teleport.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 265 + + IBSystem Version + 9A569 + targetFramework + IBCocoaFramework + + diff --git a/French.lproj/MainMenuApp.nib/keyedobjects.nib b/French.lproj/MainMenuApp.nib/keyedobjects.nib new file mode 100644 index 0000000..0d5ee09 Binary files /dev/null and b/French.lproj/MainMenuApp.nib/keyedobjects.nib differ diff --git a/French.lproj/teleportPref.xib b/French.lproj/teleportPref.xib new file mode 100644 index 0000000..fbf3427 --- /dev/null +++ b/French.lproj/teleportPref.xib @@ -0,0 +1,5919 @@ + + + + 1050 + 9C31 + 629 + 949.26 + 352.00 + + YES + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + TPPreferencePane + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{517, 450}, {595, 429}} + 1081606144 + PDwgZG8gbm90IGxvY2FsaXplID4+A + NSWindow + + View + + + + 256 + + YES + + + 274 + + YES + + + 268 + {{149, 387}, {122, 18}} + + + YES + + 604110336 + 0 + Partager ce Mac + + LucidaGrande + 1.300000e+01 + 1044 + + + 1211912703 + 2 + + NSSwitch + + + + 200 + 25 + + + + + 265 + {{545, 379}, {32, 32}} + + + YES + + 67239424 + 134217728 + QWJvdXTigKY + + + 138690815 + 130 + + NSImage + icon-about + + + + + + 200 + 25 + + + + + 268 + {{18, 387}, {119, 18}} + + + YES + + 67239424 + 0 + Activer teleport + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 274 + + YES + + + 256 + + YES + + + 274 + {{20, 17}, {511, 296}} + + + TPLayoutView + NSView + + + + 290 + {{17, 1}, {427, 14}} + + + YES + + 67239424 + 4325376 + R2xpc3Nlci1kw6lwb3NlciBsZXMgaMO0dGVzIGRpc3BvbmlibGVzIGF1dG91ciBkZSB2b3RyZSDDqWNy +YW4gcG91ciBsZXMgY29udHLDtGxlci4 + + + 1.100000e+01 + 3100 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2OQA + + + + 6 + + controlTextColor + + 3 + MAA + + + + + + {{10, 33}, {549, 319}} + + + + + {{13, 10}, {569, 365}} + + + + YES + + layout + + Disposition + + + + + + + 256 + + YES + + + 301 + + YES + + + 292 + {{182, 101}, {282, 14}} + + YES + + 67239424 + 272760832 + UG91ciByZXRpcmVyIHVuIGjDtHRlLCBzw6lsZWN0aW9ubmV6IGxlIGV0IGZhaXRlcyByZXRvdXIuA + + + + + + + + + -2147483380 + {{179, 250}, {172, 32}} + + YES + + 67239424 + 134217728 + Q2hvaXNpciBsZSBjZXJ0aWZpY2F04oCmA + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{179, 250}, {164, 32}} + + YES + + 67239424 + 134217728 + Afficher le certificat + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{183, 286}, {158, 18}} + + YES + + 67239424 + 0 + Activer le chiffrement + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{23, 287}, {148, 17}} + + YES + + 67239424 + 71303168 + Chiffrement : + + + + + + + + + 292 + {{185, 23}, {299, 58}} + + YES + 3 + 1 + + YES + + -2080244224 + 0 + TWUgZGVtYW5kZXIgc2kgbCdow7R0ZSBwZXV0IMOqdHJlIGF1dG9yaXPDqQ + + + 1211912703 + 0 + + NSRadioButton + + + + + + 200 + 25 + + + 67239424 + 0 + UmVqZXRlciBzaSBsJ2jDtHRlIG4nZXN0IHBhcyBkZSBjb25maWFuY2U + + 1 + + 1211912703 + 0 + + + + 200 + 25 + + + 67239424 + 0 + Accepter automatiquement + + 2 + + 1211912703 + 0 + + + + 400 + 75 + + + {299, 18} + {4, 2} + 1143480320 + NSActionCell + + 67239424 + 0 + Radio + + 1211912703 + 0 + + 400 + 75 + + + + + 3 + MQA + + + + + + 274 + + YES + + + 2304 + + YES + + + 256 + {333, 104} + + YES + + + 256 + {333, 17} + + + + + + 256 + {{-26, 0}, {16, 17}} + + + + YES + + name + 1.490000e+02 + 4.000000e+01 + 1.000000e+03 + + 75628032 + 0 + Nom + + + 3 + MC4zMzMzMzI5OQA + + + 6 + + headerTextColor + + + + + 337772096 + 133120 + Text Cell + + + + 6 + + controlBackgroundColor + + + + + 1 + YES + + + + address + 1.160000e+02 + 8.000000e+00 + 1.000000e+03 + + 75628032 + 0 + Adresse + + + + + + 337772096 + 133120 + + + + + + + 1 + YES + + + + certificate + 5.900000e+01 + 5.857568e+01 + 1.000000e+03 + + 67239424 + 0 + Certificat + + + 6 + + headerColor + + + + + + 67239424 + 134348800 + Afficher + + + -2034876161 + 36 + + + 400 + 75 + + + + + 3.000000e+00 + 2.000000e+00 + + + 6 + + gridColor + + 3 + MC41AA + + + 1.700000e+01 + 448823296 + 1 + 15 + 0 + YES + + + {{1, 17}, {333, 104}} + + + + + 4 + + + + 256 + {{-100, -100}, {15, 180}} + + + _doScroller: + 9.222222e-01 + + + + 256 + {{-100, -100}, {463, 15}} + + 1 + + + 9.904762e-01 + + + + 2304 + + YES + + + {{1, 0}, {333, 17}} + + + + + 4 + + + + {{185, 116}, {335, 122}} + + + 2 + + + + + + QSAAAEEgAABBmAAAQZgAAA + + + + 292 + {{14, 64}, {157, 17}} + + YES + + 67239424 + 71303168 + RGVtYW5kZSBkZSBjb250csO0bGUgOg + + + + + + + + + 268 + {{8, 221}, {163, 17}} + + YES + + 67239424 + 71303168 + SMO0dGVzIGRlIGNvbmZpYW5jZSA6A + + + + + + + + {549, 319} + + + + + {{10, 33}, {549, 319}} + + UsOpZ2xhZ2VzIGRlIHPDqWN1cml0w6k + + + + + options + + + 256 + + YES + + + 301 + + YES + + + 268 + {{211, 132}, {171, 18}} + + YES + + 67239424 + 0 + U2V1bGVtZW50IHNpIGluZsOpcmlldXIgw6A + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{23, 181}, {148, 17}} + + YES + + 67239424 + 71303168 + Transferts : + + + + + + + + + 268 + {{11, 17}, {160, 17}} + + YES + + 67239424 + 71303168 + TWlzZXMgw6Agam91ciA6A + + + + + + + + + 268 + {{43, 287}, {128, 17}} + + YES + + 67239424 + 71303168 + Commuter : + + + + + + + + + 268 + {{183, 74}, {288, 18}} + + YES + + 67239424 + 0 + Afficher le status dans la barre des menus + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 180}, {294, 18}} + + YES + + -2080244224 + 0 + R2xpc3Nlci1kw6lwb3NlciBkZXMgZmljaGllcnMgZW50cmUgbGVzIE1hY3M + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{211, 108}, {128, 18}} + + YES + + 67239424 + 0 + Seulement avec : + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 156}, {211, 18}} + + YES + + -2080244224 + 0 + Synchroniser le presse-papier + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 286}, {199, 18}} + + YES + + 67239424 + 0 + Commuter seulement avec : + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{342, 103}, {129, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + 4oyYIENvbW1hbmRlA + + 1048576 + 2147483647 + 1 + + + NSMenuCheckmark + + + + NSMenuMixedState + + _popUpItemAction: + 20 + + + YES + + + OtherViews + + + YES + + + + 4oylIE9wdGlvbg + + 1048576 + 2147483647 + + + _popUpItemAction: + 19 + + + + + 4oyDIENvbnRyw7RsZQ + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + 4oenIFNoaWZ0A + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + 4oeqIFZlcnJvdWlsbGFnZSBtYWp1c2N1bGU + + 1048576 + 2147483647 + + + _popUpItemAction: + 16 + + + + + 3 + YES + YES + 1 + + + + + 268 + {{370, 260}, {34, 17}} + + YES + + 67239424 + 272629760 + Court + + + + + + + + + 268 + {{183, 214}, {257, 18}} + + YES + + 67239424 + 0 + RXNzYXllciBkZSByw6l2ZWlsbGVyIGxlcyBNYWNzIGVuIHZlaWxsZQ + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 262}, {184, 18}} + + YES + + 67239424 + 0 + Q29tbXV0ZXIgYXZlYyB1biBkw6lsYWkgOg + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 238}, {269, 18}} + + YES + + 67239424 + 0 + Q29tbXV0ZXIgYXByw6hzIHVuZSBkb3VibGUgaW1wdWxzaW9uA + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{388, 131}, {58, 22}} + + YES + + -1804468671 + 71304192 + + + + YES + + YES + allowsFloats + attributedStringForZero + decimalSeparator + formatterBehavior + groupingSeparator + locale + maximum + minimum + negativeFormat + positiveFormat + textAttributesForNegativeValues + usesGroupingSeparator + + + YES + + + 0 + + YES + + YES + + + YES + + + + wqA + + , + + + + + 0 + 2 + NO + YES + 2 + AABAAAAAAAAAAAAAAAAAAA + + + 0 + 0 + NO + NO + 2 + AAAAAAAAAAAAAAAAAAAAAA + + + 0K + + YES + + YES + + + YES + + + + + + + + + + + + + + + NaN + + + + + + + + NO + NO + YES + + taille + + YES + + 6 + + textBackgroundColor + + + + 6 + + textColor + + + + + + + 268 + {{385, 281}, {129, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + 20 + + + YES + + + OtherViews + + + YES + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 19 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 16 + + + + + 3 + YES + YES + 1 + + + + + 268 + {{183, 50}, {349, 18}} + + YES + + 67239424 + 0 + QWZmaWNoZXIgdW5lIGZlbsOqdHJlIHRyYW5zbHVjaWRlIHBlbmRhbnQgbGUgY29udHLDtGxlA + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{407, 7}, {86, 32}} + + YES + + 67239424 + 134217728 + VsOpcmlmaWVyA + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{35, 75}, {136, 17}} + + YES + + 67239424 + 71303168 + Statut : + + + + + + + + + 268 + {{409, 261}, {83, 15}} + + YES + + 67239424 + 131072 + + + + + Helvetica + 1.200000e+01 + 16 + + + 1.000000e+00 + 1.000000e-01 + 5.000000e-01 + 0.000000e+00 + 0 + 1 + NO + NO + + + + + 268 + {{497, 263}, {31, 14}} + + YES + + 67239424 + 272629760 + Long + + + + + + + + + 268 + {{183, 16}, {224, 18}} + + YES + + 67239424 + 0 + VsOpcmlmaWVyIGxhIHZlcnNpb24gYXUgZMOpbWFycmFnZQ + + + 1211912703 + 2 + + + + 200 + 25 + + + + {549, 319} + + + + + {{10, 33}, {549, 319}} + + Options + + + + + + + 0 + YES + YES + + + {595, 438} + + + + + + {595, 429} + + + + {{0, 0}, {1680, 1028}} + {224.664, 32} + {3.40282e+38, 3.40282e+38} + + + 1 + 2 + {{194, 375}, {295, 307}} + 1886912512 + + TPClosingWindow + + View + + + + 256 + + YES + + + 256 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + CorePasteboardFlavorType 0x504E4766 + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{20, 159}, {255, 128}} + + + YES + + 537001472 + 33554432 + + + teleport + + 0 + 0 + 0 + NO + + YES + + + + 256 + {{17, 129}, {261, 22}} + + + YES + + 67239424 + 138412032 + teleport + + LucidaGrande-Bold + 1.800000e+01 + 16 + + + + + + + + + 256 + {{17, 113}, {261, 14}} + + + YES + + 67239424 + 138412032 + PHZlcnNpb24+A + + + + + + + + + 256 + {{12, 43}, {274, 39}} + + + YES + + 67239424 + 4194304 + wqkgMjAwMy0yMDA4IGFieXNzb2Z0CkJhc8OpIHN1ciB1bmUgaWTDqWUgZGUgRGF2ZSBTYWcgZXQgSmVy +ZW15IFF1aW5uLgpSZW1lcmNpZW1lbnRzIMOgIE1hbnUwMiwgSnVsaWVuIGV0IFBhdWwuA + + + 1.000000e+01 + 2843 + + + + + + + + + 258 + {{12, 8}, {99, 17}} + + + YES + + 69336577 + 272629760 + 4p6kIHNpdGUgaW50ZXJuZXQ + + + 1.100000e+01 + 16 + + http://teleport.abyssoft.com + + YES + + + + + + + 258 + {{119, 8}, {69, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIGZvcnVtcw + + http://www.abyssoft.com/software/teleport/forums/ + + + + + + + + 258 + {{200, 8}, {92, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIGZhaXJlIHVuIGRvbg + + aHR0cHM6Ly93d3cucGF5cGFsLmNvbS9jZ2ktYmluL3dlYnNjcj9jbWQ9X3hjbGljayZidXNpbmVzcz1q +dWwlNDBtYWMlMmVjb20maXRlbV9uYW1lPXRlbGVwb3J0Jm5vX3NoaXBwaW5nPTAmbm9fbm90ZT0wJnRh +eD0wJmN1cnJlbmN5X2NvZGU9VVNEJmNoYXJzZXQ9VVRGJTJkOCZjaGFyc2V0PVVURiUyZDg + + + + + + + {295, 307} + + + + {{0, 0}, {1680, 1028}} + {213, 129} + {3.40282e+38, 3.40282e+38} + + + + YES + prefs.allowControl + prefs.sharePasteboard + prefs.requireKey + prefs.delayedSwitch + prefs.showStatusItem + prefs.limitPasteboardSize + prefs.maxPasterboardSize + active + prefs.autocheckVersion + prefs.rejectAuthenticationRequests + prefs.enableEncryption + prefs.switchKeyTag + prefs.copyFiles + prefs.requirePasteboardKey + prefs.pasteboardKeyTag + prefs.trustRequestBehavior + prefs.wakeOnLAN + prefs.switchDelay + localHost.supportDragNDrop + prefs.maxPasteboardSize + prefs.hideControlBezel + prefs.switchWithDoubleTap + + YES + + + + + YES + numberOfSelectedRows + + NSTableView + + + + + + YES + + + _window + + + + 26 + + + + layoutView + + + + 145 + + + + allowControlCheckbox + + + + 156 + + + + aboutWindow + + + + 170 + + + + delegate + + + + 171 + + + + showAboutSheet: + + + + 173 + + + + activationCheckbox + + + + 175 + + + + versionTextField + + + + 183 + + + + + + + + 184 + + + + + + + + 200 + + + + view + + + + 202 + + + + sharePasteboardCheckbox + + + + 205 + + + + requireKeyCheckbox + + + + 206 + + + + _firstKeyView + + + + 232 + + + + _initialKeyView + + + + 233 + + + + _lastKeyView + + + + 234 + + + + dataSource + + + + 258 + + + + + + + + 259 + + + + trustedHostsTableView + + + + 260 + + + + statusCheckbox + + + + 266 + + + + delayedSwitchCheckbox + + + + 272 + + + + content + + + + 298 + + + + value: selection.prefs.allowControl + + + + + + + value + selection.prefs.allowControl + 2 + + + 320 + + + + value: selection.prefs.sharePasteboard + + + + + + + + selection.prefs.sharePasteboard + 2 + + + 321 + + + + value: selection.prefs.requireKey + + + + + + + + selection.prefs.requireKey + 2 + + + 322 + + + + value: selection.prefs.delayedSwitch + + + + + + + + selection.prefs.delayedSwitch + 2 + + + 323 + + + + value: selection.prefs.showStatusItem + + + + + + + + selection.prefs.showStatusItem + 2 + + + 324 + + + + value: selection.prefs.limitPasteboardSize + + + + + + + + selection.prefs.limitPasteboardSize + 2 + + + 329 + + + + enabled: selection.prefs.sharePasteboard + + + + + + + enabled + + 2 + + + 330 + + + + + + + + + + + + + 2 + + + 331 + + + + checkVersion: + + + + 337 + + + + value: selection.active + + + + + + + + selection.active + 2 + + + 342 + + + + enabled: selection.active + + + + + + + + + 2 + + + 343 + + + + + + + + + + + + + 2 + + + 344 + + + + enabled2: selection.active + + + + + + + enabled2 + + + YES + + YES + NSMultipleValuesPlaceholder + NSNoSelectionPlaceholder + NSNotApplicablePlaceholder + NSNullPlaceholder + + + YES + + + + + + + + 2 + + + 345 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 346 + + + + enabled3: selection.prefs.limitPasteboardSize + + + + + + + enabled3 + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 347 + + + + + + + + + + + + + 2 + + + 348 + + + + + + + + + + + + + 2 + + + 349 + + + + + + + + + + + + + 2 + + + 350 + + + + + + + + + + + + + 2 + + + 352 + + + + value: selection.prefs.autocheckVersion + + + + + + + + selection.prefs.autocheckVersion + 2 + + + 355 + + + + + + + + + + + + + 2 + + + 356 + + + + + + + + + + + + + 2 + + + 383 + + + + value: selection.prefs.enableEncryption + + + + + + + + selection.prefs.enableEncryption + 2 + + + 384 + + + + enabled: selection.prefs.enableEncryption + + + + + + + + + 2 + + + 386 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 387 + + + + enabled: selection.prefs.requireKey + + + + + + + + + 2 + + + 399 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 400 + + + + selectedTag: selection.prefs.switchKeyTag + + + + + + + selectedTag + selection.prefs.switchKeyTag + 2 + + + 401 + + + + + + + + + + + + + 2 + + + 403 + + + + value: selection.prefs.copyFiles + + + + + + + + selection.prefs.copyFiles + 2 + + + 405 + + + + + + + + 407 + + + + + + + + + + + + + 2 + + + 419 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 420 + + + + value: selection.prefs.requirePasteboardKey + + + + + + + + selection.prefs.requirePasteboardKey + 2 + + + 421 + + + + enabled: selection.prefs.requirePasteboardKey + + + + + + + + + 2 + + + 422 + + + + enabled2: selection.prefs.sharePasteboard + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 423 + + + + enabled3: selection.active + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 424 + + + + selectedTag: selection.prefs.pasteboardKeyTag + + + + + + + + selection.prefs.pasteboardKeyTag + 2 + + + 425 + + + + + + + + + + + + + 2 + + + 431 + + + + + + + + + + + + + 2 + + + 432 + + + + selectedTag: selection.prefs.trustRequestBehavior + + + + + + + + selection.prefs.trustRequestBehavior + 2 + + + 442 + + + + + + + + + + + + + 2 + + + 445 + + + + value: selection.prefs.wakeOnLAN + + + + + + + + selection.prefs.wakeOnLAN + 2 + + + 446 + + + + value: selection.prefs.switchDelay + + + + + + + + selection.prefs.switchDelay + 2 + + + 450 + + + + enabled: selection.prefs.delayedSwitch + + + + + + + + + 2 + + + 451 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 452 + + + + enabled2: selection.localHost.supportDragNDrop + + + + + + + + selection.localHost.supportDragNDrop + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 453 + + + + + + + + + + + + + 2 + + + 454 + + + + value: selection.prefs.maxPasteboardSize + + + + + + + + selection.prefs.maxPasteboardSize + 2 + + + 455 + + + + showCertificateButton + + + + 300281 + + + + chooseCertificateButton + + + + 300282 + + + + showCertificateViewer: + + + + 300283 + + + + showCertificateChooser: + + + + 300284 + + + + + + + + + + + + + 2 + + + 300293 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 300294 + + + + value: selection.prefs.hideControlBezel + + + + + + + + selection.prefs.hideControlBezel + + NSValueTransformerName + NSNegateBoolean + + 2 + + + 300300 + + + + + + + + + + + + + 2 + + + 300301 + + + + value: selection.prefs.switchWithDoubleTap + + + + + + + + selection.prefs.switchWithDoubleTap + 2 + + + 300305 + + + + + + + + + + + + + 2 + + + 300306 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + -3 + + + Application + + + 12 + + + YES + + + + PrefPane + + + 6 + + + YES + + + + + + 201 + + + YES + + + + + + + + + 136 + + + YES + + + + + + 172 + + + YES + + + + + + 174 + + + YES + + + + + + 186 + + + YES + + + + + + + + 187 + + + YES + + + + + + 189 + + + YES + + + + + + + 137 + + + + + 152 + + + YES + + + + + + 188 + + + YES + + + + + + 190 + + + YES + + + + + + 357 + + + YES + + + + + + 358 + + + YES + + + + + + 161 + + + YES + + + + About + + + 162 + + + YES + + + + + + + + + + + + 164 + + + YES + + + + + + 165 + + + YES + + + + + + 166 + + + YES + + + + + + 168 + + + YES + + + + + + 291 + + + YES + + + + + + 292 + + + YES + + + + + + 293 + + + YES + + + + + + 297 + + + PrefPaneController + + + 406 + + + TrustedHostsTable + + + 100136 + + + + + 100172 + + + + + 100174 + + + + + 100152 + + + + + 100164 + + + + + 100165 + + + + + 100166 + + + + + 100168 + + + + + 100291 + + + + + 100292 + + + + + 100293 + + + + + 300315 + + + YES + + + + + + + + + + + + + + 441 + + + YES + + + + + + 100441 + + + + + 300279 + + + YES + + + + + + 300280 + + + + + 385 + + + YES + + + + + + 100385 + + + + + 381 + + + YES + + + + + + 100381 + + + + + 300297 + + + YES + + + + + + 300298 + + + + + 434 + + + YES + + + + + + + + + 100434 + + + + + 439 + + + + + 438 + + + + + 437 + + + + + 246 + + + YES + + + + + + + + + 300246 + + + + + 200246 + + + + + 100246 + + + + + 247 + + + YES + + + + + + + + 360 + + + YES + + + + + + 249 + + + YES + + + + + + 248 + + + YES + + + + + + 100248 + + + + + 100249 + + + + + 361 + + + + + 440 + + + YES + + + + + + 100440 + + + + + 362 + + + YES + + + + + + 100362 + + + + + 300316 + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + 325 + + + YES + + + + + + 100325 + + + + + 363 + + + YES + + + + + + 100363 + + + + + 367 + + + YES + + + + + + 100367 + + + + + 366 + + + YES + + + + + + 100366 + + + + + 264 + + + YES + + + + + + 100264 + + + + + 402 + + + YES + + + + + + 100402 + + + + + 411 + + + YES + + + + + + 100411 + + + + + 191 + + + YES + + + + + + 100191 + + + + + 194 + + + YES + + + + + + 100194 + + + + + 412 + + + YES + + + + + + 100412 + + + YES + + + + + + 413 + + + YES + + + + + + + + + + 414 + + + + + 415 + + + + + 416 + + + + + 417 + + + + + 418 + + + + + 448 + + + YES + + + + + + 100448 + + + + + 443 + + + YES + + + + + + 100443 + + + + + 271 + + + YES + + + + + + 100271 + + + + + 300302 + + + YES + + + + + + 300303 + + + + + 326 + + + YES + + + + + + 100326 + + + YES + + + + + + 334 + + + + + 389 + + + YES + + + + + + 100389 + + + YES + + + + + + 390 + + + YES + + + + + + + + + + 391 + + + + + 392 + + + + + 393 + + + + + 394 + + + + + 395 + + + + + 300295 + + + YES + + + + + + 300296 + + + + + 336 + + + YES + + + + + + 100336 + + + + + 368 + + + YES + + + + + + 100368 + + + + + 447 + + + YES + + + + + + 100447 + + + + + 449 + + + YES + + + + + + 100449 + + + + + 353 + + + YES + + + + + + 100353 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + -3.ImportedFromIB2 + 100246.IBShouldRemoveOnLegacySave + 100248.IBShouldRemoveOnLegacySave + 100249.IBShouldRemoveOnLegacySave + 12.IBPluginDependency + 12.IBWindowTemplateEditedContentRect + 12.ImportedFromIB2 + 12.editorWindowContentRectSynchronizationRect + 12.windowTemplate.hasMaxSize + 12.windowTemplate.hasMinSize + 12.windowTemplate.maxSize + 12.windowTemplate.minSize + 136.IBAttributePlaceholdersKey + 136.IBPluginDependency + 136.ImportedFromIB2 + 137.IBAttributePlaceholdersKey + 137.IBPluginDependency + 137.ImportedFromIB2 + 152.IBPluginDependency + 152.ImportedFromIB2 + 161.IBPluginDependency + 161.IBWindowTemplateEditedContentRect + 161.ImportedFromIB2 + 161.editorWindowContentRectSynchronizationRect + 161.windowTemplate.hasMaxSize + 161.windowTemplate.hasMinSize + 161.windowTemplate.maxSize + 161.windowTemplate.minSize + 162.IBPluginDependency + 162.ImportedFromIB2 + 164.IBPluginDependency + 164.ImportedFromIB2 + 165.IBPluginDependency + 165.ImportedFromIB2 + 166.CustomClassName + 166.IBPluginDependency + 166.ImportedFromIB2 + 168.IBPluginDependency + 168.ImportedFromIB2 + 172.IBAttributePlaceholdersKey + 172.IBPluginDependency + 172.ImportedFromIB2 + 174.IBAttributePlaceholdersKey + 174.IBPluginDependency + 174.ImportedFromIB2 + 186.IBPluginDependency + 186.ImportedFromIB2 + 187.IBPluginDependency + 187.ImportedFromIB2 + 188.IBPluginDependency + 188.ImportedFromIB2 + 189.IBPluginDependency + 189.ImportedFromIB2 + 190.IBPluginDependency + 190.ImportedFromIB2 + 191.IBPluginDependency + 191.ImportedFromIB2 + 194.IBPluginDependency + 194.ImportedFromIB2 + 200246.IBShouldRemoveOnLegacySave + 201.IBPluginDependency + 201.ImportedFromIB2 + 246.IBPluginDependency + 246.ImportedFromIB2 + 247.CustomClassName + 247.IBPluginDependency + 247.ImportedFromIB2 + 248.IBPluginDependency + 248.ImportedFromIB2 + 249.IBPluginDependency + 249.ImportedFromIB2 + 264.IBPluginDependency + 264.ImportedFromIB2 + 271.IBPluginDependency + 271.ImportedFromIB2 + 291.CustomClassName + 291.IBPluginDependency + 291.ImportedFromIB2 + 292.CustomClassName + 292.IBPluginDependency + 292.ImportedFromIB2 + 293.CustomClassName + 293.IBPluginDependency + 293.ImportedFromIB2 + 297.IBPluginDependency + 297.ImportedFromIB2 + 300246.IBShouldRemoveOnLegacySave + 300279.IBPluginDependency + 300279.ImportedFromIB2 + 300295.IBPluginDependency + 300295.ImportedFromIB2 + 300297.IBPluginDependency + 300297.ImportedFromIB2 + 300302.IBAttributePlaceholdersKey + 300302.IBPluginDependency + 300302.ImportedFromIB2 + 325.IBPluginDependency + 325.ImportedFromIB2 + 326.IBPluginDependency + 326.ImportedFromIB2 + 334.IBPluginDependency + 334.ImportedFromIB2 + 336.IBPluginDependency + 336.ImportedFromIB2 + 353.IBPluginDependency + 353.ImportedFromIB2 + 357.IBPluginDependency + 357.ImportedFromIB2 + 358.IBPluginDependency + 358.ImportedFromIB2 + 360.IBPluginDependency + 360.ImportedFromIB2 + 361.IBPluginDependency + 361.ImportedFromIB2 + 362.IBPluginDependency + 362.ImportedFromIB2 + 363.IBPluginDependency + 363.ImportedFromIB2 + 366.IBPluginDependency + 366.ImportedFromIB2 + 367.IBPluginDependency + 367.ImportedFromIB2 + 368.IBPluginDependency + 368.ImportedFromIB2 + 381.IBPluginDependency + 381.ImportedFromIB2 + 385.IBPluginDependency + 385.ImportedFromIB2 + 389.IBPluginDependency + 389.ImportedFromIB2 + 390.IBPluginDependency + 390.ImportedFromIB2 + 390.editorWindowContentRectSynchronizationRect + 391.IBPluginDependency + 391.ImportedFromIB2 + 392.IBPluginDependency + 392.ImportedFromIB2 + 393.IBPluginDependency + 393.ImportedFromIB2 + 394.IBPluginDependency + 394.ImportedFromIB2 + 395.IBPluginDependency + 395.ImportedFromIB2 + 402.IBPluginDependency + 402.ImportedFromIB2 + 406.IBPluginDependency + 406.ImportedFromIB2 + 411.IBPluginDependency + 411.ImportedFromIB2 + 412.IBPluginDependency + 412.ImportedFromIB2 + 413.IBPluginDependency + 413.ImportedFromIB2 + 413.editorWindowContentRectSynchronizationRect + 414.IBPluginDependency + 414.ImportedFromIB2 + 415.IBPluginDependency + 415.ImportedFromIB2 + 416.IBPluginDependency + 416.ImportedFromIB2 + 417.IBPluginDependency + 417.ImportedFromIB2 + 418.IBPluginDependency + 418.ImportedFromIB2 + 434.IBPluginDependency + 434.ImportedFromIB2 + 437.IBPluginDependency + 437.ImportedFromIB2 + 438.IBPluginDependency + 438.ImportedFromIB2 + 439.IBPluginDependency + 439.ImportedFromIB2 + 440.IBPluginDependency + 440.ImportedFromIB2 + 441.IBPluginDependency + 441.ImportedFromIB2 + 443.IBAttributePlaceholdersKey + 443.IBPluginDependency + 443.ImportedFromIB2 + 447.IBPluginDependency + 447.ImportedFromIB2 + 448.IBPluginDependency + 448.ImportedFromIB2 + 449.IBPluginDependency + 449.ImportedFromIB2 + 6.IBPluginDependency + 6.ImportedFromIB2 + + + YES + + + + + + + + + {{517, 427}, {595, 429}} + + + + + {3.40282e+38, 3.40282e+38} + {224.664, 10} + + ToolTip + + + + Check to allow other teleport users to control your Mac. + + + + + + + + + + Q2VjaSBlc3QgbGEgem9uZSBkZSBkaXNwb3NpdGlvbi4gR2xpc3Nlci1kw6lwb3NlciBsZXMgaMO0dGVz +IG/DuSB2b3VzIGxlIHNvdWhhaXRlei4 + + + + + + + + {{194, 375}, {295, 307}} + + + + + + {213, 107} + + + + + + + TPVersionTextField + + + + + + + + + + QSBwcm9wb3MgZGUgdGVsZXBvcnTigKY + + + + + + + + + + Check to activate teleport. + + + + + + + + + + + + + + + + + + + + + + + + TPTableView + + + + + + + + + + + TPLinkTextField + + + + + + + + + + + + + + + + + + + + + + + VG91Y2hleiBsZSBib3JkIGRlIGwnw6ljcmFuLCByZXZlbmV6IGV0IHJlLXRvdWNoZXogcmFwaWRlbWVu +dCBsZSBib3JkIHBvdXIgZWZmZWN0dWVyIHVuZSBkb3VibGUtaW1wdWxzaW9uLg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{889, 654}, {243, 103}} + + + + + + + + + + + + + + + + + + + + + {{871, 476}, {243, 103}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + U2V1bGVtZW50IHBvdXIgbGVzIE1hY3MgY29ubmVjdMOpcyBlbiBFdGhlcm5ldCBldCBhdmVjIGwnb3B0 +aW9uICJSw6l2ZWlsbGVyIHBvdXIgbCdhY2PDqXMgRXRoZXJuZXQgcGFyIGwnYWRtaW5pc3RyYXRldXIi +IGNvY2jDqSBkYW5zIGxlIHBhbm5lYXUgZGUgcHLDqWbDqXJlbmNlcyBFY29ub21pZSBkJ0VuZXJnaWUu +A + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 300316 + + + + YES + + + + + YES + + YES + + + YES + + + + + id + + + IBUserSource + + + + + TPClosingWindow + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBProjectSource + TPClosingWindow.h + + + + NSObject + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPHotBorder.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection_Private.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSecureSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPUDPSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTransfersManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTableView.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPDaemonManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPPreferencesManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPConnectionsManager.h + + + + + NSTextField + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + TPVersionTextField + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPVersionTextField.h + + + + + NSPreferencePane + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + TPPreferencePane + NSPreferencePane + + YES + + YES + addOther: + checkVersion: + closeAddOther: + confirmAddOther: + deleteTrustedHost: + showAboutSheet: + showCertificateChooser: + showCertificateViewer: + + + YES + id + + + + + + + + + + + YES + + YES + aboutWindow + activationCheckbox + allowControlCheckbox + chooseCertificateButton + delayedSwitchCheckbox + layoutView + requireKeyCheckbox + sharePasteboardCheckbox + showCertificateButton + statusCheckbox + trustedHostsTableView + versionTextField + + + YES + + + + + + TPLayoutView + + + + + + + + + + + TPPreferencePane.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + TPTableView + NSTableView + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTableView.h + + + + TPLayoutView + + + YES + + YES + scroll: + setScaleFactor: + + + YES + + + + + + + + + + + TPLayoutView.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + TPLinkTextField + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPLinkTextField.h + + + + + + + : + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + addOther: + changeTabPanel: + + closeAddOther: + closeTrustedCertificate: + confirmAddOther: + deleteTrustedHost: + + showCertificate: + showTrustedHosts: + + + YES + + + + + + + + + + + + + + YES + + YES + + + + + + + addOtherAddressField + addOtherHeightField + addOtherNameField + addOtherSheet + addOtherWidthField + + certificateView + certificateWindow + + + + + + + + + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + ../../teleport.xcodeproj + 3 + + YnBsaXN0MDDUAAEAAgADAAQABQAGAAkAClgkdmVyc2lvblQkdG9wWSRhcmNoaXZlclgkb2JqZWN0cxIA +AYag0QAHAAhdSUIub2JqZWN0ZGF0YYABXxAPTlNLZXllZEFyY2hpdmVyrxEEBwALAAwAMQA1ADYAPAA9 +AEEARQCfAKcAtwDCAAsAwwDdAN4A5gDnAOoA7gDvAPIA8wD3AP0A/gECAQcBDwEXARgBIwEkAScBKwEs +ATEBOQFSAVMBVAFVAVYBVwFYAVkBWgFbAVwBXQFeAV8BYAFhAWIBYwFkAWUBZgFnAWgBbAFuAXABdAF1 +AXoBhgGHAYgBjwALAZABmgGbAaABqgGrAawBsQGzAbgBuQG8Ab8BwgHDAcgB0QHYAdkA+wHaAd0B3gHj +AgICAwIEAhMCFAIaAiMCJAInAiwCPwJAAkMCSQJbAlwCYwJkAmcCbAJtAnACeQJ6An8CgAKDAowCjQKT +ApQCmwKjAqQCqgKrArACsQK0AsACwQLGAscCygLLAtAC0QLWAt8C4ALrAuwC8QLyAvMC9gL4AvkC/AMB +AwcDCwIDAwwDDgMTAxoDIQMiAyoDKwMsAzEDNgM9A0QDTANNA1UDVgNmA2oDbwN4A3kDgQOCA4QDhQOH +A4gDjgOWA5cDmAObA6EDpgOqA7YDvgO/A8cDyAPPA9AD1wPYA9oD4QPiA+oD6wPyA/MD+wP8BBYEFwQd +BCYEJwQqBCsELQQ2BDcEPwRABEEA9gRCBEcESARLBFMEVwRYBFsEZgRnBGgEawRzBHQEeAR5BHoEfQSE +BIUEjASNBJQElQScBJ0DhASeBJ8EpgSnBKwEsATJBNAE0QTZBNoE4QTiBOkE6gTxBPIE+QT6BQEFAgUJ +BQoFEQUSBRoFGwUiBSMFKwUsBTMFNAU8BT0FRAVFBU0FTgVVBVYFawVtBYAFhQWGBYoFiwWPBZAFkQWT +BZYFngWoBZAFqQWzBZAFtAW+BZAFvwXJBZAFygXMBdAF0wXaBdsF4wXkBesF7AX0BfUF/AX9BgUGBgYN +Bg4GFgYXBh4GHwYsBk4GawZsBm0GbgZvBnAGcQZyBnMGdAZ1BnYGdwaCBoUGhgaLBowGkAaTBpYGmgab +BpwGoAEcBqMA2waoBqkGrADyBq8Gswa0BrcGuAa9Br4GwwbEBssGzAbXBtkG4gWQBuYG6AbwBZAG+QWQ +BwIFkAcLBZAHFAcbBxwHJAclBywHLQc0BzUHNwc+Bz8HRgdHB04HTwdjB2UHaQdqB20HcAd3B3gHfweA +B4cHiAeQBJ0DhAeRB5IHlAeVB5YHmwecB6EHogenB70HvgCtB78HwgfGAAsHxwfIB8kHygfNB84H0wfe +B98H4AfiB+wH9wgBCAIIAwgECAUIBggHCAgICQgTCBcIGAgbCB4IJggnCBcILggyCDMIOwg8CEQIRQhK +CFUIVghXCGEIYghlCGYIcAhxCHkIegh7CIUIhgiOCI8IkAALB8cIkQfJCJIIlwiYCJ0IngijCKQIqQiq +CK8IsAi1CLYIuwi8CMEIxgjHCMwIzQjSCNMI2AjZCOMI5AjlCOYI6QjwCPEI8gj5CPoI+wkGCQcJCAkU +CRUJFgkXCRgJGQkgCSEJIgkpCSoJKwkyCTMJNAk7CUIJSQlKCUsJTAlUCVUJVglcCV0JXgllCWYJbQlu +CW8Jdgl3CXgJgQmCCY4JlwmYCZkJpQmsCa0Jrgm3CcMJygnRCdIJ0wnaCeMJ7wn2CfcJ+An/CgYKBwoO +ChUKFgoXCh4KJQomCicKMAo8CkMKRApFCkwKTQpOClUKXAplCmYKZwpzCnoKewp8CoMKigqRCpoKpgqt +CrQKuwrECtAK2QraCtsK5wruCu8K9gr9Cv4LBwsTCxoLGwscCyMLJAsrCywLMws6CzsLPAs/C0QLRQtK +C0sLUAtRC1YLVwtcC10L3wviC+ML5QxnDOoNbQ1uDW8NcA1xDXINcw10DXUNdg13DXgNeQ16DXsNfA19 +DX4Nfw2ADYENgg2DDYQNhQ2GDYcNiA2JDYoNiw2MDY0Njg2PDZANkQ2SDZMNlA2VDZYNlw2YDZkNmg2b +DZwNnQ2eDZ8NoA2hDaINowfBDaQNpQ2mDacNqA2pDaoNqw2sDa0Nrg2vDbANsQ2yDbMNtA21DbYNtw24 +DbkNug27DbwNvQ2+Db8NwA3BDcINww3EDcUNxg3HDcgNyQ3KDcsNzA3NDc4Nzw3QDdEN0g3TDdQN1Q3W +DdcN2A3ZDdoN2w3cDd0N3g3fDeAN4Q3iDeMN5A3lDeYN5w3oDekN6g3rDewN9A38DtYPsA+xD7IPswGn +D7QPtQ+2D7cPuA+5D7oPuw+8D70Pvg+/D8APwQDaD8IPww/ED8UPxg/HD8gPyQ/KD8sPzA/ND84Pzw/Q +D9EP0g/TD9QP1Q/WD9cP2A/ZD9oP2w/cD90P3g/fD+AP4Q/iD+MP5A/lD+YP5w/oD+kP6g/rD+wP7Q/u +D+8P8AK9D/EP8g/zD/QP9Q/2D/cP+A/5D/oP+w/8D/0P/g//EAAQARACEAMQBBAFEAYQBxAIEAkQChAL +EAwQDRAOEA8QEBAREBIQExAUEBUQFhAXEBgIUBAZEBoQGxAcEB0QHhAfECAQIRAiECMQJBAlECYQJxAo +ECkQKhArECwQLRAuEC8QMBAxEDIQMxA0EDUQNhA3EDgQORA6EDsQPBA9ED4QPxBAEEEQQhBDEEQQRRBG +EEcQSBBJEEoQSxBMEE0QThBPEFAQURBSEFMQVBBVEFYQVxBYEFkQWgO7EFsQXBBdEF4QXxBgEGEQYhBj +EGQQZRBmEGcQaBBpEGoQaxBsEG0QbhBvEHAQcRByEHMQdBB1EHYQdxB4EHkQehB7EHwQfRB+EH8QgBCB +EIIQhRCIEItVJG51bGzfEBIADQAOAA8AEAARABIAEwAUABUAFgAXABgAGQAaABsAHAAdAB4AHwAgACEA +IgAjACQAJQAmACcAKAApACoAKwAsAC0ALgAvADBWTlNSb290ViRjbGFzc11OU09iamVjdHNLZXlzXxAP +TlNDbGFzc2VzVmFsdWVzXxAZTlNBY2Nlc3NpYmlsaXR5T2lkc1ZhbHVlc11OU0Nvbm5lY3Rpb25zW05T +TmFtZXNLZXlzW05TRnJhbWV3b3JrXU5TQ2xhc3Nlc0tleXNaTlNPaWRzS2V5c11OU05hbWVzVmFsdWVz +XxAZTlNBY2Nlc3NpYmlsaXR5Q29ubmVjdG9yc11OU0ZvbnRNYW5hZ2VyXxAQTlNWaXNpYmxlV2luZG93 +c18QD05TT2JqZWN0c1ZhbHVlc18QF05TQWNjZXNzaWJpbGl0eU9pZHNLZXlzWU5TTmV4dE9pZFxOU09p +ZHNWYWx1ZXOAAoEEBoECoYEDKYEEBYAIgQKmgAWBAyiBAyqBAqeBBAOAAIAGgQKlgQQEEgAElSSBAyvS +AA4AMgAzADRbTlNDbGFzc05hbWWABIADXxAQVFBQcmVmZXJlbmNlUGFuZdIANwA4ADkAOlgkY2xhc3Nl +c1okY2xhc3NuYW1logA6ADteTlNDdXN0b21PYmplY3RYTlNPYmplY3RfEBBJQkNvY29hRnJhbWV3b3Jr +0gAOAD4APwBAWk5TLm9iamVjdHOAB6DSADcAOABCAEOjAEMARAA7XE5TTXV0YWJsZVNldFVOU1NldNIA +DgA+AEYAR4A9rxBXAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0AXgBf +AGAAYQBiAGMAZABlAGYAZwBoAGkAagBrAGwAbQBuAG8AcABxAHIAcwB0AHUAdgB3AHgAeQB6AHsAfAB9 +AH4AfwCAAIEAggCDAIQAhQCGAIcAiACJAIoAiwCMAI0AjgCPAJAAkQCSAJMAlACVAJYAlwCYAJkAmgCb +AJwAnQCegAmAG4AkgEKAV4BfgI2Aj4CbgKGAqICpgQG0gQG2gQG4gQHGgQH8gQH+gQIAgQICgQIEgQIG +gQIIgQIKgQILgQINgQIPgQIRgQITgQIYgQIbgQIegQIngQIqgQItgQIwgQIxgQIygQI2gQI8gQI+gQJB +gQJEgQJHgQJLgQJOgQJQgQJRgQJUgQJVgQJXgQJagQJbgQJdgQJegQJhgQJigQJlgQJngQJqgQJtgQJu +gQJvgQJzgQJ2gQJ3gQJ4gQJ5gQJ7gQJ8gQJ9gQJ+gQKAgQKEgQKGgQKHgQKJgQKLgQKOgQKQgQKSgQKT +gQKXgQKZgQKbgQKdgQKf1AAOAKAAoQCiAKMApAAfAKZdTlNEZXN0aW5hdGlvblhOU1NvdXJjZVdOU0xh +YmVsgBqACoACgBnYAKgADgCpAKoAqwCsAK0ArgCvALAAsQCyALMAtAC1AK9fEA9OU05leHRSZXNwb25k +ZXJXTlNGcmFtZVZOU0NlbGxYTlN2RmxhZ3NZTlNFbmFibGVkWE5TV2luZG93W05TU3VwZXJ2aWV3gAuA +GIAMgA4RAQwJgA2AC9gAqAAOALgAqwC5AK0AMgCuALoAuwC8AL0AvgC1AMAAulpOU1N1YnZpZXdzW05T +RnJhbWVTaXplgKqAXYCrEQESgQGygA2AXICqXxAXe3sxNDksIDM4N30sIHsxMjIsIDE4fX3dAMQADgDF +AMYAxwDIAMkAygDLAMwAzQDOAM8A0ADRANIA0wDUANUA0gDXANgApADaANsA3FtOU0NlbGxGbGFnc18Q +E05TQWx0ZXJuYXRlQ29udGVudHNfEBJOU1BlcmlvZGljSW50ZXJ2YWxeTlNCdXR0b25GbGFnczJfEBBO +U0FsdGVybmF0ZUltYWdlXxAPTlNLZXlFcXVpdmFsZW50Wk5TQ29udGVudHNZTlNTdXBwb3J0XU5TQ29u +dHJvbFZpZXdfEA9OU1BlcmlvZGljRGVsYXlcTlNDZWxsRmxhZ3MyXU5TQnV0dG9uRmxhZ3MSJAH+AIAX +gBYQGRACgBOAFoAPgBCAChDIEAASSDxR/18QD1BhcnRhZ2VyIGNlIE1hY9QADgDfAOAA4QDiAOMA5ADl +Vk5TU2l6ZVZOU05hbWVYTlNmRmxhZ3OAEiNAKgAAAAAAAIAREQQUXEx1Y2lkYUdyYW5kZdIANwA4AOgA +6aIA6QA7Vk5TRm9udNIADgDrAOwA7VtOU0ltYWdlTmFtZYAVgBRYTlNTd2l0Y2jSADcAOADwAPGiAPEA +O18QE05TQnV0dG9uSW1hZ2VTb3VyY2VQ0gA3ADgA9AD1pAD1APYAqgA7XE5TQnV0dG9uQ2VsbFxOU0Fj +dGlvbkNlbGzSADcAOAD4APmlAPkA+gD7APwAO1hOU0J1dHRvbllOU0NvbnRyb2xWTlNWaWV3W05TUmVz +cG9uZGVyXxAUYWxsb3dDb250cm9sQ2hlY2tib3jSADcAOAD/AQCjAQABAQA7XxAUTlNOaWJPdXRsZXRD +b25uZWN0b3JeTlNOaWJDb25uZWN0b3LUAA4AoAChAKIAowEEAB8BBoAagByAAoAj1wCoAA4AqQCqAKsA +rACuAQgAsAEKAQsBDAC0AQiAHYAYgB6AHxP/////gAABDAmAHdcAqAAOALgAqwC5ADIArgEQALsBEgET +ARQAwAEQgMGAXYDDEQEtgP2AXIDBXxAXe3sxNzksIDI1MH0sIHsxNzIsIDMyfX3cAMQADgDFAMYAxwDJ +AMoAywDMAM0AzgDPARkA0QDSANMBHAEdAR4A2AEEANoBIQEiEgQB/gCAF4AWEAGAIYAggBCAHBIIAAAA +E/////+GgkD/bxAWAEMAaABvAGkAcwBpAHIAIABsAGUAIABjAGUAcgB0AGkAZgBpAGMAYQB0ICbSAA4B +JQEmAPJZTlMuc3RyaW5ngCLSADcAOAEoASmjASkBKgA7XxAPTlNNdXRhYmxlU3RyaW5nWE5TU3RyaW5n +XxAXY2hvb3NlQ2VydGlmaWNhdGVCdXR0b27UAA4AoAChAKIAowAfAS8BMIAagAKAJYBB1AAOATIBMwE0 +ATUBNgC0AThfEA9fTlNNYW5hZ2VkUHJveHlaTlNFZGl0YWJsZV5OU0RlY2xhcmVkS2V5c4BAgD4JgCbS +AA4APgBGATuAPa8QFgE8AT0BPgE/AUABQQFCAUMBRAFFAUYBRwFIAUkBSgFLAUwBTQFOAU8BUAFRgCeA +KIApgCqAK4AsgC2ALoAvgDCAMYAygDOANIA1gDaAN4A4gDmAOoA7gDxfEBJwcmVmcy5hbGxvd0NvbnRy +b2xfEBVwcmVmcy5zaGFyZVBhc3RlYm9hcmRfEBBwcmVmcy5yZXF1aXJlS2V5XxATcHJlZnMuZGVsYXll +ZFN3aXRjaF8QFHByZWZzLnNob3dTdGF0dXNJdGVtXxAZcHJlZnMubGltaXRQYXN0ZWJvYXJkU2l6ZV8Q +GHByZWZzLm1heFBhc3RlcmJvYXJkU2l6ZVZhY3RpdmVfEBZwcmVmcy5hdXRvY2hlY2tWZXJzaW9uXxAi +cHJlZnMucmVqZWN0QXV0aGVudGljYXRpb25SZXF1ZXN0c18QFnByZWZzLmVuYWJsZUVuY3J5cHRpb25f +EBJwcmVmcy5zd2l0Y2hLZXlUYWdfEA9wcmVmcy5jb3B5RmlsZXNfEBpwcmVmcy5yZXF1aXJlUGFzdGVi +b2FyZEtleV8QFnByZWZzLnBhc3RlYm9hcmRLZXlUYWdfEBpwcmVmcy50cnVzdFJlcXVlc3RCZWhhdmlv +cl8QD3ByZWZzLndha2VPbkxBTl8QEXByZWZzLnN3aXRjaERlbGF5XxAabG9jYWxIb3N0LnN1cHBvcnRE +cmFnTkRyb3BfEBdwcmVmcy5tYXhQYXN0ZWJvYXJkU2l6ZV8QFnByZWZzLmhpZGVDb250cm9sQmV6ZWxf +EBlwcmVmcy5zd2l0Y2hXaXRoRG91YmxlVGFw0gA3ADgBaQFqowFqAWsAO15OU011dGFibGVBcnJheVdO +U0FycmF50QAOAW2AP9IANwA4AW8BMqIBMgA70gA3ADgBcQFyowFyAXMAO18QEk5TT2JqZWN0Q29udHJv +bGxlclxOU0NvbnRyb2xsZXJXY29udGVudNQADgCgAKEAogCjAXcAHwF5gBqAQ4ACgFbaAKgADgCpAXsA +qgCrAKwArQAyAK4BfAF9AX4BfwGAAYEAtAGDAYQBfF8QE05TT3JpZ2luYWxDbGFzc05hbWWARoBVgEeA +RYBJEQEACYBIgESARl8QElRQVmVyc2lvblRleHRGaWVsZFtOU1RleHRGaWVsZNcAqAAOALgAqwC5AK0A +rgGDAYoBiwGBAYwBgwGOgEiAuYEBy4EB94BIgQH4XxAWe3sxNywgMTEzfSwgezI2MSwgMTR9fdgAxAAO +AZEAygDLAMwAzgGSARkBkwGUAZUBlgF3AZgBmV8QEU5TQmFja2dyb3VuZENvbG9yW05TVGV4dENvbG9y +gFSATIBKgEuAQxIIQAAAgFFZPHZlcnNpb24+1AAOAN8A4ADhAOIBnQDkAZ+AEiNAJgAAAAAAAIAREQwc +1QAOAaEBogGjAaQBpQGmAacBqAGpV05TQ29sb3JcTlNDb2xvclNwYWNlW05TQ29sb3JOYW1lXU5TQ2F0 +YWxvZ05hbWWAUIBPEAaAToBNVlN5c3RlbVxjb250cm9sQ29sb3LTAA4BogGtAaUBrwGwV05TV2hpdGWA +UBADSzAuNjY2NjY2NjkA0gA3ADgBsgGhogGhADvVAA4BoQGiAaMBpAGlAbUBpwG2AamAUIBTgFKATV8Q +EGNvbnRyb2xUZXh0Q29sb3LTAA4BogGtAaUBrwG7gFBCMADSADcAOAG9Ab6kAb4A9gCqADtfEA9OU1Rl +eHRGaWVsZENlbGzSADcAOAHAAcGiAcEAO15OU0NsYXNzU3dhcHBlcl8QEHZlcnNpb25UZXh0RmllbGTU +AA4AoAChAKIAowAfAcYBx4AagAKAWIBe2ACoAA4AqQCrAK0AMgHJAK4BygC7AcwAvQC1Ac4AwAHKW05T +RXh0ZW5zaW9ugFmAXYBagA2AW4BcgFnXAKgADgCpALgAqwCtAK4B0gGKAdQB1QGBALUB0oCwgLmAuICy +gA2AsF8QFnt7MjAsIDE3fSwgezUxMSwgMjk2fX1cVFBMYXlvdXRWaWV30gA3ADgB2wHcpAHcAPsA/AA7 +XE5TQ3VzdG9tVmlld1hkZWxlZ2F0ZdQADgCgAKEAogCjAeAAHwHigBqAYIACgIzfEBUAqAHkAA4B5QF7 +AeYBkQHnAegB6QHqAesAqwC5AKwAMgHsAe0ArgHuAe8B8ADbAX0B8gHzAfQB9QC0AfcB+AEcAfkBgQH6 +ALQB/AH9Af4B8AIAAgFfEB9OU0RyYWdnaW5nU291cmNlTWFza0Zvck5vbkxvY2FsWU5TVHZGbGFnc1xO +U0hlYWRlclZpZXdfEBJOU0FsbG93c1R5cGVTZWxlY3RcTlNDb3JuZXJWaWV3XxAXTlNJbnRlcmNlbGxT +cGFjaW5nV2lkdGhfEBlOU0NvbHVtbkF1dG9yZXNpemluZ1N0eWxlXxAYTlNJbnRlcmNlbGxTcGFjaW5n +SGVpZ2h0W05TR3JpZENvbG9yXxAcTlNEcmFnZ2luZ1NvdXJjZU1hc2tGb3JMb2NhbF5OU1RhYmxlQ29s +dW1uc1tOU1Jvd0hlaWdodIBjgFUSGsCAAIBigGWAhgmAaSNACAAAAAAAACNAAAAAAAAAAIBkCYBhgIkQ +D4BjgG0jQDEAAAAAAABbVFBUYWJsZVZpZXdbTlNUYWJsZVZpZXfaAKgADgCpAgUAuACrAgYCBwCuAggC +CQIKAgsCDAINAg4B4AIQAgkB4FlOU2N2RmxhZ3NZTlNEb2NWaWV3WU5TQkdDb2xvcl1OU05leHRLZXlW +aWV3gGqA6oDpEASA6BEJAIBggHiAaoBgWnszMzMsIDEwNH3WAKgADgCrALkArgIDAhUCFgGBAhcCFQHg +gGaAaIBngGaAYNoAqAAOAKkCBQC4AKsCBgIHAK4CCAIJAgoCHQIMAh4CDgH0AhACCQH0gGqA6oDygPGA +ZYB4gGqAZVl7MzMzLCAxN33SADcAOAIlAiakAiYA+wD8ADtfEBFOU1RhYmxlSGVhZGVyVmlld9UAqAAO +AKkAqwCuAgkCKQIqAYECCYBqgGyAa4Bq3gCoAi0ADgCpAi4B6AIvALgCMACrAK4CMQIIAjIBCAI0AjUC +NgDUAfcCFQI5AjoAvQEIAjwB8AHwW05TSFNjcm9sbGVyWE5Tc0ZsYWdzXxAQTlNIZWFkZXJDbGlwVmll +d1xOU1Njcm9sbEFtdHNbTlNWU2Nyb2xsZXJdTlNDb250ZW50Vmlld4AdgO+A9IDzgGmAZoDnTxAQQSAA +AEEgAABBmAAAQZgAAIAdgOuAY4BjXxAUe3stMjYsIDB9LCB7MTYsIDE3fX3SADcAOAJBAkKkAkIA+wD8 +ADtdX05TQ29ybmVyVmlld9IADgA+AEYCRYA9owJGAkcCSIBugHuAgNoCSgAOAksCTAJNAk4CTwJQAlEC +AwC0AlMCVAJVAlYCVwEcAlgCWQHgXk5TSXNSZXNpemVhYmxlXE5TSGVhZGVyQ2VsbFxOU0lkZW50aWZp +ZXJXTlNXaWR0aFpOU0RhdGFDZWxsXk5TUmVzaXppbmdNYXNrWk5TTWluV2lkdGhaTlNNYXhXaWR0aAmA +eoBwgG8jQGKgAAAAAACAdiNARAAAAAAAACNAj0AAAAAAAIBgVG5hbWXXAMQADgGRAMoAywDOAZICXQJe +Al8CYAGWANsCYhIEgf4AgHWAcoBxgEuAc1NOb23TAA4BogGtAaUBrwJmgFBLMC4zMzMzMzI5OQDVAA4B +oQGiAaMBpAGlAbUBpwJqAamAUIBTgHSATV8QD2hlYWRlclRleHRDb2xvctIANwA4Am4Cb6UCbwG+APYA +qgA7XxARTlNUYWJsZUhlYWRlckNlbGzYAMQADgGRAMoAywDMAM4BkgJxAZMCEAJ0AZYB4AJ3AZkSFCH+ +QIBUgHiAd4BLgGASAAIIAIBRWVRleHQgQ2VsbNUADgGhAaIBowGkAaUBpgGnAn0BqYBQgE+AeYBNXxAW +Y29udHJvbEJhY2tncm91bmRDb2xvctIANwA4AoECgqICggA7XU5TVGFibGVDb2x1bW7aAkoADgJLAkwC +TQJOAk8CUAJRAgMAtAJTAoYChwKIAokBHAKKAlkB4AmAeoB9gHwjQF0AAAAAAACAfyNAIAAAAAAAAIBg +V2FkZHJlc3PXAMQADgGRAMoAywDOAZICXQJeAl8CkAGWANsCYoB1gHKAfoBLgHNXQWRyZXNzZdgAxAAO +AZEAygDLAMwAzgGSAnEBkwIQAnQBlgHgAncBmYBUgHiAd4BLgGCAUdgADgJLAkwCTQJOAlACUQIDAlMC +nQKeAp8CoAKhAlkB4IB6gIKAgSNATYAAAAAAAICHI0BNSa/gAAAAgGBbY2VydGlmaWNhdGXXAMQADgGR +AMoAywDOAZIBGQJeAqYCpwGWANsCYoB1gISAg4BLgHNaQ2VydGlmaWNhdNUADgGhAaIBowGkAaUB9QGn +Aq4BqYBQgIaAhYBNW2hlYWRlckNvbG9y0wAOAaIBrQGlAa8Cs4BQQjEA3ADEAA4AxQDGAMcAyQDKAMsA +zADNAM4AzwEZANEA0gK3ArgA0gK6AZYB4AK9Ar4Cv4AXgBYQSxAkgBaAiIBLgGARAZASCAIAABP///// +hrZA/1hBZmZpY2hlctUADgGhAaIBowGkAaUCwwGnAsQBqYBQgIuAioBNWWdyaWRDb2xvctMADgGiAa0B +pQGvAsmAUEQwLjUAXxAVdHJ1c3RlZEhvc3RzVGFibGVWaWV31AAOAKAAoQCiAKMBxgAfAs+AGoBYgAKA +jlpsYXlvdXRWaWV31AAOAKAAoQCiAtIAHwLUAtWAmoACgJCAmdgAqAAOAKkAqgCrAKwArQCuAK8AsALZ +AtoC2wC0ALUAr4ALgBiAkYCSEQEJCYANgAtfEBZ7ezU0NSwgMzc5fSwgezMyLCAzMn193QDEAA4AxQLh +AMYAxwDJAMoAywDMAM0AzgDPARkA0QDSAuQA0wLlAuYC5wDYAtQA2gEhAupdTlNOb3JtYWxJbWFnZYAX +gBaAlBCCgJiAk4AQgJASCERA/2YAQQBiAG8AdQB0ICbTAA4AMgLtAu4C7wLwXk5TUmVzb3VyY2VOYW1l +gJeAlYCWV05TSW1hZ2VaaWNvbi1hYm91dNIANwA4AvQC9aIC9QA7XxAQTlNDdXN0b21SZXNvdXJjZdIA +DgElASYA8oAiXxAPc2hvd0Fib3V0U2hlZXQ60gA3ADgC+gL7owL7AQEAO18QFU5TTmliQ29udHJvbENv +bm5lY3RvctQADgCgAKEAogCjAeAC/wEwgBqAYICcgEHUAA4BMgMCATQBNQMEAwUDBl8QEU5TT2JqZWN0 +Q2xhc3NOYW1lgECAoICfgJ3SAA4APgBGAwmAPaEDCoCeXxAUbnVtYmVyT2ZTZWxlY3RlZFJvd3PRAA4B +bYA/1AAOAKAAoQCiAKMDEAAfAxKAGoCigAKAp9cAqAAOAKkAqgCrAKwArgMUALADFgMXALMAtAMUgKOA +GICkgKUJgKPXAKgADgC4AKsAuQAyAK4DGwC7Ax0BEwMeAMADG4EBAoBdgQEEgQGugFyBAQJfEBZ7ezE4 +MywgNzR9LCB7Mjg4LCAxOH193QDEAA4AxQDGAMcAyADJAMoAywDMAM0AzgDPARkA0QDSANMA1ADVANID +JwDYAxAA2gDbANyAF4AWgBOAFoCmgBCAol8QKkFmZmljaGVyIGxlIHN0YXR1cyBkYW5zIGxhIGJhcnJl +IGRlcyBtZW51c15zdGF0dXNDaGVja2JveNQADgCgAKEAogCjAB8B4AHHgBqAAoBggF7UAA4AoAChAKIA +owCvAB8DNYAagAuAAoEBs9cAqAAOALgAqwC5AK0ArgC1AYoDOQGBAzoAtQM8gA2AuYEBvoEBv4ANgQHA +0gAOAD4ARgM/gD2kAKQC1ANCAdKACoCQgKyAsNgAqAAOAKkAqgCrAKwArQCuAK8AsANHA0gAswC0ALUA +r4ALgBiArYCuCYANgAtfEBZ7ezE4LCAzODd9LCB7MTE5LCAxOH193QDEAA4AxQDGAMcAyADJAMoAywDM +AM0AzgDPARkA0QDSANMA1ADVANIDUgDYA0IA2gDbANyAF4AWgBOAFoCvgBCArF8QEEFjdGl2ZXIgdGVs +ZXBvcnTdAKgADgNXAeUAqQDpALgAqwCtA1gArgNZA1oArwNcA10A2wNeANgDYAC9ALUAtACvALQDZV5O +U1RhYlZpZXdJdGVtc18QEU5TRHJhd3NCYWNrZ3JvdW5kXxAWTlNBbGxvd1RydW5jYXRlZExhYmVsc18Q +FU5TU2VsZWN0ZWRUYWJWaWV3SXRlbYALgQGxgLuAuoAQgLGADQmACwmAvNIADgA+AEYDaIA9oQHKgFnS +AA4APgBGA2yAPaIBxgNugFiAs9gAqAAOAKkAqgCrAKwArQCuAcoDcQNyA3MDdAC0ALUByoBZgLeAtIC1 +EQEiCYANgFlfEBR7ezE3LCAxfSwgezQyNywgMTR9fdgAxAAOAZEAygDLAMwAzgGSARkBkwGUA3wBlgNu +A38BmYBUgEyAtoBLgLMSAEIAAIBRbxBPAEcAbABpAHMAcwBlAHIALQBkAOkAcABvAHMAZQByACAAbABl +AHMAIABoAPQAdABlAHMAIABkAGkAcwBwAG8AbgBpAGIAbABlAHMAIABhAHUAdABvAHUAcgAgAGQAZQAg +AHYAbwB0AHIAZQAgAOkAYwByAGEAbgAgAHAAbwB1AHIAIABsAGUAcwAgAGMAbwBuAHQAcgD0AGwAZQBy +AC7SADcAOAODAYelAYcA+gD7APwAO18QFnt7MTAsIDMzfSwgezU0OSwgMzE5fX3SADcAOAOGAPujAPsA +/AA7XxAWe3sxMywgMTB9LCB7NTY5LCAzNjV9fdIADgA+AEYDioA9owNlA4wDjYC8gMCBAQDWAA4CTAD7 +A48BoQCiA5ADkQHKAdIBlAOVWU5TVGFiVmlld4C/gL2AWYCwgEyAvlZsYXlvdXRbRGlzcG9zaXRpb27S +ADcAOAOZA5qiA5oAO11OU1RhYlZpZXdJdGVt1QAOAPsDjwGhAKIDkAEQAdIBlAOggL+AwYCwgEyA/9UA +qAAOAKkAuACrACsBigOkA6UBgYAAgLmA/oDC0gAOAD4ARgOogD2hAQiAHdIADgA+AEYDrIA9qQOtAQQD +rwOwA7EDsgIJA7QDtYDEgByAyIDNgNGA1YBqgPWA+dcAqAAOAKkAqgCrAKwArgEIA3EDuQO6A7sAtAEI +gB2At4DFgMYRASQJgB1fEBd7ezE4MiwgMTAxfSwgezI4MiwgMTR9fdgAxAAOAZEAygDLAMwAzgGSARkB +kwGUA8IBlgOtA8UBmYBUgEyAx4BLgMQSEEIAAIBRbxA3AFAAbwB1AHIAIAByAGUAdABpAHIAZQByACAA +dQBuACAAaAD0AHQAZQAsACAAcwDpAGwAZQBjAHQAaQBvAG4AbgBlAHoAIABsAGUAIABlAHQAIABmAGEA +aQB0AGUAcwAgAHIAZQB0AG8AdQByAC7XAKgADgCpAKoAqwCsAK4BCACwA8sDzACzALQBCIAdgBiAyYDK +CYAdXxAXe3sxNzksIDI1MH0sIHsxNjQsIDMyfX3cAMQADgDFAMYAxwDJAMoAywDMAM0AzgDPARkA0QDS +ANMBHAPTA9QA2AOvANoBIQEigBeAFoDMgMuAEIDIXxAWQWZmaWNoZXIgbGUgY2VydGlmaWNhdNIADgEl +ASYA8oAi1wCoAA4AqQCqAKsArACuAQgAsAPdA94AswC0AQiAHYAYgM6AzwmAHV8QF3t7MTgzLCAyODZ9 +LCB7MTU4LCAxOH193QDEAA4AxQDGAMcAyADJAMoAywDMAM0AzgDPARkA0QDSANMA1ADVANID5wDYA7AA +2gDbANyAF4AWgBOAFoDQgBCAzV8QFkFjdGl2ZXIgbGUgY2hpZmZyZW1lbnTXAKgADgCpAKoAqwCsAK4B +CANxA+4D7wCzALQBCIAdgLeA0oDTCYAdXxAWe3syMywgMjg3fSwgezE0OCwgMTd9fdgAxAAOAZEAygDL +AMwAzgGSARkBkwGUA/YA2AOxA/kBmYBUgEyA1IAQgNESBEAAAIBRXUNoaWZmcmVtZW50IDrfEBIAqAAO +AKkD/QP+A/8BkQQABAEA6QQCAKsArAQDAK4EBAQFBAYBCAQIBAkECgGvBAsBlAQNAfUA2AQQA7sAtAEc +AQgEEwQUBBVbTlNQcm90b0NlbGxZTlNOdW1Sb3dzXk5TU2VsZWN0ZWRDZWxsW05TQ2VsbENsYXNzXxAV +TlNDZWxsQmFja2dyb3VuZENvbG9yWk5TQ2VsbFNpemVZTlNOdW1Db2xzXxASTlNJbnRlcmNlbGxTcGFj +aW5nXU5TTWF0cml4RmxhZ3NXTlNDZWxsc4AdgOaA1oDkgNiATIDjgIaAEIDhCYAdgOISRCggAIDXXxAW +e3sxODUsIDIzfSwgezI5OSwgNTh9fdIADgA+AEYEGYA9owQLBBsEHIDYgN2A390AxAAOAMUAxgDHAMgA +yQDKAMsAzADNAM4AzwQeANEEIADTANsEIQQgBCMA2AOyANoA2wDcE/////+EAf4AgBeA3IDagNyA2YAQ +gNVvECgATQBlACAAZABlAG0AYQBuAGQAZQByACAAcwBpACAAbAAnAGgA9AB0AGUAIABwAGUAdQB0ACAA +6gB0AHIAZQAgAGEAdQB0AG8AcgBpAHMA6dIADgDrAOwEKYAVgNtdTlNSYWRpb0J1dHRvbtIADgElASYA +8oAi3gDEAA4AxQDGAMcAyADJAMoAywDMAM0AzgDPBC4BGQDRBCAA0wDbBCEEIAQzANgDsgDaANsA3AEc +VU5TVGFngBeA3IDagNyA3oAQgNVvECgAUgBlAGoAZQB0AGUAcgAgAHMAaQAgAGwAJwBoAPQAdABlACAA +bgAnAGUAcwB0ACAAcABhAHMAIABkAGUAIABjAG8AbgBmAGkAYQBuAGMAZd4AxAAOAMUAxgDHAMgAyQDK +AMsAzADNAM4AzwQuARkA0QDSArcA2wQhANIEPADYA7ICvQDbANwA1IAXgBaA2oAWgOCAEIDVXxAYQWNj +ZXB0ZXIgYXV0b21hdGlxdWVtZW50WXsyOTksIDE4fVZ7NCwgMn3aAMQADgDGAMcAyADKAMsAzQDOAM8B +GQDRArcA2wQhBEUA2AK9ANsA3IAXgNqA5YAQVVJhZGlv0gA3ADgESQRKpQRKAPoA+wD8ADtYTlNNYXRy +aXjSAA4APgBGBE2APaUB8AI8AjQCFQH3gGOA64DvgGaAadIADgA+AEYEVYA9oQHggGBfEBV7ezEsIDE3 +fSwgezMzMywgMTA0fX3SADcAOARZBFqkBFoA+wD8ADtaTlNDbGlwVmlld9gAqARcAA4AqQCrBF0ArgRe +AgkCCQRhBGIBgQRjAgkEZVhOU1RhcmdldFhOU0FjdGlvbllOU1BlcmNlbnSAaoBqgO6A7IDtgGojP+2C +2CAAAABfEBl7ey0xMDAsIC0xMDB9LCB7MTUsIDE4MH19XF9kb1Njcm9sbGVyOtIANwA4BGkEaqUEagD6 +APsA/AA7Wk5TU2Nyb2xsZXLZAKgEXAAOAKkCLgCrBF0ArgReAgkCCQRhBG8BHAGBBGMCCQRygGqAaoDu +gPCA7YBqIz/vsfsgAAAAXxAZe3stMTAwLCAtMTAwfSwgezQ2MywgMTV9fdIADgA+AEYEdoA9oQH0gGVf +EBN7ezEsIDB9LCB7MzMzLCAxN319XxAYe3sxODUsIDExNn0sIHszMzUsIDEyMn190gA3ADgEewR8pAR8 +APsA/AA7XE5TU2Nyb2xsVmlld9cAqAAOAKkAqgCrAKwArgEIA3EEgASBA7sAtAEIgB2At4D2gPcJgB1f +EBV7ezE0LCA2NH0sIHsxNTcsIDE3fX3YAMQADgGRAMoAywDMAM4BkgEZAZMBlASIANgDtAP5AZmAVIBM +gPiAEID1gFFvEBUARABlAG0AYQBuAGQAZQAgAGQAZQAgAGMAbwBuAHQAcgD0AGwAZQAgADrXAKgADgCp +AKoAqwCsAK4BCANxBJAEkQCzALQBCIAdgLeA+oD7CYAdXxAVe3s4LCAyMjF9LCB7MTYzLCAxN3192ADE +AA4BkQDKAMsAzADOAZIBGQGTAZQEmADYA7UD+QGZgFSATID8gBCA+YBRbxAUAEgA9AB0AGUAcwAgAGQA +ZQAgAGMAbwBuAGYAaQBhAG4AYwBlACAAOlp7NTQ5LCAzMTl9bxAUAFIA6QBnAGwAYQBnAGUAcwAgAGQA +ZQAgAHMA6QBjAHUAcgBpAHQA6dYADgJMAPsDjwGhAKIDkAShAxsB0gGUBKWAv4EBAYEBAoCwgEyBAbBX +b3B0aW9uc9UAqAAOAKkAuACrACsBigSqBKsBgYAAgLmBAa+BAQPSAA4APgBGBK6APaEDFICj0gAOAD4A +RgSygD2vEBYEswS0BLUEtgMQBLgEuQS6BLsEvAS9BL4EvwTABMEEwgTDBMQExQTGBMcEyIEBBYEBCYEB +DYEBEYCigQEVgQEZgQEdgQEhgQElgQFDgQFHgQFLgQFPgQFTgQGAgQGRgQGVgQGagQGegQGmgQGq1wCo +AA4AqQCqAKsArACuAxQAsATMBM0AswC0AxSAo4AYgQEGgQEHCYCjXxAXe3syMTEsIDEzMn0sIHsxNzEs +IDE4fX3dAMQADgDFAMYAxwDIAMkAygDLAMwAzQDOAM8BGQDRANIA0wDUANUA0gTWANgEswDaANsA3IAX +gBaAE4AWgQEIgBCBAQVvEBgAUwBlAHUAbABlAG0AZQBuAHQAIABzAGkAIABpAG4AZgDpAHIAaQBlAHUA +cgAgAODXAKgADgCpAKoAqwCsAK4DFANxBN0E3gCzALQDFICjgLeBAQqBAQsJgKNfEBZ7ezIzLCAxODF9 +LCB7MTQ4LCAxN3192ADEAA4BkQDKAMsAzADOAZIBGQGTAZQE5QDYBLQD+QGZgFSATIEBDIAQgQEJgFFc +VHJhbnNmZXJ0cyA61wCoAA4AqQCqAKsArACuAxQDcQTtBO4AswC0AxSAo4C3gQEOgQEPCYCjXxAVe3sx +MSwgMTd9LCB7MTYwLCAxN3192ADEAA4BkQDKAMsAzADOAZIBGQGTAZQE9QDYBLUD+QGZgFSATIEBEIAQ +gQENgFFuAE0AaQBzAGUAcwAgAOAAIABqAG8AdQByACAAOtcAqAAOAKkAqgCrAKwArgMUA3EE/QT+ALMA +tAMUgKOAt4EBEoEBEwmAo18QFnt7NDMsIDI4N30sIHsxMjgsIDE3fX3YAMQADgGRAMoAywDMAM4BkgEZ +AZMBlAUFANgEtgP5AZmAVIBMgQEUgBCBARGAUVpDb21tdXRlciA61wCoAA4AqQCqAKsArACuAxQAsAUN +BQ4AswC0AxSAo4AYgQEWgQEXCYCjXxAXe3sxODMsIDE4MH0sIHsyOTQsIDE4fX3dAMQADgDFAMYAxwDI +AMkAygDLAMwAzQDOAM8EHgDRANIA0wDUANUA0gUXANgEuADaANsA3IAXgBaAE4AWgQEYgBCBARVvECsA +RwBsAGkAcwBzAGUAcgAtAGQA6QBwAG8AcwBlAHIAIABkAGUAcwAgAGYAaQBjAGgAaQBlAHIAcwAgAGUA +bgB0AHIAZQAgAGwAZQBzACAATQBhAGMAc9cAqAAOAKkAqgCrAKwArgMUALAFHgUfALMAtAMUgKOAGIEB +GoEBGwmAo18QF3t7MjExLCAxMDh9LCB7MTI4LCAxOH193QDEAA4AxQDGAMcAyADJAMoAywDMAM0AzgDP +ARkA0QDSANMA1ADVANIFKADYBLkA2gDbANyAF4AWgBOAFoEBHIAQgQEZXxAQU2V1bGVtZW50IGF2ZWMg +OtcAqAAOAKkAqgCrAKwArgMUALAFLwUwALMAtAMUgKOAGIEBHoEBHwmAo18QF3t7MTgzLCAxNTZ9LCB7 +MjExLCAxOH193QDEAA4AxQDGAMcAyADJAMoAywDMAM0AzgDPBB4A0QDSANMA1ADVANIFOQDYBLoA2gDb +ANyAF4AWgBOAFoEBIIAQgQEdXxAdU3luY2hyb25pc2VyIGxlIHByZXNzZS1wYXBpZXLXAKgADgCpAKoA +qwCsAK4DFACwBUAFQQCzALQDFICjgBiBASKBASMJgKNfEBd7ezE4MywgMjg2fSwgezE5OSwgMTh9fd0A +xAAOAMUAxgDHAMgAyQDKAMsAzADNAM4AzwEZANEA0gDTANQA1QDSBUoA2AS7ANoA2wDcgBeAFoATgBaB +ASSAEIEBIV8QGUNvbW11dGVyIHNldWxlbWVudCBhdmVjIDrXAKgADgCpAKoAqwCsAK4DFAVQBVEFUgCz +ALQDFICjgQFCgQEmgQEnCYCjXxAXe3szNDIsIDEwM30sIHsxMjksIDI2fX3fEBIAxAVXBVgAxQDGAA4A +xwDJAMsFWQDMBVoFWwVcAM0AzgVdAM8FXgC0ARwA0gK3BWEBHAViANgFZAS8Aa8AtAC0Ar0FaAVpBWpf +EBpOU01lbnVJdGVtUmVzcGVjdEFsaWdubWVudF8QD05TQXJyb3dQb3NpdGlvblpOU01lbnVJdGVtXxAP +TlNQcmVmZXJyZWRFZGdlXxASTlNVc2VzSXRlbUZyb21NZW51XU5TQWx0ZXJzU3RhdGVWTlNNZW51E/// +//+EQf5ACYAWgQFBgQEogBCBASmBASUJCREIAIEBKhIGgkD/0gAOASUBJgDygCLcBFwADgVuBW8FcAVx +BXIFcwVdBF0ELgV0BVIFdgV3BXgA0gV6BXsFfAVpBX4FfwEcV05TVGl0bGVfEBFOU0tleUVxdWl2TW9k +TWFza1pOU0tleUVxdWl2XU5TTW5lbW9uaWNMb2NZTlNPbkltYWdlXE5TTWl4ZWRJbWFnZVdOU1N0YXRl +gQEngQExgQErEgAQAACAFhJ/////gQEsgQEugQEqgQEwEBTTAA4FbgWBBYIFgwWEW05TTWVudUl0ZW1z +gQFAgQEygQEzaiMYACAAQwBvAG0AbQBhAG4AZABl0wAOADIC7QLuAu8FiYCXgJWBAS1fEA9OU01lbnVD +aGVja21hcmvTAA4AMgLtAu4C7wWOgJeAlYEBL18QEE5TTWVudU1peGVkU3RhdGVfEBFfcG9wVXBJdGVt +QWN0aW9uOtIANwA4BZIFWaIFWQA70gAOASUBJgWVgCJaT3RoZXJWaWV3c9IADgA+AEYFmIA9pQVkBZoF +mwWcBZ2BASmBATSBATeBATqBAT3bBFwADgVuBW8FcAVxBXIFcwVdBF0ELgVSBXYFoQV4ANIFegV7BXwF +aQWmBaeBASeBATGBATWAFoEBLIEBLoEBKoEBNhATaCMlACAATwBwAHQAaQBvAG7bBFwADgVuBW8FcAVx +BXIFcwVdBF0ELgVSBXYFrAV4ANIFegV7BXwFaQWxBbKBASeBATGBATiAFoEBLIEBLoEBKoEBORASaiMD +ACAAQwBvAG4AdAByAPQAbABl2wRcAA4FbgVvBXAFcQVyBXMFXQRdBC4FUgV2BbcFeADSBXoFewV8BWkF +vAW9gQEngQExgQE7gBaBASyBAS6BASqBATwQEWch5wAgAFMAaABpAGYAdNsEXAAOBW4FbwVwBXEFcgVz +BV0EXQQuBVIFdgXCBXgA0gV6BXsFfAVpBccFyIEBJ4EBMYEBPoAWgQEsgQEugQEqgQE/EBBvEBgh6gAg +AFYAZQByAHIAbwB1AGkAbABsAGEAZwBlACAAbQBhAGoAdQBzAGMAdQBsAGXSADcAOAXLBV2iBV0AO9IA +NwA4Bc0FzqYFzgXPAPUA9gCqADtfEBFOU1BvcFVwQnV0dG9uQ2VsbF5OU01lbnVJdGVtQ2VsbNIANwA4 +BdEF0qYF0gD5APoA+wD8ADtdTlNQb3BVcEJ1dHRvbtcAqAAOAKkAqgCrAKwArgMUA3EF1gXXALMAtAMU +gKOAt4EBRIEBRQmAo18QFnt7MzcwLCAyNjB9LCB7MzQsIDE3fX3YAMQADgGRAMoAywDMAM4BkgEZAZMB +lAXeAZYEvQXhAZmAVIBMgQFGgEuBAUMSEEAAAIBRVUNvdXJ01wCoAA4AqQCqAKsArACuAxQAsAXnBegA +swC0AxSAo4AYgQFIgQFJCYCjXxAXe3sxODMsIDIxNH0sIHsyNTcsIDE4fX3dAMQADgDFAMYAxwDIAMkA +ygDLAMwAzQDOAM8BGQDRANIA0wDUANUA0gXxANgEvgDaANsA3IAXgBaAE4AWgQFKgBCBAUdvECcARQBz +AHMAYQB5AGUAcgAgAGQAZQAgAHIA6QB2AGUAaQBsAGwAZQByACAAbABlAHMAIABNAGEAYwBzACAAZQBu +ACAAdgBlAGkAbABsAGXXAKgADgCpAKoAqwCsAK4DFACwBfgF+QCzALQDFICjgBiBAUyBAU0JgKNfEBd7 +ezE4MywgMjYyfSwgezE4NCwgMTh9fd0AxAAOAMUAxgDHAMgAyQDKAMsAzADNAM4AzwEZANEA0gDTANQA +1QDSBgIA2AS/ANoA2wDcgBeAFoATgBaBAU6AEIEBS28QGABDAG8AbQBtAHUAdABlAHIAIABhAHYAZQBj +ACAAdQBuACAAZADpAGwAYQBpACAAOtcAqAAOAKkAqgCrAKwArgMUALAGCQYKALMAtAMUgKOAGIEBUIEB +UQmAo18QF3t7MTgzLCAyMzh9LCB7MjY5LCAxOH193QDEAA4AxQDGAMcAyADJAMoAywDMAM0AzgDPARkA +0QDSANMA1ADVANIGEwDYBMAA2gDbANyAF4AWgBOAFoEBUoAQgQFPbxAjAEMAbwBtAG0AdQB0AGUAcgAg +AGEAcAByAOgAcwAgAHUAbgBlACAAZABvAHUAYgBsAGUAIABpAG0AcAB1AGwAcwBpAG8AbtcAqAAOAKkA +qgCrAKwArgMUA3EGGgYbALMAtAMUgKOAt4EBVIEBVQmAo18QFnt7Mzg4LCAxMzF9LCB7NTgsIDIyfX3a +AMQADgGRAMsAzAYgAM4DWAYhAZIGIgGTBiQA2ATBBicGKAC0BioGK1tOU0Zvcm1hdHRlcl8QE05TUGxh +Y2Vob2xkZXJTdHJpbmcT/////5Rx/kGAVIEBfIAQgQFTgQFWEgRABAAJgQF7gQF+3xARAA4GLQYuBi8G +MAYxBjIGMwY0BjUGNgY3BjgGOQY6BjsGPAY9Bj4GPwZAACsGQgZDBkQGRQZGALQGSAArBkoGSwZMBkxW +TlMubmlsWk5TLmRlY2ltYWxWTlMubmFuW05TLnJvdW5kaW5nV05TLnplcm9fEBBOUy5uZWdhdGl2ZWF0 +dHJzVk5TLm1heF1OUy5hdHRyaWJ1dGVzXxARTlMucG9zaXRpdmVmb3JtYXRfEA9OUy5hbGxvd3NmbG9h +dHNfEBFOUy5uZWdhdGl2ZWZvcm1hdF8QEE5TLnBvc2l0aXZlYXR0cnNbTlMudGhvdXNhbmRWTlMubWlu +XE5TLmxvY2FsaXplZF8QD05TLmhhc3Rob3VzYW5kc4EBeoEBdoEBbYEBeIAAgQFngQFsgQFygQFXgQFm +CYEBaIAAgQF0gQFkCAjTAA4GTwA+BlAGUQZeV05TLmtleXOBAXWsBlIGUwZUBlUGVgZXBlgGWQZaBlsG +XAZdgQFYgQFZgQFagQFbgQFcgQFdgQFegQFfgQFggQFhgQFigQFjrAZLBkYGQgZDBj8GZAZlBmYGRAZo +BkgGSoEBZIEBZoEBZ4EBbIEBbYEBboEBb4EBcYEBcoEBc4EBaIEBdFdtaW5pbXVtXnBvc2l0aXZlRm9y +bWF0XxAXYXR0cmlidXRlZFN0cmluZ0Zvclplcm9fEB90ZXh0QXR0cmlidXRlc0Zvck5lZ2F0aXZlVmFs +dWVzXxAQZGVjaW1hbFNlcGFyYXRvcl8QEWZvcm1hdHRlckJlaGF2aW9yVmxvY2FsZVxhbGxvd3NGbG9h +dHNXbWF4aW11bV8QFXVzZXNHcm91cGluZ1NlcGFyYXRvcl5uZWdhdGl2ZUZvcm1hdF8QEWdyb3VwaW5n +U2VwYXJhdG9y1wAOBngGeQZ6BnsGfAZ9Bn4GTADbANQA2waABkxaTlMuY29tcGFjdFtOUy5leHBvbmVu +dF5OUy5tYW50aXNzYS5ib1lOUy5sZW5ndGhbTlMubWFudGlzc2FbTlMubmVnYXRpdmWBAWUITxAQAAAA +AAAAAAAAAAAAAAAAAAjSADcAOAaDBoSiBoQAO18QGk5TRGVjaW1hbE51bWJlclBsYWNlaG9sZGVyUjBL +0wAOBocBKgaIBokGSFxOU0F0dHJpYnV0ZXOBAWuBAWmBAWhRMNMADgZPAD4GjQaOBo+BAWqgoNIANwA4 +BpEGkqIGkgA7XE5TRGljdGlvbmFyedIANwA4BpQGlaIGlQA7XxASTlNBdHRyaWJ1dGVkU3RyaW5n0wAO +Bk8APgaNBpgGmYEBaqCgYQCgEQPo0gAOBp0GngDSXU5TLmlkZW50aWZpZXKBAXCAFtIANwA4BqEGoqIG +ogA7WE5TTG9jYWxl1wAOBngGeQZ6BnsGfAZ9Bn4AtADbANQA1AamBkyBAWUJTxAQAABAAAAAAAAAAAAA +AAAAAAhRLNIANwA4BqoGq6MGqwaSADtfEBNOU011dGFibGVEaWN0aW9uYXJ50gAOASoGiAaugQFrgQF3 +0wAOBocBKgaIBokGsoEBa4EBaYEBeVNOYU7SADcAOAa1BrajBrYGIAA7XxARTlNOdW1iZXJGb3JtYXR0 +ZXJWdGFpbGxl1QAOAaEBogGjAaQBpQH1AacGuwGpgFCAhoEBfYBNXxATdGV4dEJhY2tncm91bmRDb2xv +ctUADgGhAaIBowGkAaUBtQGnBsEBqYBQgFOBAX+ATVl0ZXh0Q29sb3LXAKgADgCpAKoAqwCsAK4DFAVQ +BscGyACzALQDFICjgQFCgQGBgQGCCYCjXxAXe3szODUsIDI4MX0sIHsxMjksIDI2fX3fEBIAxAVXBVgA +xQDGAA4AxwDJAMsFWQDMBVoFWwVcAM0AzgVdAM8FXgC0ARwA0gK3BWEBHAbQANgG0gTCAa8AtAC0Ar0F +aAbWBWoJgBaBAUGBAYOAEIEBhIEBgAkJgQGF0gAOASUBJgDygCLcBFwADgVuBW8FcAVxBXIFcwVdBF0E +LgV0BsgFdgV3BXgA0gV6BXsFfAbWBuEFfwEcgQGCgQExgQErgBaBASyBAS6BAYWBAYbTAA4FbgWBBYIG +5AblgQFAgQGHgQGI0gAOASUBJgWVgCLSAA4APgBGBuqAPaUG0gbsBu0G7gbvgQGEgQGJgQGLgQGNgQGP +2wRcAA4FbgVvBXAFcQVyBXMFXQRdBC4GyAV2BaEFeADSBXoFewV8BtYG+AWngQGCgQExgQE1gBaBASyB +AS6BAYWBAYrbBFwADgVuBW8FcAVxBXIFcwVdBF0ELgbIBXYFrAV4ANIFegV7BXwG1gcBBbKBAYKBATGB +ATiAFoEBLIEBLoEBhYEBjNsEXAAOBW4FbwVwBXEFcgVzBV0EXQQuBsgFdgW3BXgA0gV6BXsFfAbWBwoF +vYEBgoEBMYEBO4AWgQEsgQEugQGFgQGO2wRcAA4FbgVvBXAFcQVyBXMFXQRdBC4GyAV2BcIFeADSBXoF +ewV8BtYHEwXIgQGCgQExgQE+gBaBASyBAS6BAYWBAZDXAKgADgCpAKoAqwCsAK4DFACwBxcHGACzALQD +FICjgBiBAZKBAZMJgKNfEBZ7ezE4MywgNTB9LCB7MzQ5LCAxOH193QDEAA4AxQDGAMcAyADJAMoAywDM +AM0AzgDPARkA0QDSANMA1ADVANIHIQDYBMMA2gDbANyAF4AWgBOAFoEBlIAQgQGRbxA0AEEAZgBmAGkA +YwBoAGUAcgAgAHUAbgBlACAAZgBlAG4A6gB0AHIAZQAgAHQAcgBhAG4AcwBsAHUAYwBpAGQAZQAgAHAA +ZQBuAGQAYQBuAHQAIABsAGUAIABjAG8AbgB0AHIA9ABsAGXXAKgADgCpAKoAqwCsAK4DFACwBygHKQCz +ALQDFICjgBiBAZaBAZcJgKNfEBR7ezQwNywgN30sIHs4NiwgMzJ9fdwAxAAOAMUAxgDHAMkAygDLAMwA +zQDOAM8BGQDRANIA0wEcBzAHMQDYBMQA2gEhASKAF4AWgQGZgQGYgBCBAZVoAFYA6QByAGkAZgBpAGUA +ctIADgElASYA8oAi1wCoAA4AqQCqAKsArACuAxQDcQc6BzsAswC0AxSAo4C3gQGbgQGcCYCjXxAVe3sz +NSwgNzV9LCB7MTM2LCAxN3192ADEAA4BkQDKAMsAzADOAZIBGQGTAZQHQgDYBMUD+QGZgFSATIEBnYAQ +gQGagFFYU3RhdHV0IDrXAKgADgCpAKoAqwCsAK4DFAdJB0oHSwCzALQDFICjgQGlgQGfgQGgCYCjXxAW +e3s0MDksIDI2MX0sIHs4MywgMTV9fd4HUADEAA4HUQdSAMoAywDMB1MHVADOB1UHVgdXB1gBGQdZANsB +HAdaB1sExgddB14HXwZMB2EGTFdOU1ZhbHVlXxATTlNOdW1iZXJPZlRpY2tNYXJrc18QEk5TVGlja01h +cmtQb3NpdGlvblpOU01heFZhbHVlWk5TTWluVmFsdWVaTlNWZXJ0aWNhbF1OU0FsdEluY1ZhbHVlXxAa +TlNBbGxvd3NUaWNrTWFya1ZhbHVlc09ubHkjP+AAAAAAAACBAaSBAaGBAaKBAZ4jP/AAAAAAAAAjP7mZ +mZmZmZoSAAIAAAgjAAAAAAAAAAAI0gAOASUBJgDygCLUAA4A3wDgAOEA4gdnB2gFyIASI0AoAAAAAAAA +gQGjWUhlbHZldGljYdIANwA4B2sHbKQHbAD2AKoAO1xOU1NsaWRlckNlbGzSADcAOAduB2+lB28A+gD7 +APwAO1hOU1NsaWRlctcAqAAOAKkAqgCrAKwArgMUA3EHcwd0ALMAtAMUgKOAt4EBp4EBqAmAo18QFnt7 +NDk3LCAyNjN9LCB7MzEsIDE0fX3YAMQADgGRAMoAywDMAM4BkgEZAZMBlAd7AZYExwXhAZmAVIBMgQGp +gEuBAaaAUVRMb25n1wCoAA4AqQCqAKsArACuAxQAsAeDB4QAswC0AxSAo4AYgQGrgQGsCYCjXxAWe3sx +ODMsIDE2fSwgezIyNCwgMTh9fd0AxAAOAMUAxgDHAMgAyQDKAMsAzADNAM4AzwEZANEA0gDTANQA1QDS +B40A2ATIANoA2wDcgBeAFoATgBaBAa2AEIEBqm8QIABWAOkAcgBpAGYAaQBlAHIAIABsAGEAIAB2AGUA +cgBzAGkAbwBuACAAYQB1ACAAZADpAG0AYQByAHIAYQBnAGVXT3B0aW9uc9IANwA4B5MDj6QDjwD7APwA +O1p7NTk1LCA0Mzh9VHZpZXfUAA4AoAChAKIC0gAfBMQHmoCagAKBAZWBAbVdY2hlY2tWZXJzaW9uOtQA +DgCgAKEAogCjA68AHweggBqAyIACgQG3XxAVc2hvd0NlcnRpZmljYXRlQnV0dG9u1AAOAKAAoQCiAKMH +pAAfB6aAGoEBuYACgQHF3AeoAA4HqQeqB6sHrAetB64HrwewB7EHsgC6B7QHtQe2B7cHuAe5B7oA1AH+ +B7sHvFxOU1dpbmRvd1ZpZXdcTlNTY3JlZW5SZWN0XU5TV2luZG93VGl0bGVZTlNXVEZsYWdzXU5TV2lu +ZG93Q2xhc3NcTlNXaW5kb3dSZWN0WU5TTWF4U2l6ZV8QD05TV2luZG93QmFja2luZ18QEU5TV2luZG93 +U3R5bGVNYXNrWU5TTWluU2l6ZVtOU1ZpZXdDbGFzc4CqgQHEgQHBgQG7EkB4AACBAbyBAbqBAcOBAcKB +Ab1fEBh7ezUxNywgNDUwfSwgezU5NSwgNDI5fX1fEBU8PCBkbyBub3QgbG9jYWxpemUgPj7SAA4BJQEm +B8GAIlRWaWV30gAOAD4ARgfEgD2hAK+AC1p7NTk1LCA0Mjl9XxAWe3swLCAwfSwgezE2ODAsIDEwMjh9 +fV17MjI0LjY2NCwgMzJ9XxAaezMuNDAyODJlKzM4LCAzLjQwMjgyZSszOH3SADcAOAfLB8yiB8wAO18Q +EE5TV2luZG93VGVtcGxhdGVXX3dpbmRvd9QADgCgAKEAogCjAB8H0QHHgBqAAoEBx4Be3AeoAA4HqQeq +B6sHrAetB64HrwewB7EHsgF8B7QH1gDSB9gH2QfaB9sA1AEcB9wH3YBGgQHEgQH5gBYScHgAAIEByYEB +yIEB+4EB+oEByl8QGHt7MTk0LCAzNzV9LCB7Mjk1LCAzMDd9fV8QD1RQQ2xvc2luZ1dpbmRvd9IADgEl +ASYHwYAi0gAOAD4ARgfkgD2nB+UH5gF3B+gH6QfqB+uBAcyBAduAQ4EB4YEB5oEB7YEB8toAqAAOAKkB +MwCqB+0AqwCsAK0ArgF8B+8H8AC0B/IH8wGBALQBgwF8W05TRHJhZ1R5cGVzgEaBAdqBAdUJgQHWgQHN +CYBIgEbSAA4APgA/B/mAB6cH+gf7B/wH/Qf+B/8IAIEBzoEBz4EB0IEB0YEB0oEB04EB1F8QGUFwcGxl +IFBERiBwYXN0ZWJvYXJkIHR5cGVfEBlBcHBsZSBQTkcgcGFzdGVib2FyZCB0eXBlXxAjQ29yZVBhc3Rl +Ym9hcmRGbGF2b3JUeXBlIDB4NTA0RTQ3NjZfEBVOU0ZpbGVuYW1lc1Bib2FyZFR5cGVfEDFOZVhUIEVu +Y2Fwc3VsYXRlZCBQb3N0U2NyaXB0IHYxLjIgcGFzdGVib2FyZCB0eXBlXxAeTmVYVCBUSUZGIHY0LjAg +cGFzdGVib2FyZCB0eXBlXxAaQXBwbGUgUElDVCBwYXN0ZWJvYXJkIHR5cGVfEBd7ezIwLCAxNTl9LCB7 +MjU1LCAxMjh9fdgAxAAOCAoAyggLCAwAzggNCA4IDwDbCBAA2wDbCBEGTFdOU1N0eWxlV05TQWxpZ25X +TlNTY2FsZVpOU0FuaW1hdGVzEiAB/gCBAdmBAdcSAgAAAAjTAA4AMgLtAu4C7wgWgJeAlYEB2Fh0ZWxl +cG9ydNIANwA4CBkIGqMIGgCqADtbTlNJbWFnZUNlbGzSADcAOAgcCB2lCB0A+gD7APwAO1tOU0ltYWdl +Vmlld9gAqAAOAKkAqgCrAKwArQCuAXwDcQghCCIBgQC0AYMBfIBGgLeBAdyBAd0JgEiARl8QFnt7MTcs +IDEyOX0sIHsyNjEsIDIyfX3YAMQADgGRAMoAywDMAM4BkgEZAZMBlAgqCCsH5gGYAZmAVIBMgQHegQHf +gQHbgFHUAA4A3wDgAOEA4ggwCDEFyIASI0AyAAAAAAAAgQHgXxARTHVjaWRhR3JhbmRlLUJvbGTYAKgA +DgCpAKoAqwCsAK0ArgF8A3EINgg3AYEAtAGDAXyARoC3gQHigQHjCYBIgEZfEBV7ezEyLCA0M30sIHsy +NzQsIDM5fX3YAMQADgGRAMoAywDMAM4BkgEZAZMBlAg/CEAH6AhCAZmAVIBMgQHkgQHlgQHhEgBAAACA +UW8QawCpACAAMgAwADAAMwAtADIAMAAwADgAIABhAGIAeQBzAHMAbwBmAHQACgBCAGEAcwDpACAAcwB1 +AHIAIAB1AG4AZQAgAGkAZADpAGUAIABkAGUAIABEAGEAdgBlACAAUwBhAGcAIABlAHQAIABKAGUAcgBl +AG0AeQAgAFEAdQBpAG4AbgAuAAoAUgBlAG0AZQByAGMAaQBlAG0AZQBuAHQAcwAgAOAAIABNAGEAbgB1 +ADAAMgAsACAASgB1AGwAaQBlAG4AIABlAHQAIABQAGEAdQBsAC7UAA4A3wDgAOEA4ghHAOQISYASI0Ak +AAAAAAAAgBERCxvaAKgADgCpAXsAqgCrAKwArQAyAK4BfAF9CE0BfwhPCFAAtAGDCFMBfIBGgFWBAeiA +RYEB6REBAgmASIEB54BGXxAPVFBMaW5rVGV4dEZpZWxkXxATe3sxMiwgOH0sIHs5OSwgMTd9fdoAxAAO +AZEAygDLAMwAzgNYBiEBkghYAZMBlAhbCFwH6QXhALQIXwGZEgQh/gGAVIBMgQHqgQHrgQHmCYEB7IBR +bxAPJ6QAIABzAGkAdABlACAAaQBuAHQAZQByAG4AZQB01AAOAN8A4ADhAOIBnQDkBciAEoARXxAcaHR0 +cDovL3RlbGVwb3J0LmFieXNzb2Z0LmNvbdoAqAAOAKkBewCqAKsArACtADIArgF8AX0IaQF/CGsIUAC0 +AYMIUwF8gEaAVYEB7oBFgQHvCYBIgQHngEZfEBR7ezExOSwgOH0sIHs2OSwgMTd9fdkAxAAOAZEAygDL +AMwAzgYhAZIBGQGTAZQIdAhcB+oF4Qh3AZmAVIBMgQHwgQHrgQHtgQHxgFFoJ6QAIABmAG8AcgB1AG0A +c18QMWh0dHA6Ly93d3cuYWJ5c3NvZnQuY29tL3NvZnR3YXJlL3RlbGVwb3J0L2ZvcnVtcy/aAKgADgCp +AXsAqgCrAKwArQAyAK4BfAF9CH4BfwiACFAAtAGDCFMBfIBGgFWBAfOARYEB9AmASIEB54BGXxAUe3sy +MDAsIDh9LCB7OTIsIDE3fX3ZAMQADgGRAMoAywDMAM4GIQGSARkBkwGUCIkIXAfrBeEIjAGZgFSATIEB +9YEB64EB8oEB9oBRbiekACAAZgBhAGkAcgBlACAAdQBuACAAZABvAG5fEK1odHRwczovL3d3dy5wYXlw +YWwuY29tL2NnaS1iaW4vd2Vic2NyP2NtZD1feGNsaWNrJmJ1c2luZXNzPWp1bCU0MG1hYyUyZWNvbSZp +dGVtX25hbWU9dGVsZXBvcnQmbm9fc2hpcHBpbmc9MCZub19ub3RlPTAmdGF4PTAmY3VycmVuY3lfY29k +ZT1VU0QmY2hhcnNldD1VVEYlMmQ4JmNoYXJzZXQ9VVRGJTJkOFp7Mjk1LCAzMDd9WnsyMTMsIDEyOX3U +AA4AoAChAKIAowNCAB8IloAagKyAAoEB/V8QEmFjdGl2YXRpb25DaGVja2JveNQADgCgAKEAogCjAcYA +HwicgBqAWIACgQH/XxAPX2luaXRpYWxLZXlWaWV31AAOAKAAoQCiAKMBxgAfCKKAGoBYgAKBAgFcX2xh +c3RLZXlWaWV31AAOAKAAoQCiAKMEvwAfCKiAGoEBS4ACgQIDXxAVZGVsYXllZFN3aXRjaENoZWNrYm94 +1AAOAKAAoQCiAtIAHwEECK6AmoACgByBAgVfEBdzaG93Q2VydGlmaWNhdGVDaG9vc2VyOtQADgCgAKEA +ogCjBLsAHwi0gBqBASGAAoECB18QEnJlcXVpcmVLZXlDaGVja2JveNQADgCgAKEAogCjAB8B4Ai6gBqA +AoBggQIJWmRhdGFTb3VyY2XUAA4AoAChAKIAowAfAdIBx4AagAKAsIBe1AAOAKAAoQCiAKMH0QAfCMWA +GoEBx4ACgQIMW2Fib3V0V2luZG931AAOAKAAoQCiAtIAHwOvCMuAmoACgMiBAg5fEBZzaG93Q2VydGlm +aWNhdGVWaWV3ZXI61AAOAKAAoQCiAKMBxgAfCNGAGoBYgAKBAhBdX2ZpcnN0S2V5Vmlld9QADgCgAKEA +ogCjBLoAHwjXgBqBAR2AAoECEl8QF3NoYXJlUGFzdGVib2FyZENoZWNrYm941wAOAKAI2gjbAKEAogjc +CN0BLwjfCOADQgjiANRZTlNLZXlQYXRoWU5TQmluZGluZ18QHE5TTmliQmluZGluZ0Nvbm5lY3RvclZl +cnNpb26BAheAJYECFoECFYCsgQIUXxAXdmFsdWU6IHNlbGVjdGlvbi5hY3RpdmVVdmFsdWVfEBBzZWxl +Y3Rpb24uYWN0aXZl0gA3ADgI5wjoowjoAQEAO18QFU5TTmliQmluZGluZ0Nvbm5lY3RvctcADgCgCNoI +2wChAKII3AjdAS8I3wjtBL8I7wDUgQIXgCWBAhaBAhqBAUuBAhlfEBllbmFibGVkOiBzZWxlY3Rpb24u +YWN0aXZlV2VuYWJsZWTXAA4AoAjaCNsAoQCiCNwI3QEvCPUI7QS5CPgA1IECF4AlgQIdgQIagQEZgQIc +XxAoZW5hYmxlZDogc2VsZWN0aW9uLnByZWZzLnNoYXJlUGFzdGVib2FyZF8QH3NlbGVjdGlvbi5wcmVm +cy5zaGFyZVBhc3RlYm9hcmTZAA4AoAjaCNsI/AChAKII/QjcCN0BLwjfCQEAZgS5CQQJBQDUXxATTlNQ +cmV2aW91c0Nvbm5lY3RvcllOU09wdGlvbnOBAheAJYECFoECIIECG4EBGYECH4ECIV8QGmVuYWJsZWQy +OiBzZWxlY3Rpb24uYWN0aXZlWGVuYWJsZWQy0wAOBk8APgaNCQoJD4EBaqQJCwkMCQ0JDoECIoECI4EC +JIECJaQJEAkQCRAJEIECJoECJoECJoECJl8QEU5TTnVsbFBsYWNlaG9sZGVyXxAaTlNOb3RBcHBsaWNh +YmxlUGxhY2Vob2xkZXJfEBhOU05vU2VsZWN0aW9uUGxhY2Vob2xkZXJfEBtOU011bHRpcGxlVmFsdWVz +UGxhY2Vob2xkZXIT///////////XAA4AoAjaCNsAoQCiCNwI3QEvCRwI4ATICR8A1IECF4AlgQIpgQIV +gQGqgQIoXxAndmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5hdXRvY2hlY2tWZXJzaW9uXxAgc2VsZWN0aW9u +LnByZWZzLmF1dG9jaGVja1ZlcnNpb27XAA4AoAjaCNsAoQCiCNwI3QEvCSUI4ACkCSgA1IECF4AlgQIs +gQIVgAqBAitfECN2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmFsbG93Q29udHJvbF8QHHNlbGVjdGlvbi5w +cmVmcy5hbGxvd0NvbnRyb2zXAA4AoAjaCNsAoQCiCNwI3QEvCS4I4ATGCTEA1IECF4AlgQIvgQIVgQGe +gQIuXxAidmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5zd2l0Y2hEZWxheV8QG3NlbGVjdGlvbi5wcmVmcy5z +d2l0Y2hEZWxhedcADgCgCNoI2wChAKII3AjdAS8I3wjtAeAI7wDUgQIXgCWBAhaBAhqAYIECGdcADgCg +CNoI2wChAKII3AjdAS8I3wjtAKQI7wDUgQIXgCWBAhaBAhqACoECGdcADgCgCNoI2wChAKII3AjdAS8J +RQlGBMIJSADUgQIXgCWBAjWBAjSBAYCBAjNfEClzZWxlY3RlZFRhZzogc2VsZWN0aW9uLnByZWZzLnN3 +aXRjaEtleVRhZ1tzZWxlY3RlZFRhZ18QHHNlbGVjdGlvbi5wcmVmcy5zd2l0Y2hLZXlUYWfYAA4AoAja +CNsAoQCiCP0I3AjdAS8JTwjgBMMJUglTANSBAheAJYECOIECFYEBkYECN4ECOV8QJ3ZhbHVlOiBzZWxl +Y3Rpb24ucHJlZnMuaGlkZUNvbnRyb2xCZXplbF8QIHNlbGVjdGlvbi5wcmVmcy5oaWRlQ29udHJvbEJl +emVs0wAOBk8APgaNCVgJWoEBaqEJWYECOqEJW4ECO18QFk5TVmFsdWVUcmFuc2Zvcm1lck5hbWVfEA9O +U05lZ2F0ZUJvb2xlYW7XAA4AoAjaCNsAoQCiCNwI3QEvCPUI4AS6CWQA1IECF4AlgQIdgQIVgQEdgQI9 +XxAmdmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5zaGFyZVBhc3RlYm9hcmTXAA4AoAjaCNsAoQCiCNwI3QEv +CWkI4AMQCWwA1IECF4AlgQJAgQIVgKKBAj9fECV2YWx1ZTogc2VsZWN0aW9uLnByZWZzLnNob3dTdGF0 +dXNJdGVtXxAec2VsZWN0aW9uLnByZWZzLnNob3dTdGF0dXNJdGVt1wAOAKAI2gjbAKEAogjcCN0BLwly +CO0EvAl1ANSBAheAJYECQ4ECGoEBJYECQl8QLWVuYWJsZWQ6IHNlbGVjdGlvbi5wcmVmcy5yZXF1aXJl +UGFzdGVib2FyZEtleV8QJHNlbGVjdGlvbi5wcmVmcy5yZXF1aXJlUGFzdGVib2FyZEtledkADgCgCNoI +2wj8AKEAogj9CNwI3QEvCPUJAQBxBLwJfwmAANSBAheAJYECHYECIIECQYEBJYECRYECRl8QKWVuYWJs +ZWQyOiBzZWxlY3Rpb24ucHJlZnMuc2hhcmVQYXN0ZWJvYXJk0wAOBk8APgaNCYQJiYEBaqQJCwkMCQ0J +DoECIoECI4ECJIECJaQJEAkQCRAJEIECJoECJoECJoECJtkADgCgCNoI2wj8AKEAogj9CNwI3QEvCN8J +kgByBLwJlQmWANSBAheAJYECFoECSYECRIEBJYECSIECSl8QGmVuYWJsZWQzOiBzZWxlY3Rpb24uYWN0 +aXZlWGVuYWJsZWQz0wAOBk8APgaNCZsJoIEBaqQJCwkMCQ0JDoECIoECI4ECJIECJaQJEAkQCRAJEIEC +JoECJoECJoECJtcADgCgCNoI2wChAKII3AjdAS8JqAjtAQQJqwDUgQIXgCWBAk2BAhqAHIECTF8QKWVu +YWJsZWQ6IHNlbGVjdGlvbi5wcmVmcy5lbmFibGVFbmNyeXB0aW9uXxAgc2VsZWN0aW9uLnByZWZzLmVu +YWJsZUVuY3J5cHRpb27ZAA4AoAjaCNsI/AChAKII/QjcCN0BLwjfCQEAdAEECQQJtgDUgQIXgCWBAhaB +AiCBAkuAHIECH4ECT9MADgZPAD4GjQm5Cb6BAWqkCQsJDAkNCQ6BAiKBAiOBAiSBAiWkCRAJEAkQCRCB +AiaBAiaBAiaBAibXAA4AoAjaCNsAoQCiCNwI3QEvCN8I7QTECO8A1IECF4AlgQIWgQIagQGVgQIZ1wAO +AKAI2gjbAKEAogjcCN0BLwnNCOAEwAnQANSBAheAJYECU4ECFYEBT4ECUl8QKnZhbHVlOiBzZWxlY3Rp +b24ucHJlZnMuc3dpdGNoV2l0aERvdWJsZVRhcF8QI3NlbGVjdGlvbi5wcmVmcy5zd2l0Y2hXaXRoRG91 +YmxlVGFw1wAOAKAI2gjbAKEAogjcCN0BLwmoCO0DrwmrANSBAheAJYECTYECGoDIgQJM2QAOAKAI2gjb +CPwAoQCiCP0I3AjdAS8I3wkBAHgDrwkECeIA1IECF4AlgQIWgQIggQJUgMiBAh+BAlbTAA4GTwA+Bo0J +5QnqgQFqpAkLCQwJDQkOgQIigQIjgQIkgQIlpAkQCRAJEAkQgQImgQImgQImgQIm1wAOAKAI2gjbAKEA +ogjcCN0BLwnyCOAEuAn1ANSBAheAJYECWYECFYEBFYECWF8QIHZhbHVlOiBzZWxlY3Rpb24ucHJlZnMu +Y29weUZpbGVzXxAZc2VsZWN0aW9uLnByZWZzLmNvcHlGaWxlc9cADgCgCNoI2wChAKII3AjdAS8I3wjt +BMMI7wDUgQIXgCWBAhaBAhqBAZGBAhnXAA4AoAjaCNsAoQCiCNwI3QEvCagI4AOwCgUA1IECF4AlgQJN +gQIVgM2BAlxfECd2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmVuYWJsZUVuY3J5cHRpb27XAA4AoAjaCNsA +oQCiCNwI3QEvCN8I7QS6CO8A1IECF4AlgQIWgQIagQEdgQIZ1wAOAKAI2gjbAKEAogjcCN0BLwoRCOAE +wQoUANSBAheAJYECYIECFYEBU4ECX18QKHZhbHVlOiBzZWxlY3Rpb24ucHJlZnMubWF4UGFzdGVib2Fy +ZFNpemVfECFzZWxlY3Rpb24ucHJlZnMubWF4UGFzdGVib2FyZFNpemXXAA4AoAjaCNsAoQCiCNwI3QEv +CN8I7QS+CO8A1IECF4AlgQIWgQIagQFHgQIZ1wAOAKAI2gjbAKEAogjcCN0BLwohCO0ExgokANSBAheA +JYECZIECGoEBnoECY18QJmVuYWJsZWQ6IHNlbGVjdGlvbi5wcmVmcy5kZWxheWVkU3dpdGNoXxAdc2Vs +ZWN0aW9uLnByZWZzLmRlbGF5ZWRTd2l0Y2jZAA4AoAjaCNsI/AChAKII/QjcCN0BLwjfCQEAgATGCQQK +LwDUgQIXgCWBAhaBAiCBAmKBAZ6BAh+BAmbTAA4GTwA+Bo0KMgo3gQFqpAkLCQwJDQkOgQIigQIjgQIk +gQIlpAkQCRAJEAkQgQImgQImgQImgQIm1wAOAKAI2gjbAKEAogjcCN0BLwo/COAEvgpCANSBAheAJYEC +aYECFYEBR4ECaF8QIHZhbHVlOiBzZWxlY3Rpb24ucHJlZnMud2FrZU9uTEFOXxAZc2VsZWN0aW9uLnBy +ZWZzLndha2VPbkxBTtcADgCgCNoI2wChAKII3AjdAS8KSAlGBLwKSwDUgQIXgCWBAmyBAjSBASWBAmtf +EC1zZWxlY3RlZFRhZzogc2VsZWN0aW9uLnByZWZzLnBhc3RlYm9hcmRLZXlUYWdfECBzZWxlY3Rpb24u +cHJlZnMucGFzdGVib2FyZEtleVRhZ9cADgCgCNoI2wChAKII3AjdAS8I3wjtA7AI7wDUgQIXgCWBAhaB +AhqAzYECGdcADgCgCNoI2wChAKII3AjdAS8I3wjtBLgI7wDUgQIXgCWBAhaBAhqBARWBAhnZAA4AoAja +CNsI/AChAKII/QjcCN0BLwpfCQEAhQS4CmMKZADUgQIXgCWBAnGBAiCBAm6BARWBAnCBAnJfEC5lbmFi +bGVkMjogc2VsZWN0aW9uLmxvY2FsSG9zdC5zdXBwb3J0RHJhZ05Ecm9wXxAkc2VsZWN0aW9uLmxvY2Fs +SG9zdC5zdXBwb3J0RHJhZ05Ecm9w0wAOBk8APgaNCmkKboEBaqQJCwkMCQ0JDoECIoECI4ECJIECJaQJ +EAkQCRAJEIECJoECJoECJoECJtcADgCgCNoI2wChAKII3AjdAS8KdgjgBLsKeQDUgQIXgCWBAnWBAhWB +ASGBAnRfECF2YWx1ZTogc2VsZWN0aW9uLnByZWZzLnJlcXVpcmVLZXlfEBpzZWxlY3Rpb24ucHJlZnMu +cmVxdWlyZUtledcADgCgCNoI2wChAKII3AjdAS8I3wjtBLsI7wDUgQIXgCWBAhaBAhqBASGBAhnXAA4A +oAjaCNsAoQCiCNwI3QEvCN8I7QNuCO8A1IECF4AlgQIWgQIagLOBAhnXAA4AoAjaCNsAoQCiCNwI3QEv +CPUI7QSzCPgA1IECF4AlgQIdgQIagQEFgQIc2QAOAKAI2gjbCPwAoQCiCP0I3AjdAS8I3wkBAIoEswkE +CpkA1IECF4AlgQIWgQIggQJ4gQEFgQIfgQJ60wAOBk8APgaNCpwKoYEBaqQJCwkMCQ0JDoECIoECI4EC +JIECJaQJEAkQCRAJEIECJoECJoECJoECJtcADgCgCNoI2wChAKII3AjdAS8I3wjtBMAI7wDUgQIXgCWB +AhaBAhqBAU+BAhnXAA4AoAjaCNsAoQCiCNwI3QEvCN8I7QOyCO8A1IECF4AlgQIWgQIagNWBAhnXAA4A +oAjaCNsAoQCiCNwI3QEvCPUI7QTBCPgA1IECF4AlgQIdgQIagQFTgQIc2QAOAKAI2gjbCPwAoQCiCP0I +3AjdAS8I3wkBAI4EwQkECsMA1IECF4AlgQIWgQIggQJ9gQFTgQIfgQJ/0wAOBk8APgaNCsYKy4EBaqQJ +CwkMCQ0JDoECIoECI4ECJIECJaQJEAkQCRAJEIECJoECJoECJoECJtkADgCgCNoI2wj8AKEAogj9CNwI +3QEvCtMJkgCPBMEK1wrYANSBAheAJYECgoECSYECfoEBU4ECgYECg18QLWVuYWJsZWQzOiBzZWxlY3Rp +b24ucHJlZnMubGltaXRQYXN0ZWJvYXJkU2l6ZV8QI3NlbGVjdGlvbi5wcmVmcy5saW1pdFBhc3RlYm9h +cmRTaXpl0wAOBk8APgaNCt0K4oEBaqQJCwkMCQ0JDoECIoECI4ECJIECJaQJEAkQCRAJEIECJoECJoEC +JoECJtcADgCgCNoI2wChAKII3AjdAS8K0wjgBLMK7QDUgQIXgCWBAoKBAhWBAQWBAoVfECp2YWx1ZTog +c2VsZWN0aW9uLnByZWZzLmxpbWl0UGFzdGVib2FyZFNpemXXAA4AoAjaCNsAoQCiCNwI3QEvCN8I7QMQ +CO8A1IECF4AlgQIWgQIagKKBAhnXAA4AoAjaCNsAoQCiCNwI3QEvCnYI7QTCCvwA1IECF4AlgQJ1gQIa +gQGAgQKIXxAjZW5hYmxlZDogc2VsZWN0aW9uLnByZWZzLnJlcXVpcmVLZXnZAA4AoAjaCNsI/AChAKII +/QjcCN0BLwjfCQEAkwTCCQQLBgDUgQIXgCWBAhaBAiCBAoeBAYCBAh+BAorTAA4GTwA+Bo0LCQsOgQFq +pAkLCQwJDQkOgQIigQIjgQIkgQIlpAkQCRAJEAkQgQImgQImgQImgQIm1wAOAKAI2gjbAKEAogjcCN0B +LwsWCUYDsgsZANSBAheAJYECjYECNIDVgQKMXxAxc2VsZWN0ZWRUYWc6IHNlbGVjdGlvbi5wcmVmcy50 +cnVzdFJlcXVlc3RCZWhhdmlvcl8QJHNlbGVjdGlvbi5wcmVmcy50cnVzdFJlcXVlc3RCZWhhdmlvctcA +DgCgCNoI2wChAKII3AjdAS8JcgjgBLkLIgDUgQIXgCWBAkOBAhWBARmBAo9fECt2YWx1ZTogc2VsZWN0 +aW9uLnByZWZzLnJlcXVpcmVQYXN0ZWJvYXJkS2V51wAOAKAI2gjbAKEAogjcCN0BLwohCOAEvwsqANSB +AheAJYECZIECFYEBS4ECkV8QJHZhbHVlOiBzZWxlY3Rpb24ucHJlZnMuZGVsYXllZFN3aXRjaNcADgCg +CNoI2wChAKII3AjdAS8I3wjtBMgI7wDUgQIXgCWBAhaBAhqBAaqBAhnUAA4AoAs0CzULNgCkCzgLOVhO +U01hcmtlclZOU0ZpbGWBApaACoEClYEClF8QEE5TVG9vbFRpcEhlbHBLZXlfEDhDaGVjayB0byBhbGxv +dyBvdGhlciB0ZWxlcG9ydCB1c2VycyB0byBjb250cm9sIHlvdXIgTWFjLtIANwA4Cz0LPqILPgA7XxAR +TlNJQkhlbHBDb25uZWN0b3LUAA4AoAs0CzULNgLUC0ILOYECloCQgQKYgQKUbxAVAEEAIABwAHIAbwBw +AG8AcwAgAGQAZQAgAHQAZQBsAGUAcABvAHIAdCAm1AAOAKALNAs1CzYDQgtICzmBApaArIECmoEClF8Q +G0NoZWNrIHRvIGFjdGl2YXRlIHRlbGVwb3J0LtQADgCgCzQLNQs2AcYLTgs5gQKWgFiBApyBApRvEFAA +QwBlAGMAaQAgAGUAcwB0ACAAbABhACAAegBvAG4AZQAgAGQAZQAgAGQAaQBzAHAAbwBzAGkAdABpAG8A +bgAuACAARwBsAGkAcwBzAGUAcgAtAGQA6QBwAG8AcwBlAHIAIABsAGUAcwAgAGgA9AB0AGUAcwAgAG8A ++QAgAHYAbwB1AHMAIABsAGUAIABzAG8AdQBoAGEAaQB0AGUAegAu1AAOAKALNAs1CzYEvgtUCzmBApaB +AUeBAp6BApRvEK4AUwBlAHUAbABlAG0AZQBuAHQAIABwAG8AdQByACAAbABlAHMAIABNAGEAYwBzACAA +YwBvAG4AbgBlAGMAdADpAHMAIABlAG4AIABFAHQAaABlAHIAbgBlAHQAIABlAHQAIABhAHYAZQBjACAA +bAAnAG8AcAB0AGkAbwBuACAAIgBSAOkAdgBlAGkAbABsAGUAcgAgAHAAbwB1AHIAIABsACcAYQBjAGMA +6QBzACAARQB0AGgAZQByAG4AZQB0ACAAcABhAHIAIABsACcAYQBkAG0AaQBuAGkAcwB0AHIAYQB0AGUA +dQByACIAIABjAG8AYwBoAOkAIABkAGEAbgBzACAAbABlACAAcABhAG4AbgBlAGEAdQAgAGQAZQAgAHAA +cgDpAGYA6QByAGUAbgBjAGUAcwAgAEUAYwBvAG4AbwBtAGkAZQAgAGQAJwBFAG4AZQByAGcAaQBlAC7U +AA4AoAs0CzULNgTAC1oLOYECloEBT4ECoIEClG8QaQBUAG8AdQBjAGgAZQB6ACAAbABlACAAYgBvAHIA +ZAAgAGQAZQAgAGwAJwDpAGMAcgBhAG4ALAAgAHIAZQB2AGUAbgBlAHoAIABlAHQAIAByAGUALQB0AG8A +dQBjAGgAZQB6ACAAcgBhAHAAaQBkAGUAbQBlAG4AdAAgAGwAZQAgAGIAbwByAGQAIABwAG8AdQByACAA +ZQBmAGYAZQBjAHQAdQBlAHIAIAB1AG4AZQAgAGQAbwB1AGIAbABlAC0AaQBtAHAAdQBsAHMAaQBvAG4A +LtIADgA+C14LX4ECpK8QfwS1BtIHhAiAA7EAugtmBLsEvgcpBLMFUgWaBLQEkQXoAK8DcwTBBu0C2gHK +BAoCNAfmB+sDrQNCBZsDFAEEBtYDbgO0AqAAsgTNAokCCQYbBIEDFwTGAQsFDgONBsgIawEvBLkGJwTD +BLgEyAS9AxsEwATuA7IIIgNlB+gCRwI8BZ0F1wVpBAsDjAcYBMUBCAOwAkgG7AhPB+kH6gT+A0gBgAS8 +AXcFMATCAtQEtgPvBgoH8gTHB6QApAOvAxAF+QH0AkYB4AF8B+UEvwTeBLoEHAO1B0sDugPeB3QFQQWc +B9EINwbuBBsCVwc7AdIBxgVkBu8BEATEA8wC/wUfgQENgQGEgQGsgQH0gNGAqoECooEBIYEBR4EBl4EB +BYEBJ4EBNIEBCYD7gQFJgAuAtYEBU4EBi4CSgFmA5IDvgQHbgQHygMSArIEBN4CjgByBAYWAs4D1gIeA +DoEBB4B/gGqBAVWA94ClgQGegB+BAReBAQCBAYKBAe+AJYEBGYEBVoEBkYEBFYEBqoEBQ4EBAoEBT4EB +D4DVgQHdgLyBAeGAe4DrgQE9gQFFgQEqgNiAwIEBk4EBmoAdgM2AgIEBiYEB6YEB5oEB7YEBE4CugEmB +ASWAQ4EBH4EBgICQgQERgNOBAVGBAdaBAaaBAbmACoDIgKKBAU2AZYBugGCARoEBzIEBS4EBC4EBHYDf +gPmBAaCAxoDPgQGogQEjgQE6gQHHgQHjgQGNgN2AdoEBnICwgFiBASmBAY+AwYEBlYDKgJyBARvSAA4A +MgAzC+GABIECo11OU0FwcGxpY2F0aW9u0gA3ADgL5AFrogFrADvSAA4APgteC+eBAqSvEH8DFAbWBMgH +6wEIB6QAHwMUAxQExAMUBLwFaQMUA7UEvgC6A24DFAbWAtQDZQOyAgkBfAF8AQgArwVpAxsBCAbIAcoB +CAJIAKQEswJHAQgEwQO0AxADFAEEBLgB0gTCB+oAHwMUBhsDFAMUAxQDFAONAxQEtQEIB+YB0gF8AeAC +CQVpBL0FUgOyAdIEwwMUARABCAHgBtYH6QF8AXwEtgNCAXcDFAF8BLoDFACvAxQDsQTAB+UDFAAfAK8B +CAMUBL8CCQHgAgkH0QF8AxQEtAMUA7IBCATGA60DsATHBLsFaQAfB+gG1gOyAkYExQCvAcoFaQbWA4wD +FAOvAB8EuYCjgQGFgQGqgQHygB2BAbmAAoCjgKOBAZWAo4EBJYEBKoCjgPmBAUeAqoCzgKOBAYWAkIC8 +gNWAaoBGgEaAHYALgQEqgQECgB2BAYKAWYAdgICACoEBBYB7gB2BAVOA9YCigKOAHIEBFYCwgQGAgQHt +gAKAo4EBVYCjgKOAo4CjgQEAgKOBAQ2AHYEB24CwgEaAYIBqgQEqgQFDgQEngNWAsIEBkYCjgMGAHYBg +gQGFgQHmgEaARoEBEYCsgEOAo4BGgQEdgKOAC4CjgNGBAU+BAcyAo4ACgAuAHYCjgQFLgGqAYIBqgQHH +gEaAo4EBCYCjgNWAHYEBnoDEgM2BAaaBASGBASqAAoEB4YEBhYDVgG6BAZqAC4BZgQEqgQGFgMCAo4DI +gAKBARnSAA4APgteDGmBAqSvEIAEtQbSCIAHhAOxALoLZgS7BL4HKQSzBVIFmgS0BJEEwQCvA3MF6Abt +AtoBygQKAjQH5gfrA60DQgWbAxQBBAbWA24DtAKgALIEzQKJAgkGGwSBAxcExgELBQ4DjQhrBsgBLwS5 +BicEwwS4BMgEvQMbAB8EwAOyBO4IIgNlB+gCRwI8BZ0F1wVpBAsDjAcYBMUBCAOwAkgG7AhPB+kH6gGA +A0gE/gS8AXcFMATCAtQEtgPvBgoH8gflB6QBfACkBMcDEAOvBfkB9AHgAkYEvwTeBLoEHAO1B0sDugPe +B3QH0QVBBZwINwbuBBsCVwc7AdIC/wEQAcYDzATEBWQG7wUfgQENgQGEgQH0gQGsgNGAqoECooEBIYEB +R4EBl4EBBYEBJ4EBNIEBCYD7gQFTgAuAtYEBSYEBi4CSgFmA5IDvgQHbgQHygMSArIEBN4CjgByBAYWA +s4D1gIeADoEBB4B/gGqBAVWA94ClgQGegB+BAReBAQCBAe+BAYKAJYEBGYEBVoEBkYEBFYEBqoEBQ4EB +AoACgQFPgNWBAQ+BAd2AvIEB4YB7gOuBAT2BAUWBASqA2IDAgQGTgQGagB2AzYCAgQGJgQHpgQHmgQHt +gEmAroEBE4EBJYBDgQEfgQGAgJCBARGA04EBUYEB1oEBzIEBuYBGgAqBAaaAooDIgQFNgGWAYIBugQFL +gQELgQEdgN+A+YEBoIDGgM+BAaiBAceBASOBATqBAeOBAY2A3YB2gQGcgLCAnIDBgFiAyoEBlYEBKYEB +j4EBG9IADgA+C14M7IECpK8QgAztDO4M7wzwDPEM8gzzDPQM9Qz2DPcM+Az5DPoM+wz8DP0M/gz/DQAN +AQ0CDQMNBA0FDQYNBw0IDQkNCg0LDQwNDQ0ODQ8NEA0RDRINEw0UDRUNFg0XDRgNGQ0aDRsNHA0dDR4N +Hw0gDSENIg0jDSQNJQ0mDScNKA0pDSoNKw0sDS0NLg0vDTANMQ0yDTMNNA01DTYNNw04DTkNOg07DTwN +PQ0+DT8NQA1BDUINQw1EDUUNRg1HDUgNSQ1KDUsNTA1NDU4NTw1QDVENUg1TDVQNVQ1WDVcNWA1ZDVoN +Ww1cDV0NXg1fDWANYQ1iDWMNZA1lDWYNZw1oDWkNag1rDWyBAqiBAqmBAqqBAquBAqyBAq2BAq6BAq+B +ArCBArGBArKBArOBArSBArWBAraBAreBAriBArmBArqBAruBAryBAr2BAr6BAr+BAsCBAsGBAsKBAsOB +AsSBAsWBAsaBAseBAsiBAsmBAsqBAsuBAsyBAs2BAs6BAs+BAtCBAtGBAtKBAtOBAtSBAtWBAtaBAteB +AtiBAtmBAtqBAtuBAtyBAt2BAt6BAt+BAuCBAuGBAuKBAuOBAuSBAuWBAuaBAueBAuiBAumBAuqBAuuB +AuyBAu2BAu6BAu+BAvCBAvGBAvKBAvOBAvSBAvWBAvaBAveBAviBAvmBAvqBAvuBAvyBAv2BAv6BAv+B +AwCBAwGBAwKBAwOBAwSBAwWBAwaBAweBAwiBAwmBAwqBAwuBAwyBAw2BAw6BAw+BAxCBAxGBAxKBAxOB +AxSBAxWBAxaBAxeBAxiBAxmBAxqBAxuBAxyBAx2BAx6BAx+BAyCBAyGBAyKBAyOBAySBAyWBAyaBAydv +EBwAUwB0AGEAdABpAGMAIABUAGUAeAB0ACAAKABNAGkAcwBlAHMAIADgACAAagBvAHUAcgAgADoAKW8Q +GABNAGUAbgB1ACAASQB0AGUAbQAgACgjGAAgAEMAbwBtAG0AYQBuAGQAZQApAC0AMW8QIABUAGUAeAB0 +ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgnpAAgAGYAYQBpAHIAZQAgAHUAbgAgAGQAbwBuAClvEC4A +QgB1AHQAdABvAG4AIABDAGUAbABsACAAKABWAOkAcgBpAGYAaQBlAHIAIABsAGEAIAB2AGUAcgBzAGkA +bwBuACAAYQB1ACAAZADpAG0AYQByAHIAYQBnAGUAKV8QG1N0YXRpYyBUZXh0IChDaGlmZnJlbWVudCA6 +KVxDb250ZW50IFZpZXdbQXBwbGljYXRpb25fECVDaGVjayBCb3ggKENvbW11dGVyIHNldWxlbWVudCBh +dmVjIDopbxAzAEMAaABlAGMAawAgAEIAbwB4ACAAKABFAHMAcwBhAHkAZQByACAAZABlACAAcgDpAHYA +ZQBpAGwAbABlAHIAIABsAGUAcwAgAE0AYQBjAHMAIABlAG4AIAB2AGUAaQBsAGwAZQApbxAWAEIAdQB0 +AHQAbwBuACAAQwBlAGwAbAAgACgAVgDpAHIAaQBmAGkAZQByAClvECQAQwBoAGUAYwBrACAAQgBvAHgA +IAAoAFMAZQB1AGwAZQBtAGUAbgB0ACAAcwBpACAAaQBuAGYA6QByAGkAZQB1AHIAIADgAClvEB8AUABv +AHAAIABVAHAAIABCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoIxgAIABDAG8AbQBtAGEAbgBkAGUAKW8Q +FABNAGUAbgB1ACAASQB0AGUAbQAgACgjJQAgAE8AcAB0AGkAbwBuAClfEBpTdGF0aWMgVGV4dCAoVHJh +bnNmZXJ0cyA6KW8QJgBUAGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgASAD0AHQAZQBzACAA +ZABlACAAYwBvAG4AZgBpAGEAbgBjAGUAIAA6AClaVGV4dCBGaWVsZFtDdXN0b20gVmlld28QYQBUAGUA +eAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgARwBsAGkAcwBzAGUAcgAtAGQA6QBwAG8AcwBlAHIA +IABsAGUAcwAgAGgA9AB0AGUAcwAgAGQAaQBzAHAAbwBuAGkAYgBsAGUAcwAgAGEAdQB0AG8AdQByACAA +ZABlACAAdgBvAHQAcgBlACAA6QBjAHIAYQBuACAAcABvAHUAcgAgAGwAZQBzACAAYwBvAG4AdAByAPQA +bABlAHIALgApbxA1AEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgARQBzAHMAYQB5AGUAcgAgAGQAZQAg +AHIA6QB2AGUAaQBsAGwAZQByACAAbABlAHMAIABNAGEAYwBzACAAZQBuACAAdgBlAGkAbABsAGUAKW8Q +GABNAGUAbgB1ACAASQB0AGUAbQAgACgjAwAgAEMAbwBuAHQAcgD0AGwAZQApAC0AMW8QFABCAHUAdAB0 +AG8AbgAgAEMAZQBsAGwAIAAoAEEAYgBvAHUAdCAmAClWVmlldy0xXxAnUHJvdG90eXBlIFByb3RvdHlw +ZSBCdXR0b24gQ2VsbCAoUmFkaW8pXxATSG9yaXpvbnRhbCBTY3JvbGxlcl8QFlN0YXRpYyBUZXh0ICh0 +ZWxlcG9ydClvECAATABpAG4AawAgAFQAZQB4AHQAIABGAGkAZQBsAGQAIAAoJ6QAIABmAGEAaQByAGUA +IAB1AG4AIABkAG8AbgApbxBFAFMAdABhAHQAaQBjACAAVABlAHgAdAAgACgAUABvAHUAcgAgAHIAZQB0 +AGkAcgBlAHIAIAB1AG4AIABoAPQAdABlACwAIABzAOkAbABlAGMAdABpAG8AbgBuAGUAegAgAGwAZQAg +AGUAdAAgAGYAYQBpAHQAZQBzACAAcgBlAHQAbwB1AHIALgApXxAcQ2hlY2sgQm94IChBY3RpdmVyIHRl +bGVwb3J0KW8QFgBNAGUAbgB1ACAASQB0AGUAbQAgACgjAwAgAEMAbwBuAHQAcgD0AGwAZQApXUN1c3Rv +bSBWaWV3LTFvECQAUAB1AHMAaAAgAEIAdQB0AHQAbwBuACAAKABDAGgAbwBpAHMAaQByACAAbABlACAA +YwBlAHIAdABpAGYAaQBjAGEAdCAmAClfEBNNZW51IChPdGhlclZpZXdzKS0xbxBdAFMAdABhAHQAaQBj +ACAAVABlAHgAdAAgACgARwBsAGkAcwBzAGUAcgAtAGQA6QBwAG8AcwBlAHIAIABsAGUAcwAgAGgA9AB0 +AGUAcwAgAGQAaQBzAHAAbwBuAGkAYgBsAGUAcwAgAGEAdQB0AG8AdQByACAAZABlACAAdgBvAHQAcgBl +ACAA6QBjAHIAYQBuACAAcABvAHUAcgAgAGwAZQBzACAAYwBvAG4AdAByAPQAbABlAHIALgApbxAjAFMA +dABhAHQAaQBjACAAVABlAHgAdAAgACgARABlAG0AYQBuAGQAZQAgAGQAZQAgAGMAbwBuAHQAcgD0AGwA +ZQAgADoAKV8QFkJ1dHRvbiBDZWxsIChBZmZpY2hlcilfEB1CdXR0b24gQ2VsbCAoUGFydGFnZXIgY2Ug +TWFjKW8QJgBCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAFMAZQB1AGwAZQBtAGUAbgB0ACAAcwBpACAA +aQBuAGYA6QByAGkAZQB1AHIAIADgAClfEBtUZXh0IEZpZWxkIENlbGwgKFRleHQgQ2VsbClbU2Nyb2xs +IFZpZXdfEA9UZXh0IEZpZWxkIENlbGxvECcAVABlAHgAdAAgAEYAaQBlAGwAZAAgAEMAZQBsAGwAIAAo +AEQAZQBtAGEAbgBkAGUAIABkAGUAIABjAG8AbgB0AHIA9ABsAGUAIAA6AClfEDhCdXR0b24gQ2VsbCAo +QWZmaWNoZXIgbGUgc3RhdHVzIGRhbnMgbGEgYmFycmUgZGVzIG1lbnVzKV8QEUhvcml6b250YWwgU2xp +ZGVybxAkAEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgAQwBoAG8AaQBzAGkAcgAgAGwAZQAgAGMAZQBy +AHQAaQBmAGkAYwBhAHQgJgApbxA5AEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgARwBsAGkAcwBzAGUA +cgAtAGQA6QBwAG8AcwBlAHIAIABkAGUAcwAgAGYAaQBjAGgAaQBlAHIAcwAgAGUAbgB0AHIAZQAgAGwA +ZQBzACAATQBhAGMAcwApXxAXVGFiIFZpZXcgSXRlbSAoT3B0aW9ucylvEBoAVABlAHgAdAAgAEYAaQBl +AGwAZAAgAEMAZQBsAGwAIAAoJ6QAIABmAG8AcgB1AG0AcwApbxAhAFAAbwBwACAAVQBwACAAQgB1AHQA +dABvAG4AIABDAGUAbABsACAAKCMYACAAQwBvAG0AbQBhAG4AZABlACkALQAxXxASUHJlZlBhbmVDb250 +cm9sbGVyXxAcQ2hlY2sgQm94IChTZXVsZW1lbnQgYXZlYyA6KV8QEE51bWJlciBGb3JtYXR0ZXJvEEAA +QwBoAGUAYwBrACAAQgBvAHgAIAAoAEEAZgBmAGkAYwBoAGUAcgAgAHUAbgBlACAAZgBlAG4A6gB0AHIA +ZQAgAHQAcgBhAG4AcwBsAHUAYwBpAGQAZQAgAHAAZQBuAGQAYQBuAHQAIABsAGUAIABjAG8AbgB0AHIA +9ABsAGUAKW8QNwBDAGgAZQBjAGsAIABCAG8AeAAgACgARwBsAGkAcwBzAGUAcgAtAGQA6QBwAG8AcwBl +AHIAIABkAGUAcwAgAGYAaQBjAGgAaQBlAHIAcwAgAGUAbgB0AHIAZQAgAGwAZQBzACAATQBhAGMAcwAp +bxAsAEMAaABlAGMAawAgAEIAbwB4ACAAKABWAOkAcgBpAGYAaQBlAHIAIABsAGEAIAB2AGUAcgBzAGkA +bwBuACAAYQB1ACAAZADpAG0AYQByAHIAYQBnAGUAKV8QE1N0YXRpYyBUZXh0IChDb3VydClcRmlsZSdz +IE93bmVybxAvAEMAaABlAGMAawAgAEIAbwB4ACAAKABDAG8AbQBtAHUAdABlAHIAIABhAHAAcgDoAHMA +IAB1AG4AZQAgAGQAbwB1AGIAbABlACAAaQBtAHAAdQBsAHMAaQBvAG4AKVZNYXRyaXhvECAAVABlAHgA +dAAgAEYAaQBlAGwAZAAgAEMAZQBsAGwAIAAoAE0AaQBzAGUAcwAgAOAAIABqAG8AdQByACAAOgApXxAa +VGV4dCBGaWVsZCBDZWxsICh0ZWxlcG9ydClfEBtUYWIgVmlldyBJdGVtIChEaXNwb3NpdGlvbilvEHkA +UwB0AGEAdABpAGMAIABUAGUAeAB0ACAAKACpACAAMgAwADAAMwAtADIAMAAwADgAIABhAGIAeQBzAHMA +bwBmAHQACgBCAGEAcwDpACAAcwB1AHIAIAB1AG4AZQAgAGkAZADpAGUAIABkAGUAIABEAGEAdgBlACAA +UwBhAGcAIABlAHQAIABKAGUAcgBlAG0AeQAgAFEAdQBpAG4AbgAuAAoAUgBlAG0AZQByAGMAaQBlAG0A +ZQBuAHQAcwAgAOAAIABNAGEAbgB1ADAAMgAsACAASgB1AGwAaQBlAG4AIABlAHQAIABQAGEAdQBsAC4A +KV8QFlRhYmxlIENvbHVtbiAoYWRkcmVzcylfEBFWZXJ0aWNhbCBTY3JvbGxlcm8QJABNAGUAbgB1ACAA +SQB0AGUAbQAgACgh6gAgAFYAZQByAHIAbwB1AGkAbABsAGEAZwBlACAAbQBhAGoAdQBzAGMAdQBsAGUA +KV8QF1RleHQgRmllbGQgQ2VsbCAoQ291cnQpXxARTWVudSAoT3RoZXJWaWV3cylvEDYAQgB1AHQAdABv +AG4AIABDAGUAbABsACAAKABNAGUAIABkAGUAbQBhAG4AZABlAHIAIABzAGkAIABsACcAaAD0AHQAZQAg +AHAAZQB1AHQAIADqAHQAcgBlACAAYQB1AHQAbwByAGkAcwDpAClvECQAVABhAGIAIABWAGkAZQB3ACAA +SQB0AGUAbQAgACgAUgDpAGcAbABhAGcAZQBzACAAZABlACAAcwDpAGMAdQByAGkAdADpAClvEEIAQgB1 +AHQAdABvAG4AIABDAGUAbABsACAAKABBAGYAZgBpAGMAaABlAHIAIAB1AG4AZQAgAGYAZQBuAOoAdABy +AGUAIAB0AHIAYQBuAHMAbAB1AGMAaQBkAGUAIABwAGUAbgBkAGEAbgB0ACAAbABlACAAYwBvAG4AdABy +APQAbABlAClfEBZTdGF0aWMgVGV4dCAoU3RhdHV0IDopXUN1c3RvbSBWaWV3LTJfECJDaGVjayBCb3gg +KEFjdGl2ZXIgbGUgY2hpZmZyZW1lbnQpXxAaVGFibGUgQ29sdW1uIChjZXJ0aWZpY2F0ZSlvEBYATQBl +AG4AdQAgAEkAdABlAG0AIAAoIyUAIABPAHAAdABpAG8AbgApAC0AMW8QIQBUAGUAeAB0ACAARgBpAGUA +bABkACAAQwBlAGwAbAAgACgnpAAgAHMAaQB0AGUAIABpAG4AdABlAHIAbgBlAHQAKW8QIQBMAGkAbgBr +ACAAVABlAHgAdAAgAEYAaQBlAGwAZAAgACgnpAAgAHMAaQB0AGUAIABpAG4AdABlAHIAbgBlAHQAKW8Q +GgBMAGkAbgBrACAAVABlAHgAdAAgAEYAaQBlAGwAZAAgACgnpAAgAGYAbwByAHUAbQBzAClfEBtUZXh0 +IEZpZWxkIENlbGwgKDx2ZXJzaW9uPilfEB5CdXR0b24gQ2VsbCAoQWN0aXZlciB0ZWxlcG9ydClfEBxU +ZXh0IEZpZWxkIENlbGwgKENvbW11dGVyIDopbxAZAFAAbwBwAHUAcAAgAEIAdQB0AHQAbwBuACAAKCMY +ACAAQwBvAG0AbQBhAG4AZABlAClfEB5WZXJzaW9uIFRleHQgRmllbGQgKDx2ZXJzaW9uPilfECtCdXR0 +b24gQ2VsbCAoU3luY2hyb25pc2VyIGxlIHByZXNzZS1wYXBpZXIpbxAbAFAAbwBwAHUAcAAgAEIAdQB0 +AHQAbwBuACAAKCMYACAAQwBvAG0AbQBhAG4AZABlACkALQAxbxAVAEIAZQB2AGUAbAAgAEIAdQB0AHQA +bwBuACAAKABBAGIAbwB1AHQgJgApXxAYU3RhdGljIFRleHQgKENvbW11dGVyIDopXxAfVGV4dCBGaWVs +ZCBDZWxsIChDaGlmZnJlbWVudCA6KW8QMQBCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAEMAbwBtAG0A +dQB0AGUAcgAgAGEAcAByAOgAcwAgAHUAbgBlACAAZABvAHUAYgBsAGUAIABpAG0AcAB1AGwAcwBpAG8A +bgApXxAVSW1hZ2UgQ2VsbCAodGVsZXBvcnQpXxAVSW1hZ2UgVmlldyAodGVsZXBvcnQpWFByZWZQYW5l +XkNvbnRlbnQgVmlldy0xXxAbQ2hlY2sgQm94IChQYXJ0YWdlciBjZSBNYWMpXxASU3RhdGljIFRleHQg +KExvbmcpXxA2Q2hlY2sgQm94IChBZmZpY2hlciBsZSBzdGF0dXMgZGFucyBsYSBiYXJyZSBkZXMgbWVu +dXMpXxAkUHVzaCBCdXR0b24gKEFmZmljaGVyIGxlIGNlcnRpZmljYXQpbxAmAEIAdQB0AHQAbwBuACAA +QwBlAGwAbAAgACgAQwBvAG0AbQB1AHQAZQByACAAYQB2AGUAYwAgAHUAbgAgAGQA6QBsAGEAaQAgADoA +KV8QEVRhYmxlIEhlYWRlciBWaWV3XxAlVGFibGUgVmlldyAoTm9tLCBBZHJlc3NlLCBDZXJ0aWZpY2F0 +KV8QE1RhYmxlIENvbHVtbiAobmFtZSlvECQAQwBoAGUAYwBrACAAQgBvAHgAIAAoAEMAbwBtAG0AdQB0 +AGUAcgAgAGEAdgBlAGMAIAB1AG4AIABkAOkAbABhAGkAIAA6AClfEB5UZXh0IEZpZWxkIENlbGwgKFRy +YW5zZmVydHMgOilfEClDaGVjayBCb3ggKFN5bmNocm9uaXNlciBsZSBwcmVzc2UtcGFwaWVyKV8QJkJ1 +dHRvbiBDZWxsIChBY2NlcHRlciBhdXRvbWF0aXF1ZW1lbnQpbxAiAFMAdABhAHQAaQBjACAAVABlAHgA +dAAgACgASAD0AHQAZQBzACAAZABlACAAYwBvAG4AZgBpAGEAbgBjAGUAIAA6AClbU2xpZGVyIENlbGxv +EEkAVABlAHgAdAAgAEYAaQBlAGwAZAAgAEMAZQBsAGwAIAAoAFAAbwB1AHIAIAByAGUAdABpAHIAZQBy +ACAAdQBuACAAaAD0AHQAZQAsACAAcwDpAGwAZQBjAHQAaQBvAG4AbgBlAHoAIABsAGUAIABlAHQAIABm +AGEAaQB0AGUAcwAgAHIAZQB0AG8AdQByAC4AKV8QJEJ1dHRvbiBDZWxsIChBY3RpdmVyIGxlIGNoaWZm +cmVtZW50KV8QFlRleHQgRmllbGQgQ2VsbCAoTG9uZylVQWJvdXRfECdCdXR0b24gQ2VsbCAoQ29tbXV0 +ZXIgc2V1bGVtZW50IGF2ZWMgOilvEBMATQBlAG4AdQAgAEkAdABlAG0AIAAoIecAIABTAGgAaQBmAHQA +KW8QfQBUAGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgAqQAgADIAMAAwADMALQAyADAAMAA4 +ACAAYQBiAHkAcwBzAG8AZgB0AAoAQgBhAHMA6QAgAHMAdQByACAAdQBuAGUAIABpAGQA6QBlACAAZABl +ACAARABhAHYAZQAgAFMAYQBnACAAZQB0ACAASgBlAHIAZQBtAHkAIABRAHUAaQBuAG4ALgAKAFIAZQBt +AGUAcgBjAGkAZQBtAGUAbgB0AHMAIADgACAATQBhAG4AdQAwADIALAAgAEoAdQBsAGkAZQBuACAAZQB0 +ACAAUABhAHUAbAAuAClvEBUATQBlAG4AdQAgAEkAdABlAG0AIAAoIecAIABTAGgAaQBmAHQAKQAtADFv +EDYAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABSAGUAagBlAHQAZQByACAAcwBpACAAbAAnAGgA9AB0 +AGUAIABuACcAZQBzAHQAIABwAGEAcwAgAGQAZQAgAGMAbwBuAGYAaQBhAG4AYwBlAClfEB1UZXh0IEZp +ZWxkIENlbGwgKFRleHQgQ2VsbCktMV8QGlRleHQgRmllbGQgQ2VsbCAoU3RhdHV0IDopbxA5AFQAbwBw +ACAAVABhAGIAIABWAGkAZQB3ACAAKABEAGkAcwBwAG8AcwBpAHQAaQBvAG4ALAAgAFIA6QBnAGwAYQBn +AGUAcwAgAGQAZQAgAHMA6QBjAHUAcgBpAHQA6QAsACAATwBwAHQAaQBvAG4AcwApXxARVHJ1c3RlZEhv +c3RzVGFibGVWVmlldy0yW0xheW91dCBWaWV3XxAkQnV0dG9uIENlbGwgKEFmZmljaGVyIGxlIGNlcnRp +ZmljYXQpbxAWAFAAdQBzAGgAIABCAHUAdAB0AG8AbgAgACgAVgDpAHIAaQBmAGkAZQByAClvEBYATQBl +AG4AdQAgAEkAdABlAG0AIAAoIxgAIABDAG8AbQBtAGEAbgBkAGUAKW8QJgBNAGUAbgB1ACAASQB0AGUA +bQAgACgh6gAgAFYAZQByAHIAbwB1AGkAbABsAGEAZwBlACAAbQBhAGoAdQBzAGMAdQBsAGUAKQAtADFf +EB5CdXR0b24gQ2VsbCAoU2V1bGVtZW50IGF2ZWMgOinSAA4APgteDe6BAqSlAXcH6QfqAeAH64BDgQHm +gQHtgGCBAfLSAA4APgteDfaBAqSlAYQIUwhTAfwIU4BEgQHngQHngGGBAefSAA4APgteDf6BAqSvENcG +0geECIAAYgC6AJcLZgcpBLMFmgS0AK8DcwBRAtoATAHKBAoAiABfAjQH5gfrAIMAeABWBZsAfwMUAIUA +fQbWAHwDbgByAHsCiQCbAgkEgQCTAxcAVwTGAQsFDgONBsgIawBsBicE7gOyCCIAZANlB+gCPAWdBdcF +aQBNAFIDjAcYBMUBCACUAFsG7AfpAFMAcQBQAIsAmgTCAtQAggBJBgoH8gCdA68B9AF8AEoDEAX5AFgC +RgHgAFkE3gB5BBwAcwdLB3QFQQfRAG0AgAbuAE8EGwJXBzsAXgBIAGAB0gEQAv8AWgPMBMQAVAS1AJ4D +sQS7BL4AlgVSBJEF6ATBBu0AkQCMAIkAhgBOA60DQgCBAH4BBABLA7QCoACyBM0AXQCKBhsAdQBvAG4B +LwS5AGsAagTDBLgEyAS9AxsAaQAfBMAAZwCYAkcECwCVA7ACSAhPAGMAkgCZAJAAjQfqAGEE/gNIAYAE +vAF3AIcFMACEBLYD7wCPBMcHpACkB+UAegS/AJwAjgS6AHcAdgO1AHAD3gO6BZwAXAg3AHQAVQBmAcYF +ZAbvAGgAZQUfgQGEgQGsgQH0gQIPgKqBApCBAqKBAZeBAQWBATSBAQmAC4C1gKGAkoBXgFmA5IECdoEC +CoDvgQHbgQHygQJqgQJUgQG4gQE3gQJhgKOBAm6BAl2BAYWBAluAs4ECRIECWoB/gQKZgGqA94ECh4Cl +gQHGgQGegB+BAReBAQCBAYKBAe+BAjGBAVaBAQ+A1YEB3YECE4C8gQHhgOuBAT2BAUWBASqAX4CogMCB +AZOBAZqAHYECiYECAoEBiYEB5oCpgQJBgJuBAnmBApeBAYCAkIECZ4AbgQFRgQHWgQKdgMiAZYBGgCSA +ooEBTYEB/IBugGCBAf6BAQuBAlWA34ECR4EBoIEBqIEBI4EBx4ECMoECYoEBjYCPgN2AdoEBnIECCIAJ +gQILgLCAwYCcgQIAgMqBAZWBAbSBAQ2BAp+A0YEBIYEBR4ECjoEBJ4D7gQFJgQFTgQGLgQKEgQJ7gQJ3 +gQJvgI2AxICsgQJlgQJegByAQoD1gIeADoEBB4ECBoECeIEBVYECToECPIECNoAlgQEZgQIwgQItgQGR +gQEVgQGqgQFDgQECgQIqgAKBAU+BAh6BApKAe4DYgQKLgM2AgIEB6YECEYEChoECk4ECgIECfIEB7YEC +DYEBE4CugEmBASWAQ4ECc4EBH4ECbYEBEYDTgQJ+gQGmgQG5gAqBAcyBAleBAUuBApuBAn2BAR2BAlGB +AlCA+YECPoDPgMaBATqBAgSBAeOBAkuBAbaBAhuAWIEBKYEBj4ECJ4ECGIEBG9IADgA+C14O2IECpK8Q +1w7ZDtoO2w7cDt0O3g7fDuAO4Q7iDuMO5A7lDuYO5w7oDukO6g7rDuwO7Q7uDu8O8A7xDvIO8w70DvUO +9g73DvgO+Q76DvsO/A79Dv4O/w8ADwEPAg8DDwQPBQ8GDwcPCA8JDwoPCw8MDw0PDg8PDxAPEQ8SDxMP +FA8VDxYPFw8YDxkPGg8bDxwPHQ8eDx8PIA8hDyIPIw8kDyUPJg8nDygPKQ8qDysPLA8tDy4PLw8wDzEP +Mg8zDzQPNQ82DzcPOA85DzoPOw88Dz0PPg8/D0APQQ9CD0MPRA9FD0YPRw9ID0kPSg9LD0wPTQ9OD08P +UA9RD1IPUw9UD1UPVg9XD1gPWQ9aD1sPXA9dD14PXw9gD2EPYg9jD2QPZQ9mD2cPaA9pD2oPaw9sD20P +bg9vD3APcQ9yD3MPdA91D3YPdw94D3kPeg97D3wPfQ9+D38PgA+BD4IPgw+ED4UPhg+HD4gPiQ+KD4sP +jA+ND44Pjw+QD5EPkg+TD5QPlQ+WD5cPmA+ZD5oPmw+cD50Png+fD6APoQ+iD6MPpA+lD6YPpw+oD6kP +qg+rD6wPrQ+uD6+BAyyBAy2BAy6BAy+BAzCBAzGBAzKBAzOBAzSBAzWBAzaBAzeBAziBAzmBAzqBAzuB +AzyBAz2BAz6BAz+BA0CBA0GBA0KBA0OBA0SBA0WBA0aBA0eBA0iBA0mBA0qBA0uBA0yBA02BA06BA0+B +A1CBA1GBA1KBA1OBA1SBA1WBA1aBA1eBA1iBA1mBA1qBA1uBA1yBA12BA16BA1+BA2CBA2GBA2KBA2OB +A2SBA2WBA2aBA2eBA2iBA2mBA2qBA2uBA2yBA22BA26BA2+BA3CBA3GBA3KBA3OBA3SBA3WBA3aBA3eB +A3iBA3mBA3qBA3uBA3yBA32BA36BA3+BA4CBA4GBA4KBA4OBA4SBA4WBA4aBA4eBA4iBA4mBA4qBA4uB +A4yBA42BA46BA4+BA5CBA5GBA5KBA5OBA5SBA5WBA5aBA5eBA5iBA5mBA5qBA5uBA5yBA52BA56BA5+B +A6CBA6GBA6KBA6OBA6SBA6WBA6aBA6eBA6iBA6mBA6qBA6uBA6yBA62BA66BA6+BA7CBA7GBA7KBA7OB +A7SBA7WBA7aBA7eBA7iBA7mBA7qBA7uBA7yBA72BA76BA7+BA8CBA8GBA8KBA8OBA8SBA8WBA8aBA8eB +A8iBA8mBA8qBA8uBA8yBA82BA86BA8+BA9CBA9GBA9KBA9OBA9SBA9WBA9aBA9eBA9iBA9mBA9qBA9uB +A9yBA92BA96BA9+BA+CBA+GBA+KBA+OBA+SBA+WBA+aBA+eBA+iBA+mBA+qBA+uBA+yBA+2BA+6BA++B +A/CBA/GBA/KBA/OBA/SBA/WBA/aBA/eBA/iBA/mBA/qBA/uBA/yBA/2BA/6BA/+BBACBBAGBBAIRAYgS +AAGIARIAAYfFEOgRAUMT//////////0SAAGH8BEBRREBnhEBaxDJEgABhzgRAQoSAAGHTBC4EL0SAAGI +UhEBXBIAAw42EKURASURAakRAYIQGhEBoBEBvRIABJUcEQGTEQFYEQGGEQGAEJgRAacSAASVDRIAAYeZ +EgAElSAQ9hIAAYhYEQGPEgABh6gQqxEBvxIABJT4EgABiDIQvBIAAYglEgABh8QRAVcRAU4SAAGIDxEB +shIAAYdFEQFWELsQqBIAAYeWEQGfEgABiGARAZ0RAQQRAQMRAWUSAASVCBEBcBIABJUbEQEQEQGJEQEj +EMoRAaYRAZcRAVkSAASVHxEBhRCsEQG+EgAElPoSAASVDxIAAYdEEgAElSIRAYESAASU1hCiEQEqEQEI +EgABh68QrxD4EPcQ6RIAAYgLEQGDEQG3EQGoEgABiF8SAAGIYRIAAYdiEKERAZERAcMRAYsQrREBthIA +AYeYEgABiBAQnBCqELoRAWYRAZYQ6hIAAYghEQFQEQFREQFvEgAElSMSAASVCRDCEQG7EQGlEgABiDwS +AAGIChIAAYhbEQFGEQGHEQFJEgAElRIRAbARAcUQkREBuRCuEQHEEQHHEgAElPcQtxEBuBEBaRIAAYco +EgABh+UQzhEBShIAAYfmEgAElQYRAUESAASVDBEBKREBmxEBrxEBwhIABJUHEQGSEQFhEQHAEL4RAUAS +AASVHRIABJUOEQGkEQFkEPkRAbURAboRAX0RAWgSAAGHwxDNEQFeEgAElR4RAVsRAcYSAASU+xIAAYgO +EgABh04SAAGHRhEBnBCmEQFCEgABh18RAX8RAW4SAASVChEBWhEBwRAMEIgQpBEBlREBDxIABJUhEQFL +EL8SAASVEREBYBEBahEBRBIAAYgdEgABiFkRAaISAASU/BIAAYdIEgAElQUSAASU+REBoxCJEQGhEQGK +EQFjEQFdEgABiDvSAA4APgBGEISAPaDSAA4APgteEIeBAqSg0gAOAD4LXhCKgQKkoNIANwA4EIwQjaIQ +jQA7Xk5TSUJPYmplY3REYXRhAAgAGQAiACcAMQA6AD8ARABSAFQAZgh4CH4IyQjQCNcI5Qj3CRMJIQkt +CTkJRwlSCWAJfAmKCZ0JrwnJCdMJ4AniCeUJ6AnrCe4J8AnzCfUJ+An7Cf4KAQoDCgUKCAoLChAKEwoc +CigKKgosCj8KSApRClwKYQpwCnkKjAqVCqAKogqjCqwKswrACsYKzwrRC4ILhAuGC4gLiguMC44LkAuS +C5QLlguYC5oLnQugC6MLpgupC6wLrwuyC7ULuAu7C74LwQvEC8cLygvNC9AL0wvWC9kL3AvfC+IL5Qvo +C+sL7gvxC/QL9wv6C/0MAAwDDAYMCQwMDA8MEgwVDBgMGwweDCEMJAwnDCoMLQwwDDMMNgw5DDwMPwxC +DEUMSAxLDE4MUQxUDFcMWgxdDGAMYwxmDGkMbAxvDHIMdQx4DHsMjAyaDKMMqwytDK8MsQyzDNQM5gzu +DPUM/g0IDRENHQ0fDSENIw0lDSgNKQ0rDS0NTg1ZDWUNZw1pDWsNbg1xDXMNdQ13DZENxg3SDegN/Q4M +Dh8OMQ48DkYOVA5mDnMOgQ6GDogOig6MDo4OkA6SDpQOlg6YDpoOnA6hDrMOxA7LDtIO2w7dDuYO6A7r +DvgPAQ8GDw0PFg8iDyQPJg8vDzgPPQ9TD1QPXQ9mD3MPgA+JD5QPnQ+nD64Pug/RD9oP4Q/4EAcQGBAa +EBwQHhAgED0QPxBBEEMQRRBOEE8QURBuEHAQchB0EHcQeRB7EH0QlxDIEM0QzxDRENMQ1RDXENkQ2xDg +EOkRGBEhESsRLRE2ET0RTxFYEXIRgxGFEYcRiRGLEZwRrhG5EcgRyhHMEc0RzxHYEdoSCRILEg0SDxIR +EhMSFRIXEhkSGxIdEh8SIRIjEiUSJxIpEisSLRIvEjESMxI1EkoSYhJ1EosSohK+EtkS4BL5Ex4TNxNM +E14TexOUE7ETwxPXE/QUDhQnFEMUTBRTFGIUahRvFHEUehR/FIgUjxSkFLEUuRTKFMwUzhTQFNIU+xUR +FRMVFRUXFRkVGxUeFR8VIRUjFSUVOhVGFWMVZRVnFWoVbRVvFXIVixWsFcAVzBXOFdAV0hXUFdYV2xXd +FecV+BX6FgMWBRYIFh0WJRYyFj4WTBZOFlAWUhZUFlYWXRZqFncWfxaBFoMWjxaYFp0Wsha0FrYWuBa6 +Fs0W2hbcFt8W6BbxFwMXDBcRFyAXMxdEF0YXSBdKF0wXbRd5F3sXfRd/F4EXgxeFF4cXpBemF6gXqhes +F64XsBfJF9YX3xfoF/UX/hgPGBEYExgVGBcYbhiQGJoYpxi8GMkY4xj/GRoZJhlFGVQZYBliGWQZaRlr +GW0ZbxlwGXIZexmEGYYZhxmJGYsZjRmPGZEZmhmmGbIZ2xnlGe8Z+RoHGgkaCxoNGg8aERoUGhYaGBoa +GhwaJxpAGkIaRBpGGkgaShpzGnUadxp5GnsafRp/GoEagxqNGpYanxqzGsgayhrMGs4a0BsJGxUbHhsx +Gz4bShtYG1obXBteG2AbYhtkG2YbeRt7G30bfxuBG5gboRuqG7gbwRvDG8obzBvOG9Ab+RwIHBUcIhwq +HDUcRBxPHFocWxxdHF8cYRxqHGwcdRx+HIAchRyiHKccqRyrHK0crxyxHLUcwhzEHNAc5RznHOkc6xzt +HP8dCB0THScdSB1NHU8dUR1THVUdVx1cHV4daB19HX8dgR2DHYUdnh2nHawduh3jHeQd5h3oHeod8x31 +Hf4eAB4IHiUeJx4pHiseLR4vHjceWB5aHlweXh5gHmIeZB6FHoceiR6LHpQelh6fHqEerR7KHswezh7Q +HtIe1B7fHvQe9h74Hvoe/B8IHxUfFx8aH0sfTR9PH1EfUx9VH1cfWR9bH14fYx9sH3Ufih+MH44fkB+S +H5wfqR+rH7AfyB/ZH9sf3R/fH+Ef7B/9H/8gASADIAUgJiAoICogLCAuIDEgMiA0IDYgTyCEIJIglCCW +IJggmiCcIJ4goCCiIKcgtCDBINAg0iDUINYg3iDpIPIg9yEKIRMhFSEnITAhNyFPIWAhYiFkIWYhaCF5 +IY0hjyGRIZMhlSGeIaAhoyGlIbwhwSHDIdQh1iHYIdoh3CH5Ifsh/SH/IgEiAiIEIiEiJCImIikiLCIu +IjEiSiJ/IoEigyKFIociiSKLIo0iuiLJItoi3CLeIuAi4iLzIvUi9yL5IvwjGSMbIx0jICMjIyUjKCMx +IzMjPCM+I0AjQiNEI2UjZyNpI2sjbSNuI3AjciOLI8AjwiPEI8YjyCPKI8wjziPhJBYkJSQ5JFIkaiRs +JG8kcSRzJHUkdyR5JHokfCR9JH8kiCSKJI0kjySYJJoknyShJKMkxCTGJMgkyiTMJM8k0CTSJNQk6yUM +JQ4lECUSJRQlFiUbJR0lviXHJdIl6yX0JfsmFCYdJh8mJiYoJiomLSZGJlAmUiZUJlYmWCZaJlwmYyZv +JngmfSaLJqAmoiakJqYmqCaqJr8mwSbDJsUmxybQJtIm1SbXJuAm4ib1Jvcm+Sb7Jv0m/ycBJwMnBScH +JyQnJicoJyonLCcvJzAnMidMJ20nbydxJ3MndSd3J3wnfifvKAwoDigQKBIoFCgVKBcoMShiKGQoZiho +KGoobChuKIcokCiSKK8osSizKLUotyi4KLoo1CkJKQspDSkPKREpEykVKRcpMClNKU8pUSlTKVUpVilY +KXEpkimUKZYpmCmaKZwpoSmjKbEp/CoIKhIqISotKkUqUCpaKm8qfSqFKocqiSqLKo0qjyqRKpMqlSqX +KpkqmiqcKp4qoyqlKr4qxyrJKtAq0irUKtYrCysUKxYrGCsaKxwrHisgKyIrdSt+K4ArgiuQK5krmyvU +K9or3CveK+Ar4ivkK+Yr6Cw7LHQsdix4LHosfCx+LIAsgiydLKcsrizXLNks2yzdLN8s5SzuLPktAi0L +LQ0tGC0aLRwtHi0gLSItKy0tLTAtMi1KLVMtXC1nLYgtkS2aLaQtpi2oLaotrC2uLbAtuS3VLeIt6y32 +LgEuJi4oLiouLC4uLjAuMi47LlcuYC5iLmUuZy59LpguoS6qLrcu1C7WLtgu2i7cLt0u3y73LxgvGi8c +Lx4vIC8iLyQvUS9uL3Avci90L3Yvdy95L5Evsi+0L7YvuC+6L7wvvi/pL/QwHzA4MDowPTBAMEIwRDBH +ME8wZDBmMGgwazBuMHcweTB8MH4whzCJMLgwuzC+MMEwxDDGMMkwzDDPMNIw1TDYMNsw3jDhMOQw5zDq +MO0w8DDzMPYw+TEWMRgxGjEdMSAxITEjMT0xcjF0MXYxeDF6MX0xfzGCMbUx0jHUMdYx2THcMd0x3zH4 +MhkyGzIdMiAyIjIlMicyNDJRMlMyVTJYMlsyXDJeMnYylzKZMpsynjKgMqMypTLCMt8y4TLjMuYy6TLq +MuwzBTMmMygzKjMtMy8zMjM0Mz8zXDNeM2AzYzNmM2czaTODM7gzujO8M74zwDPDM8UzyDQhND40QDRC +NEU0SDRJNEs0ZTSaNJw0njSgNKI0pTSnNKo0vTTaNNw03jThNOQ05TTnNQE1NjU4NTo1PDU+NUE1QzVG +NWY1gzWFNYc1ijWNNY41kDWqNd814TXjNeU15zXqNew17zYLNig2KjYtNjA2MzY0NjY2UDabNrg2yjbV +Nuc2/DcKNxE3GjcbNx03IDcjNyU3KDcrNyw3LTcwNzM3ODdBN0M3dDd8N5A3mzepN7M3wDfIN8s3zjfR +N9Y32DfdN+A34zfmN+k36zf4OAQ4BzgKOA04IjgvODE4Mzg2OEg4VThXOFk4XDhvOIM4jDiROJo4nDin +OLA4sji9OMA4wzjGOMk4zDj5OPw4/zkCOQQ5BzkKOQ05EDkSOSM5UDlTOVY5WTlbOV45YTlkOWc5aTl+ +Oas5rjmxObQ5tjm5Obw5vznCOcQ50zoAOgM6BjoJOgs6DjoROhQ6FzoZOkw6VTpaOmM6cDqEOpM6nDqp +Orc61DrWOtg62zreOt864Tr6Oxs7HTsfOyI7JDsnOyw7Ljs0O1E7UztVO1g7WztcO147eDutO687sTuz +O7U7uDu6O708DjwrPC08LzwyPDU8Njw4PFI8hzyJPIs8jTyPPJI8lDyXPMo85zzpPOs87jzxPPI89D0O +PUM9RT1HPUk9Sz1OPVA9Uz2cPbk9uz29PcA9wz3EPcY93z4IPhQ+Kj4zPjU+OD46Pj0+QD5FPkY+ST5M +PpM+mj6lPqw+uD7APtM+2j7oPvw/Dj8iPzU/QT9IP1U/Zz9qP20/cD9zP3U/eD97P34/gT+EP4U/iD+K +P40/kD+RP5I/nz+nP6o/wz/GP8k/zD/PP9I/1T/YP9s/3j/hP+Q/50AAQANABkAJQAxAD0ASQBVAGEAb +QB5AIUAkQCxAO0BVQHdAikCeQKVAskC6QNJA4UD1QRJBHUEpQThBQkFOQVpBXUFeQXFBckF7QYBBnUGg +Qa1BukG9QcBBw0HFQdJB1UHWQddB4EHlQfJB+0IAQhVCIkIlQiZCJ0IqQi1CNkJEQkdCSUJSQldCYEJ9 +QoBCgUKUQpVCl0KgQqdCvULGQslCzELZQtxC30LiQuZC70L2QwpDEUMmQyhDKkMtQy9DRUNaQ1xDXkNh +Q2NDbUOKQ4xDj0OSQ5VDlkOYQ7JD/UP+RABEA0QGRAhEC0QORA9EEEQTRBxEHkRPRFJEVURYRFpEXURg +RGNEZkRzRHZEeUR8RIVEh0SQRJJEnUSgRKNEpkSpRKxE2UTcRN9E4kTkROdE6kTtRPBFHUUgRSNFJkUo +RStFLkUxRTRFYUVkRWdFakVsRW9FckV1RXhFpUWoRatFrkWwRbNFtkW5RbxF2UXbRd1F4EXjReRF5kX/ +RjRGNkY4RjpGPEY/RkFGREavRsxGzkbQRtNG1kbXRtlG8EchRyNHJUcoRytHLUcwR0FHSkdMR2lHa0dt +R3BHc0d0R3ZHjkevR7FHs0e2R7hHu0e9R8ZH40flR+hH60fuR+9H8UgKSENIS0hhSHZIgUiMSJdIpUjC +SMtIzkjRSNRI10jgSOlI7kjvSPhI+UkCSQRJFUkXSSBJI0ktSTZJP0lMSVVJYElpSYZJiEmKSY1JkEmR +SZNJrEnNSc9J0UnUSdZJ2UnbSeBJ/Un/SgFKBEoHSghKCkojSlhKWkpcSl5KYEpjSmVKaEqrSrNKvErF +StBK1UrmSuhK6krtSvBK/ksPSxFLE0sVSxhLMEtBS0NLRktIS0tLfEuJS5ZLpEuuS7xLyUvTS+VL+UwD +TA9MEUwUTBdMGkwfTCJMJUwoTCtMLkxJTGFMakxsTHFMekx8TH9MgUyMTKVMs0zQTNlM3kzxTPlNCk0M +TQ5NEU0TTURNRk1JTUxNTk1TTVZNWU1cTV9NYk19TY9NmE2aTaNNpU20TbdNuk28Tb9Nwk3FTchN8U39 +Tf9OAk4FTgZOCU4MTg1OD04RThpOHE4rTi5OMU40TjdOOk49TkBOXE54Tp5Otk7qTwtPKE9CT2NPa09z +T3tPhk+LT45PkU+WT5dPpE+mT6hPq0+0T71PxE/QT9lP5E/wUBFQE1AVUBhQG1AcUB5QIFA5UFpQXFBe +UGFQZFBnUGlQelB8UIVQiFCcUL1Qv1DBUMRQx1DIUMpQzFDkUQVRB1EJUQxRD1ESURdRGVHyUgNSBVIO +UhBSE1I8Uj5SQFJDUkVSSFJLUkxSTlJRUlNSZVJ7UqRSqVKrUq1SsFKzUrZSt1K6UrxS3VLuUvBS8lMR +UzpTPFM+U0FTQ1NGU0dTSVNMU05TZVOKU4xTjlORU5RTl1OaU5xTrVPhVApUDFQOVBFUE1QWVBdUGVQc +VB5UNVRaVFxUXlRhVGRUZ1RqVGxUiVU5VURVT1VgVWJVZFVmVWlVflWPVZFVk1WVVZhVqlW7Vb1Vv1XB +VcRV0VXiVeRV51XpVexWBFYVVhdWGVYbVh5WOFZJVktWTlZQVlNWaFZ5VntWfVZ/VoJWjVaeVqBWolak +VqZWt1a5VrxWvlbBVs1W3lbgVuJW5FbnVwBXEVcTVxVXF1caVyhXOVc7Vz5XQFdDV11XeleEV45XrVew +V7JXtVe4V7pXvVfXV91X8Ff5WABYGFg1WDhYOlg9WEBYQ1hGWGJYaliHWIpYjFiPWJJYlViYWMNY5VkK +WSBZKlktWS9ZMlk1WThZO1k+WUFZXllnWXRZd1mAWYNZhlmJWYxZlVmYWZtZnlmhWbVZ0lntWgtaFFox +WjRaNlo5WjxaP1pCWmxaj1qsWq9asVq0WrdauVq8WuJbAVseWyFbI1smWylbLFsvW1RbcluPW5JblFuX +W5pbnFufW7xbv1vBW8Rbx1vJW8xb6VvsW+5b8Vv0W/db+lwmXDJcUVxyXHVcd1x6XH1cgFyDXIZcsFzT +XOBc41zmXOlc7FzvXQhdGl03XTpdPF0/XUJdRV1IXXFdjl2RXZNdll2ZXZtdnl3GXedeBF4HXgleDF4P +XhJeFV5FXmxekV6UXpZemV6cXp9eol6lXqhe1F7hXuRe7V7wXvNe9l75XwJfBV8IXwtfDl8zXzZfOF87 +Xz5fQV9EX0dfSl9nX3BffV+AX4lfjF+PX5JflV+eX6FfpF+nX6pfx1/KX8xfz1/SX9Rf12ADYCZgS2BO +YFBgU2BWYFlgW2BeYGFgbmBxYHpgfWCAYINghmCPYJJglWCYYJtguGC7YL1gwGDDYMZgyWDmYOlg62Du +YPFg9GD3YSRhSmFnYWphbGFvYXJhdGF3YZxhn2GhYaRhp2GqYaxhr2GyYb9hwmHLYc5h0WHUYddh4GHj +YeZh6WHsYgliDGIOYhFiFGIXYhpiPWJZYnZieWJ7Yn5igWKEYodipGKnYqlirGKvYrFitGLeYvti/mMA +YwNjBmMJYwxjKWMsYy5jMWM0YzdjOmNlY4ljpmOpY6tjrmOxY7Rjt2PUY9dj2WPcY99j4mPlZA5kLmRT +ZFZkWGRbZF5kYWRkZGdkamR3ZHpkg2SGZIlkjGSPZJhkm2SeZKFkpGTBZMRkxmTJZMxkz2TSZPVlEWUu +ZTFlM2U2ZTllPGU/ZW9lkmWvZbJltGW3ZbplvGW/Zdxl32XhZeRl52XqZe1mEmYVZhdmGmYdZiBmI2Ym +ZilmWmaBZo5mkWaaZp1moGajZqZmr2ayZrVmuGa7Zthm22bdZuBm42bmZulnDWcqZ0dnSmdMZ09nUmdV +Z1hndWd4Z3pnfWeAZ4JnhWeiZ6Vnp2eqZ61nsGezZ9hn22fdZ+Bn42fmZ+ln7GfvZ/xn/2gIaAtoDmgR +aBRoHWggaCNoJmgpaEZoSWhLaE5oUWhUaFdodGh3aHlofGh/aIFohGihaKRopmipaKxor2iyaNdo2mjc +aN9o4mjlaOho62juaPto/mkHaQppDWkQaRNpHGkfaSJpJWkoaU1pUGlSaVVpWGlbaV5pYWlkaZRpumnH +acpp02nWadlp3Gnfaehp62nuafFp9GoRahRqFmoZahxqH2oiak9qbGpvanFqdGp3anlqfGqZapxqnmqh +aqRqp2qqatBq9Wr4avpq/WsAawNrBmsJawxrGWscayVrKGsray5rMWs6az1rQGtDa0ZrY2tma2hra2tu +a3Brc2una85r62vua/Br82v2a/lr/GwqbEdsSmxMbE9sUmxVbFhsf2ycbJ9soWykbKdsqmytbL5sx2zO +bNFs02zWbNls7G0nbTBtNW1JbVptXW1fbWJtZW2SbaNtpm2obattrm3Mbd1t4G3ibeVt6G6Lbpxun26i +bqVuqHAHcBhwG3AecCFwJHD5cQJxBXIGcglyDHIPchJyFHIWchlyHHIfciJyJXIocityLnIwcjNyNXI3 +cjpyPXI/ckFyQ3JFckhyS3JNck9yUnJUclZyWXJbcl1yX3JhcmRyZnJocmtybXJvcnJydHJ3cnpyfXKA +coJyhXKIcotyjnKRcpRyl3Kacp1yn3KicqRyp3KpcqtyrnKxcrRytnK4crtyvnLAcsJyxHLHcspyzXLQ +ctNy1XLXctpy3HLfcuJy5HLnculy7HLvcvJy9XL3cvly+3L+cwBzAnMEcwZzCXMMcw9zEnMUcxZzGXMb +cx1zIHMjcyZzKXMscy9zMXMzczZzOHM6cz1zQHNCc0VzR3NJc0xzVXNXc1pzaHNxc3Zzf3OCdIN0hXSI +dIt0jnSQdJN0lXSXdJl0nHSedKF0pHSmdKh0q3StdK90sXS0dLZ0uHS6dLx0vnTAdMJ0xHTHdMp0zHTP +dNF003TVdNd02nTcdN504XTjdOV053TpdOx07nTxdPR09nT4dPt0/XT/dQF1A3UGdQh1C3UNdRB1EnUU +dRZ1GHUbdR51IXUjdSV1KHUqdSx1LnUwdTN1NnU4dTp1PXU/dUF1Q3VFdUh1SnVMdU51UHVTdVZ1WHVa +dVx1XnVgdWN1ZXVndWl1bHVudXB1c3V1dXd1eXV8dX51gHWDdYZ1iXWLdY51kXWTdZV1mHWadZx1n3Wi +daR1pnWodap1rXW2dbl2vHa/dsJ2xXbIdsp2zHbPdtJ21XbYdtt23nbhduR25nbpdut27XbwdvN29Xb3 +dvl2+3b+dwF3A3cFdwh3CncMdw93EXcTdxV3F3cadxx3HnchdyN3JXcodyp3LXcwdzN3Nnc4dzt3PndB +d0R3R3dKd013T3dSd1R3V3dad1x3X3dhd2N3Zndpd2x3bndwd3N3dnd4d3p3fHd/d4J3hXeId4p3jHeP +d5J3lHeXd5p3nHefd6F3pHend6p3rXevd7F3tHe2d7h3u3e9d793wXfEd8d3ynfMd8530XfTd9V32Hfb +d9534Xfkd+d36Xfrd+538Hfyd/R39nf4d/t3/ngBeAR4DXgQeRN5FnkZeRx5H3kieSV5KHkreS55MXk0 +eTd5Onk9eUB5Q3lGeUl5THlPeVJ5VXlYeVt5XnlheWR5Z3lqeW15cHlzeXZ5eXl8eX95gnmFeYh5i3mO +eZF5lHmXeZp5nXmgeaN5pnmpeax5r3myebV5uHm7eb55wXnEecd5ynnNedB503nWedl53HnfeeJ55Xno +eet57nnxefR593n6ef16AHoDegZ6CXoMeg96EnoVehh6G3oeeiF6JHoneip6LXowejN6Nno5ejx6P3pC +ekV6SHpLek56UXpUeld6WnpdemB6Y3pmeml6bHpvenJ6dXp4ent6fnqBeoR6h3qKeo16kHqTes57AXtE +e6N7wXvOe9p8AnxrfJp85X0mfVF9bn29fch91H6ZfwZ/OX9kf2t/lX+rf8SAB4CUgLOA4oDwgTuBUYIO +gleCcIKQgt+C/YMJgxuDbIOng7uEBoR7hJWEzIURhSaFRYVYhduGTIanhr2GyocrhzKHdYeSh7CIpYi+ +iNKJHYk3iUuJuooFioyKpYqzitiK9Yski2mLrovljAOMJIxDjHiMmYzHjQCNLY1IjWqNz43njf+OCI4X +jjWOSo6DjqqO+Y8NjzWPS4+Wj7eP45AMkFOQX5D0kRuRNJE6kWSRjZKKkreTJpNGk2OT2JPsk/OT/5Qm +lFWUhJTTlPSU/ZUAlQuVDZUQlROVFZUYlSGVJJUvlTGVNJU3lTmVPJVFlUiW+Zb8lv+XApcFlweXCpcN +lxCXE5cWlxmXG5cdlx+XIZcjlyWXJ5cqly2XL5cylzWXOJc7lz6XQZdEl0aXSZdMl0+XUpdUl1eXWpdc +l1+XYZdjl2aXaJdrl26XcJdzl3aXeZd8l3+XgpeFl4eXipeNl4+XkpeUl5eXmpedl5+XoZejl6aXqZer +l66XsZe0l7eXuZe8l76XwZfEl8eXyZfMl86X0ZfUl9eX2Zfbl92X35fhl+SX55fpl+uX7pfxl/SX9pf5 +l/yX/5gCmAWYCJgLmA6YEJgSmBSYF5gamByYH5ghmCOYJZgomCqYLZgwmDOYNpg4mDuYPphBmESYRphJ +mEyYT5hSmFWYWJhbmF2YX5hhmGSYZ5hpmGuYbZhvmHGYdJh3mHqYfZiAmIOYhpiImIuYjpiRmJSYl5ia +mJ2YoJijmKWYqJirmK6YsJiymLWYt5i5mLyYv5jCmMWYyJjLmM6Y0ZjUmNaY2JjbmN2Y4JjjmOaY6Zjr +mO6Y8Zj0mPaY+Zj8mP+ZApkFmQiZC5kOmRCZE5kVmReZGpkdmSCZI5kmmSmZK5kumTGZNJk3mTqZQ5lG +mvea+pr9mwCbA5sGmwmbDJsPmxKbFZsYmxubHpshmySbJ5sqmy2bMJszmzabOZs8mz+bQptFm0ibS5tO +m1GbVJtXm1qbXZtgm2ObZptpm2ybb5tym3WbeJt7m36bgZuEm4ebipuNm5Cbk5uWm5mbnJufm6KbpZuo +m6ubrpuxm7Sbt5u6m72bwJvDm8abyZvMm8+b0pvVm9ib25vem+Gb5Jvnm+qb7Zvwm/Ob9pv5m/yb/5wC +nAWcCJwLnA6cEZwUnBecGpwdnCCcI5wmnCmcLJwvnDKcNZw4nDucPpxBnEScR5xKnE2cUJxTnFacWZxc +nF+cYpxlnGica5xunHGcdJx3nHqcfZyAnIOchpyJnIycj5ySnJWcmJybnJ6coZyknKecqpytnLCcs5y2 +nLmcvJy/nMKcxZzInMuczpzRnNSc15zanN2c4JzjnOac6ZzsnO+c8pz1nPic+5z+nQGdBJ0HnQqdDZ0Q +nROdFp0ZnRydH50inSWdKJ0rnS6dMZ00nTedOp09nUCdQ51GnUmdTJ1PnVKdVZ1YnVudXp1hnWSdZ51q +nW2dcJ1znXadeZ18nX+dhJ2JnYudjp2XnZydn52inaWdp52sna+dtJ22nbidvZ3AncWdx53Knc2d0J3S +ndWd2J3dneCd453mnemd653unfOd+J39nf+eBJ4HngyeDp4RnhaeG54dniKeJ54qni2eMp41njqePZ4/ +nkGeRp5Jnk6eUZ5UnleeWp5fnmKeZ55qnm2ecJ5ynnWeeJ57noCeg56FnoiejZ6SnpeenJ6fnqSepp6p +nqyesZ6znrWet565nr6ewZ7EnseezJ7Rntae2J7bnt6e4Z7jnuae657wnvKe9J72nvme/J7+nwOfBp8J +nwyfEZ8WnxifG58enyOfKJ8tnzCfM582nzufPp9Bn0OfRp9In0ufTp9Tn1WfWJ9bn2CfZZ9nn2qfb590 +n3effJ9/n4KfhZ+In42fkJ+Tn5afmJ+bn6CfpZ+on6ufrZ+wn7Oftp+5n76fwJ/Dn8ify5/On9Of2J/d +n+Kf5Z/nn+qf75/yn/Wf+p/9oACgAqAEoAagCaAMoBGgFKAWoBugHqAhoCSgKaAuoDGgNqA7oECgRaBI +oEqgTaBQoFOgVqBboGSgZqBnoHCgc6B0oH2ggKCBoIqgjwAAAAAAAAICAAAAAAAAEI4AAAAAAAAAAAAA +AAAAAKCeA + + + diff --git a/German.lproj/InfoPlist.strings b/German.lproj/InfoPlist.strings new file mode 100644 index 0000000..e4185d8 --- /dev/null +++ b/German.lproj/InfoPlist.strings @@ -0,0 +1,5 @@ +/* Localized versions of Info.plist keys */ + +NSHumanReadableCopyright = "Copyright (c) 2003-2008, abyssoft."; +CFBundleGetInfoString = "Version 1.1"; +TPTranslationInfoString = "German version by Jérôme Gamez"; diff --git a/German.lproj/Localizable.strings b/German.lproj/Localizable.strings new file mode 100644 index 0000000..d3b7c5c Binary files /dev/null and b/German.lproj/Localizable.strings differ diff --git a/German.lproj/MainMenuApp.nib/classes.nib b/German.lproj/MainMenuApp.nib/classes.nib new file mode 100644 index 0000000..8eab51a --- /dev/null +++ b/German.lproj/MainMenuApp.nib/classes.nib @@ -0,0 +1,38 @@ + + + + + IBClasses + + + CLASS + TPPrefPaneAppController + LANGUAGE + ObjC + OUTLETS + + window + NSWindow + + SUPERCLASS + NSObject + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/German.lproj/MainMenuApp.nib/info.nib b/German.lproj/MainMenuApp.nib/info.nib new file mode 100644 index 0000000..21ddea8 --- /dev/null +++ b/German.lproj/MainMenuApp.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 629 + IBLastKnownRelativeProjectPath + ../teleport.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 265 + + IBSystem Version + 9A569 + targetFramework + IBCocoaFramework + + diff --git a/German.lproj/MainMenuApp.nib/keyedobjects.nib b/German.lproj/MainMenuApp.nib/keyedobjects.nib new file mode 100644 index 0000000..0d5ee09 Binary files /dev/null and b/German.lproj/MainMenuApp.nib/keyedobjects.nib differ diff --git a/German.lproj/teleportPref.xib b/German.lproj/teleportPref.xib new file mode 100644 index 0000000..d82168a --- /dev/null +++ b/German.lproj/teleportPref.xib @@ -0,0 +1,5929 @@ + + + + 1050 + 9C31 + 629 + 949.26 + 352.00 + + YES + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + TPPreferencePane + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{517, 450}, {595, 429}} + 1081606144 + PDwgZG8gbm90IGxvY2FsaXplID4+A + NSWindow + + View + + + + 256 + + YES + + + 274 + + YES + + + 268 + {{171, 387}, {219, 18}} + + + YES + + 604110336 + 0 + Diesen Mac gemeinsam nutzen + + LucidaGrande + 1.300000e+01 + 1044 + + + 1211912703 + 2 + + NSSwitch + + + + 200 + 25 + + + + + 265 + {{545, 379}, {32, 32}} + + + YES + + 67239424 + 134217728 + QWJvdXTigKY + + + 138690815 + 130 + + NSImage + icon-about + + + + + + 200 + 25 + + + + + 268 + {{18, 387}, {138, 18}} + + + YES + + 67239424 + 0 + teleport aktivieren + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 274 + + YES + + + 256 + + YES + + + 301 + + YES + + + 292 + {{182, 87}, {341, 28}} + + + YES + + 67239424 + 272760832 + RWludHLDpGdlIGluIGRlciBMaXN0ZSBrw7ZubmVuIGR1cmNoIEF1c3dhaGwgdW5kIERyw7xja2VuIGRl +ciBMw7ZzY2hlbi1UYXN0ZSBlbnRmZXJudCB3ZXJkZW4uA + + + 1.100000e+01 + 3100 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2OQA + + + + 6 + + controlTextColor + + 3 + MAA + + + + + + + -2147483380 + {{172, 250}, {181, 32}} + + + YES + + 67239424 + 134217728 + WmVydGlmaWthdCBhdXN3w6RobGVu4oCmA + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{172, 250}, {172, 32}} + + + YES + + 67239424 + 134217728 + WmVydGlmaWthdCBhbnplaWdlbuKApg + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{183, 286}, {191, 18}} + + + YES + + 67239424 + 0 + VmVyc2NobMO8c3NlbHVuZyBha3RpdmllcmVuA + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{23, 287}, {148, 17}} + + + YES + + 67239424 + 71303168 + VmVyc2NobMO8c3NlbHVuZzo + + + + + + + + + 292 + {{185, 23}, {335, 58}} + + + YES + 3 + 1 + + YES + + -2080244224 + 0 + Abfrage, ob die Anfrage angenomen werden soll + + + 1211912703 + 0 + + NSRadioButton + + + + + + 200 + 25 + + + 67239424 + 0 + Abweisen, wenn nicht bereits in der Liste + + 1 + + 1211912703 + 0 + + + + 200 + 25 + + + 67239424 + 0 + Automatisch annehmen + + 2 + + 1211912703 + 0 + + + + 400 + 75 + + + {335, 18} + {4, 2} + 1143480320 + NSActionCell + + 67239424 + 0 + Radio + + 1211912703 + 0 + + 400 + 75 + + + + + 3 + MQA + + + + + + 274 + + YES + + + 2304 + + YES + + + 256 + {333, 104} + + + YES + + + 256 + {333, 17} + + + + + + + 256 + {{-26, 0}, {16, 17}} + + + + + YES + + name + 1.490000e+02 + 4.000000e+01 + 1.000000e+03 + + 75628032 + 0 + Name + + + 3 + MC4zMzMzMzI5OQA + + + 6 + + headerTextColor + + + + + 337772096 + 133120 + Text Cell + + + + 6 + + controlBackgroundColor + + + + + 1 + YES + + + + address + 1.160000e+02 + 8.000000e+00 + 1.000000e+03 + + 75628032 + 0 + Adresse + + + + + + 337772096 + 133120 + + + + + + + 1 + YES + + + + certificate + 5.900000e+01 + 5.857568e+01 + 1.000000e+03 + + 67239424 + 0 + Zertifikat + + + 6 + + headerColor + + + + + + 67239424 + 134348800 + Anzeige + + + -2034876161 + 36 + + + 400 + 75 + + + + + 3.000000e+00 + 2.000000e+00 + + + 6 + + gridColor + + 3 + MC41AA + + + 1.700000e+01 + 448823296 + 1 + 15 + 0 + YES + + + {{1, 17}, {333, 104}} + + + + + + 4 + + + + 256 + {{-100, -100}, {15, 180}} + + + + _doScroller: + 9.222222e-01 + + + + 256 + {{-100, -100}, {463, 15}} + + + 1 + + + 9.904762e-01 + + + + 2304 + + YES + + + {{1, 0}, {333, 17}} + + + + + + 4 + + + + {{185, 116}, {335, 122}} + + + + 2 + + + + + + QSAAAEEgAABBmAAAQZgAAA + + + + 292 + {{14, 64}, {157, 17}} + + + YES + + 67239424 + 71303168 + Steuerungs-Anfragen: + + + + + + + + + 268 + {{2, 204}, {169, 34}} + + + YES + + 67239424 + 71303168 + VmVydHJhdWVuc3fDvHJkaWdlIEhvc3RzOg + + + + + + + + {549, 319} + + + NSView + + + {{10, 33}, {549, 319}} + + + + + {{13, 10}, {569, 365}} + + + + YES + + layout + + + 256 + + YES + + + 274 + {{20, 17}, {511, 296}} + + TPLayoutView + + + + + 290 + {{17, -13}, {517, 28}} + + YES + + 67239424 + 4325376 + Gemeinsame Macs aus dem oberen Bereich im unteren verteilen, um sie zu steuern. + + + + + + + + {{10, 33}, {549, 319}} + + Layout + + + + + + Sicherheit + + + + + options + + + 256 + + YES + + + 301 + + YES + + + 268 + {{211, 132}, {156, 18}} + + YES + + 67239424 + 0 + Nur, wenn kleiner als + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{43, 181}, {128, 17}} + + YES + + 67239424 + 71303168 + w5xiZXJ0cmFndW5nZW46A + + + + + + + + + 268 + {{43, 17}, {128, 17}} + + YES + + 67239424 + 71303168 + VmVyc2lvbnNwcsO8ZnVuZzo + + + + + + + + + 268 + {{17, 253}, {154, 51}} + + YES + + 67239424 + 71303168 + Bildschirmwechsel: + + + + + + + + + 268 + {{183, 74}, {210, 18}} + + YES + + 67239424 + 0 + U3RhdHVzIGluIE1lbsO8bGVpc3RlIGFuemVpZ2VuA + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 180}, {350, 18}} + + YES + + -2080244224 + 0 + RGF0ZWllbiBwZXIgRHJhZyAmIERyb3Agendpc2NoZW4gTWFjcyDDvGJlcnRyYWdlbg + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{211, 108}, {180, 18}} + + YES + + 67239424 + 0 + TnVyIGJlaSBnZWRyw7xja3RlciBUYXN0ZQ + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 156}, {226, 18}} + + YES + + -2080244224 + 0 + Zwischenablage synchronisieren + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 286}, {180, 18}} + + YES + + 67239424 + 0 + + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{395, 103}, {129, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + 4oyYIEJlZmVobHN0YXN0ZQ + + 1048576 + 2147483647 + 1 + + + NSMenuCheckmark + + + + NSMenuMixedState + + _popUpItemAction: + 20 + + + YES + + + OtherViews + + + YES + + + + 4oylIFdhaGx0YXN0ZQ + + 1048576 + 2147483647 + + + _popUpItemAction: + 19 + + + + + 4oyDIGN0cmwtVGFzdGU + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + 4oenIFNoaWZ0LVRhc3RlA + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + 4oeqIENhcHMgTG9jaw + + 1048576 + 2147483647 + + + _popUpItemAction: + 16 + + + + + 3 + YES + YES + 1 + + + + + 268 + {{323, 263}, {33, 14}} + + YES + + 67239424 + 272629760 + Short + + + + + + + + + 268 + {{183, 214}, {278, 18}} + + YES + + 67239424 + 0 + Versuche, schlafende Macs aufzuwecken + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 262}, {146, 18}} + + YES + + 67239424 + 0 + TWl0IFZlcnrDtmdlcnVuZw + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 238}, {204, 18}} + + YES + + 67239424 + 0 + QmVpIERvcHBlbGJlcsO8aHJ1bmc + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{373, 131}, {58, 22}} + + YES + + -1804468671 + 71304192 + + + + YES + + YES + allowsFloats + attributedStringForZero + decimalSeparator + formatterBehavior + groupingSeparator + locale + maximum + minimum + negativeFormat + positiveFormat + textAttributesForNegativeValues + usesGroupingSeparator + + + YES + + + 0 + + YES + + YES + + + YES + + + + wqA + + , + + + + + 0 + 2 + NO + YES + 2 + AABAAAAAAAAAAAAAAAAAAA + + + 0 + 0 + NO + NO + 2 + AAAAAAAAAAAAAAAAAAAAAA + + + 0K + + YES + + YES + + + YES + + + + + + + + + + + + + + + NaN + + + + + + + + NO + NO + YES + + size + + YES + + 6 + + textBackgroundColor + + + + 6 + + textColor + + + + + + + 268 + {{368, 281}, {129, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + 19 + + + YES + + + OtherViews + + + YES + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 20 + + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 16 + + + + + 1 + 3 + YES + YES + 1 + + + + + 268 + {{183, 50}, {361, 18}} + + YES + + 67239424 + 0 + Bezel bei der Steuerung eines anderen Macs anzeigen + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{422, 7}, {115, 32}} + + YES + + 67239424 + 134217728 + SmV0enQgcHLDvGZlbg + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{102, 75}, {69, 17}} + + YES + + 67239424 + 71303168 + Status: + + + + + + + + + 268 + {{361, 261}, {83, 15}} + + YES + + 67239424 + 131072 + + + + + Helvetica + 1.200000e+01 + 16 + + + 1.000000e+00 + 1.000000e-01 + 5.000000e-01 + 0.000000e+00 + 0 + 1 + NO + NO + + + + + 268 + {{449, 263}, {31, 14}} + + YES + + 67239424 + 272629760 + Long + + + + + + + + + 268 + {{183, 16}, {231, 18}} + + YES + + 67239424 + 0 + QXV0b21hdGlzY2hlIFByw7xmdW5nIGJlaW0gU3RhcnQ + + + 1211912703 + 2 + + + + 200 + 25 + + + + {549, 319} + + + + + {{10, 33}, {549, 319}} + + Optionen + + + + + + + 0 + YES + YES + + + {595, 438} + + + + + + {595, 429} + + + + {{0, 0}, {1680, 1028}} + {224.664, 32} + {3.40282e+38, 3.40282e+38} + + + 1 + 2 + {{194, 375}, {295, 307}} + 1886912512 + + TPClosingWindow + + View + + + + 256 + + YES + + + 256 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + CorePasteboardFlavorType 0x504E4766 + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{20, 159}, {255, 128}} + + + YES + + 537001472 + 33554432 + + + teleport + + 0 + 0 + 0 + NO + + YES + + + + 256 + {{17, 129}, {261, 22}} + + + YES + + 67239424 + 138412032 + + + LucidaGrande-Bold + 1.800000e+01 + 16 + + + + + + + + + 256 + {{17, 113}, {261, 14}} + + + YES + + 67239424 + 138412032 + PHZlcnNpb24+A + + + + + + + + + 256 + {{12, 43}, {274, 39}} + + + YES + + 67239424 + 4194304 + wqkgMjAwMy0yMDA4IGFieXNzb2Z0CkJhc2llcnQgYXVmIGVpbmVyIElkZWUgdm9uIERhdmUgU2FnIHVu +ZCBKZXJlbXkgUXVpbm4uCkJlc29uZGVyZXIgRGFuayBnZWh0IGFuIE1hbnUwMiwgSnVsaWVuIHVuZCBQ +YXVsLg + + + 1.000000e+01 + 2843 + + + + + + + + + 258 + {{12, 8}, {68, 17}} + + + YES + + 69336577 + 272629760 + 4p6kIHdlYnNpdGU + + + 1.100000e+01 + 16 + + http://teleport.abyssoft.com + + YES + + + + + + + 258 + {{119, 8}, {69, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIGZvcnVtA + + http://www.abyssoft.com/software/teleport/forums/ + + + + + + + + 258 + {{228, 8}, {67, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIHNwZW5kZW4 + + aHR0cHM6Ly93d3cucGF5cGFsLmNvbS9jZ2ktYmluL3dlYnNjcj9jbWQ9X3hjbGljayZidXNpbmVzcz1q +dWwlNDBtYWMlMmVjb20maXRlbV9uYW1lPXRlbGVwb3J0Jm5vX3NoaXBwaW5nPTAmbm9fbm90ZT0wJnRh +eD0wJmN1cnJlbmN5X2NvZGU9VVNEJmNoYXJzZXQ9VVRGJTJkOCZjaGFyc2V0PVVURiUyZDg + + + + + + + {295, 307} + + + + {{0, 0}, {1680, 1028}} + {213, 129} + {3.40282e+38, 3.40282e+38} + + + + YES + prefs.allowControl + prefs.sharePasteboard + prefs.requireKey + prefs.delayedSwitch + prefs.showStatusItem + prefs.limitPasteboardSize + prefs.maxPasterboardSize + active + prefs.autocheckVersion + prefs.rejectAuthenticationRequests + prefs.enableEncryption + prefs.switchKeyTag + prefs.copyFiles + prefs.requirePasteboardKey + prefs.pasteboardKeyTag + prefs.trustRequestBehavior + prefs.wakeOnLAN + prefs.switchDelay + localHost.supportDragNDrop + prefs.maxPasteboardSize + prefs.hideControlBezel + prefs.switchWithDoubleTap + + YES + + + + + YES + numberOfSelectedRows + + NSTableView + + + + + + YES + + + _window + + + + 26 + + + + layoutView + + + + 145 + + + + allowControlCheckbox + + + + 156 + + + + aboutWindow + + + + 170 + + + + delegate + + + + 171 + + + + showAboutSheet: + + + + 173 + + + + activationCheckbox + + + + 175 + + + + versionTextField + + + + 183 + + + + + + + + 184 + + + + + + + + 200 + + + + view + + + + 202 + + + + sharePasteboardCheckbox + + + + 205 + + + + requireKeyCheckbox + + + + 206 + + + + _firstKeyView + + + + 232 + + + + _initialKeyView + + + + 233 + + + + _lastKeyView + + + + 234 + + + + dataSource + + + + 258 + + + + + + + + 259 + + + + trustedHostsTableView + + + + 260 + + + + statusCheckbox + + + + 266 + + + + delayedSwitchCheckbox + + + + 272 + + + + content + + + + 298 + + + + value: selection.prefs.allowControl + + + + + + + value + selection.prefs.allowControl + 2 + + + 320 + + + + value: selection.prefs.sharePasteboard + + + + + + + + selection.prefs.sharePasteboard + 2 + + + 321 + + + + value: selection.prefs.requireKey + + + + + + + + selection.prefs.requireKey + 2 + + + 322 + + + + value: selection.prefs.delayedSwitch + + + + + + + + selection.prefs.delayedSwitch + 2 + + + 323 + + + + value: selection.prefs.showStatusItem + + + + + + + + selection.prefs.showStatusItem + 2 + + + 324 + + + + value: selection.prefs.limitPasteboardSize + + + + + + + + selection.prefs.limitPasteboardSize + 2 + + + 329 + + + + enabled: selection.prefs.sharePasteboard + + + + + + + enabled + + 2 + + + 330 + + + + + + + + + + + + + 2 + + + 331 + + + + checkVersion: + + + + 337 + + + + value: selection.active + + + + + + + + selection.active + 2 + + + 342 + + + + enabled: selection.active + + + + + + + + + 2 + + + 343 + + + + + + + + + + + + + 2 + + + 344 + + + + enabled2: selection.active + + + + + + + enabled2 + + + YES + + YES + NSMultipleValuesPlaceholder + NSNoSelectionPlaceholder + NSNotApplicablePlaceholder + NSNullPlaceholder + + + YES + + + + + + + + 2 + + + 345 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 346 + + + + enabled3: selection.prefs.limitPasteboardSize + + + + + + + enabled3 + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 347 + + + + + + + + + + + + + 2 + + + 348 + + + + + + + + + + + + + 2 + + + 349 + + + + + + + + + + + + + 2 + + + 350 + + + + + + + + + + + + + 2 + + + 352 + + + + value: selection.prefs.autocheckVersion + + + + + + + + selection.prefs.autocheckVersion + 2 + + + 355 + + + + + + + + + + + + + 2 + + + 356 + + + + + + + + + + + + + 2 + + + 383 + + + + value: selection.prefs.enableEncryption + + + + + + + + selection.prefs.enableEncryption + 2 + + + 384 + + + + enabled: selection.prefs.enableEncryption + + + + + + + + + 2 + + + 386 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 387 + + + + enabled: selection.prefs.requireKey + + + + + + + + + 2 + + + 399 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 400 + + + + selectedTag: selection.prefs.switchKeyTag + + + + + + + selectedTag + selection.prefs.switchKeyTag + 2 + + + 401 + + + + + + + + + + + + + 2 + + + 403 + + + + value: selection.prefs.copyFiles + + + + + + + + selection.prefs.copyFiles + 2 + + + 405 + + + + + + + + 407 + + + + + + + + + + + + + 2 + + + 419 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 420 + + + + value: selection.prefs.requirePasteboardKey + + + + + + + + selection.prefs.requirePasteboardKey + 2 + + + 421 + + + + enabled: selection.prefs.requirePasteboardKey + + + + + + + + + 2 + + + 422 + + + + enabled2: selection.prefs.sharePasteboard + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 423 + + + + enabled3: selection.active + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 424 + + + + selectedTag: selection.prefs.pasteboardKeyTag + + + + + + + + selection.prefs.pasteboardKeyTag + 2 + + + 425 + + + + + + + + + + + + + 2 + + + 431 + + + + + + + + + + + + + 2 + + + 432 + + + + selectedTag: selection.prefs.trustRequestBehavior + + + + + + + + selection.prefs.trustRequestBehavior + 2 + + + 442 + + + + + + + + + + + + + 2 + + + 445 + + + + value: selection.prefs.wakeOnLAN + + + + + + + + selection.prefs.wakeOnLAN + 2 + + + 446 + + + + value: selection.prefs.switchDelay + + + + + + + + selection.prefs.switchDelay + 2 + + + 450 + + + + enabled: selection.prefs.delayedSwitch + + + + + + + + + 2 + + + 451 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 452 + + + + enabled2: selection.localHost.supportDragNDrop + + + + + + + + selection.localHost.supportDragNDrop + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 453 + + + + + + + + + + + + + 2 + + + 454 + + + + value: selection.prefs.maxPasteboardSize + + + + + + + + selection.prefs.maxPasteboardSize + 2 + + + 455 + + + + showCertificateButton + + + + 300281 + + + + chooseCertificateButton + + + + 300282 + + + + showCertificateViewer: + + + + 300283 + + + + showCertificateChooser: + + + + 300284 + + + + + + + + + + + + + 2 + + + 300293 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 300294 + + + + value: selection.prefs.hideControlBezel + + + + + + + + selection.prefs.hideControlBezel + + NSValueTransformerName + NSNegateBoolean + + 2 + + + 300300 + + + + + + + + + + + + + 2 + + + 300301 + + + + value: selection.prefs.switchWithDoubleTap + + + + + + + + selection.prefs.switchWithDoubleTap + 2 + + + 300305 + + + + + + + + + + + + + 2 + + + 300306 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + -3 + + + Application + + + 12 + + + YES + + + + PrefPane + + + 6 + + + YES + + + + + + 201 + + + YES + + + + + + + + + 136 + + + YES + + + + + + 172 + + + YES + + + + + + 174 + + + YES + + + + + + 186 + + + YES + + + + + + + + 187 + + + YES + + + + + + 189 + + + YES + + + + + + + 137 + + + + + 152 + + + YES + + + + + + 188 + + + YES + + + + + + 190 + + + YES + + + + + + 357 + + + YES + + + + + + 358 + + + YES + + + + + + 161 + + + YES + + + + About + + + 162 + + + YES + + + + + + + + + + + + 164 + + + YES + + + + + + 165 + + + YES + + + + + + 166 + + + YES + + + + + + 168 + + + YES + + + + + + 291 + + + YES + + + + + + 292 + + + YES + + + + + + 293 + + + YES + + + + + + 297 + + + PrefPaneController + + + 406 + + + TrustedHostsTable + + + 100136 + + + + + 100172 + + + + + 100174 + + + + + 100152 + + + + + 100164 + + + + + 100165 + + + + + 100166 + + + + + 100168 + + + + + 100291 + + + + + 100292 + + + + + 100293 + + + + + 300315 + + + YES + + + + + + + + + + + + + + 441 + + + YES + + + + + + 100441 + + + + + 300279 + + + YES + + + + + + 300280 + + + + + 385 + + + YES + + + + + + 100385 + + + + + 381 + + + YES + + + + + + 100381 + + + + + 300297 + + + YES + + + + + + 300298 + + + + + 434 + + + YES + + + + + + + + + 100434 + + + + + 439 + + + + + 438 + + + + + 437 + + + + + 246 + + + YES + + + + + + + + + 300246 + + + + + 200246 + + + + + 100246 + + + + + 247 + + + YES + + + + + + + + 360 + + + YES + + + + + + 249 + + + YES + + + + + + 248 + + + YES + + + + + + 100248 + + + + + 100249 + + + + + 361 + + + + + 440 + + + YES + + + + + + 100440 + + + + + 362 + + + YES + + + + + + 100362 + + + + + 300316 + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + 325 + + + YES + + + + + + 100325 + + + + + 363 + + + YES + + + + + + 100363 + + + + + 367 + + + YES + + + + + + 100367 + + + + + 366 + + + YES + + + + + + 100366 + + + + + 264 + + + YES + + + + + + 100264 + + + + + 402 + + + YES + + + + + + 100402 + + + + + 411 + + + YES + + + + + + 100411 + + + + + 191 + + + YES + + + + + + 100191 + + + + + 194 + + + YES + + + + + + 100194 + + + + + 412 + + + YES + + + + + + 100412 + + + YES + + + + + + 413 + + + YES + + + + + + + + + + 414 + + + + + 415 + + + + + 416 + + + + + 417 + + + + + 418 + + + + + 448 + + + YES + + + + + + 100448 + + + + + 443 + + + YES + + + + + + 100443 + + + + + 271 + + + YES + + + + + + 100271 + + + + + 300302 + + + YES + + + + + + 300303 + + + + + 326 + + + YES + + + + + + 100326 + + + YES + + + + + + 334 + + + + + 389 + + + YES + + + + + + 100389 + + + YES + + + + + + 390 + + + YES + + + + + + + + + + 391 + + + + + 392 + + + + + 393 + + + + + 394 + + + + + 395 + + + + + 300295 + + + YES + + + + + + 300296 + + + + + 336 + + + YES + + + + + + 100336 + + + + + 368 + + + YES + + + + + + 100368 + + + + + 447 + + + YES + + + + + + 100447 + + + + + 449 + + + YES + + + + + + 100449 + + + + + 353 + + + YES + + + + + + 100353 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + -3.ImportedFromIB2 + 100246.IBShouldRemoveOnLegacySave + 100248.IBShouldRemoveOnLegacySave + 100249.IBShouldRemoveOnLegacySave + 12.IBPluginDependency + 12.IBWindowTemplateEditedContentRect + 12.ImportedFromIB2 + 12.editorWindowContentRectSynchronizationRect + 12.windowTemplate.hasMaxSize + 12.windowTemplate.hasMinSize + 12.windowTemplate.maxSize + 12.windowTemplate.minSize + 136.IBAttributePlaceholdersKey + 136.IBPluginDependency + 136.ImportedFromIB2 + 137.IBAttributePlaceholdersKey + 137.IBPluginDependency + 137.ImportedFromIB2 + 152.IBPluginDependency + 152.ImportedFromIB2 + 161.IBPluginDependency + 161.IBWindowTemplateEditedContentRect + 161.ImportedFromIB2 + 161.editorWindowContentRectSynchronizationRect + 161.windowTemplate.hasMaxSize + 161.windowTemplate.hasMinSize + 161.windowTemplate.maxSize + 161.windowTemplate.minSize + 162.IBPluginDependency + 162.ImportedFromIB2 + 164.IBPluginDependency + 164.ImportedFromIB2 + 165.IBPluginDependency + 165.ImportedFromIB2 + 166.CustomClassName + 166.IBPluginDependency + 166.ImportedFromIB2 + 168.IBPluginDependency + 168.ImportedFromIB2 + 172.IBAttributePlaceholdersKey + 172.IBPluginDependency + 172.ImportedFromIB2 + 174.IBAttributePlaceholdersKey + 174.IBPluginDependency + 174.ImportedFromIB2 + 186.IBPluginDependency + 186.ImportedFromIB2 + 187.IBPluginDependency + 187.ImportedFromIB2 + 188.IBPluginDependency + 188.ImportedFromIB2 + 189.IBPluginDependency + 189.ImportedFromIB2 + 190.IBPluginDependency + 190.ImportedFromIB2 + 191.IBPluginDependency + 191.ImportedFromIB2 + 194.IBPluginDependency + 194.ImportedFromIB2 + 200246.IBShouldRemoveOnLegacySave + 201.IBPluginDependency + 201.ImportedFromIB2 + 246.IBPluginDependency + 246.ImportedFromIB2 + 247.CustomClassName + 247.IBPluginDependency + 247.ImportedFromIB2 + 248.IBPluginDependency + 248.ImportedFromIB2 + 249.IBPluginDependency + 249.ImportedFromIB2 + 264.IBPluginDependency + 264.ImportedFromIB2 + 271.IBPluginDependency + 271.ImportedFromIB2 + 291.CustomClassName + 291.IBPluginDependency + 291.ImportedFromIB2 + 292.CustomClassName + 292.IBPluginDependency + 292.ImportedFromIB2 + 293.CustomClassName + 293.IBPluginDependency + 293.ImportedFromIB2 + 297.IBPluginDependency + 297.ImportedFromIB2 + 300246.IBShouldRemoveOnLegacySave + 300279.IBPluginDependency + 300279.ImportedFromIB2 + 300295.IBPluginDependency + 300295.ImportedFromIB2 + 300297.IBPluginDependency + 300297.ImportedFromIB2 + 300302.IBAttributePlaceholdersKey + 300302.IBPluginDependency + 300302.ImportedFromIB2 + 325.IBPluginDependency + 325.ImportedFromIB2 + 326.IBPluginDependency + 326.ImportedFromIB2 + 334.IBPluginDependency + 334.ImportedFromIB2 + 336.IBPluginDependency + 336.ImportedFromIB2 + 353.IBPluginDependency + 353.ImportedFromIB2 + 357.IBPluginDependency + 357.ImportedFromIB2 + 358.IBPluginDependency + 358.ImportedFromIB2 + 360.IBPluginDependency + 360.ImportedFromIB2 + 361.IBPluginDependency + 361.ImportedFromIB2 + 362.IBPluginDependency + 362.ImportedFromIB2 + 363.IBPluginDependency + 363.ImportedFromIB2 + 366.IBPluginDependency + 366.ImportedFromIB2 + 367.IBPluginDependency + 367.ImportedFromIB2 + 368.IBPluginDependency + 368.ImportedFromIB2 + 381.IBPluginDependency + 381.ImportedFromIB2 + 385.IBPluginDependency + 385.ImportedFromIB2 + 389.IBPluginDependency + 389.ImportedFromIB2 + 390.IBPluginDependency + 390.ImportedFromIB2 + 390.editorWindowContentRectSynchronizationRect + 391.IBPluginDependency + 391.ImportedFromIB2 + 392.IBPluginDependency + 392.ImportedFromIB2 + 393.IBPluginDependency + 393.ImportedFromIB2 + 394.IBPluginDependency + 394.ImportedFromIB2 + 395.IBPluginDependency + 395.ImportedFromIB2 + 402.IBPluginDependency + 402.ImportedFromIB2 + 406.IBPluginDependency + 406.ImportedFromIB2 + 411.IBPluginDependency + 411.ImportedFromIB2 + 412.IBPluginDependency + 412.ImportedFromIB2 + 413.IBPluginDependency + 413.ImportedFromIB2 + 413.editorWindowContentRectSynchronizationRect + 414.IBPluginDependency + 414.ImportedFromIB2 + 415.IBPluginDependency + 415.ImportedFromIB2 + 416.IBPluginDependency + 416.ImportedFromIB2 + 417.IBPluginDependency + 417.ImportedFromIB2 + 418.IBPluginDependency + 418.ImportedFromIB2 + 434.IBPluginDependency + 434.ImportedFromIB2 + 437.IBPluginDependency + 437.ImportedFromIB2 + 438.IBPluginDependency + 438.ImportedFromIB2 + 439.IBPluginDependency + 439.ImportedFromIB2 + 440.IBPluginDependency + 440.ImportedFromIB2 + 441.IBPluginDependency + 441.ImportedFromIB2 + 443.IBAttributePlaceholdersKey + 443.IBPluginDependency + 443.ImportedFromIB2 + 447.IBPluginDependency + 447.ImportedFromIB2 + 448.IBPluginDependency + 448.ImportedFromIB2 + 449.IBPluginDependency + 449.ImportedFromIB2 + 6.IBPluginDependency + 6.ImportedFromIB2 + + + YES + + + + + + + + + {{143, 174}, {595, 429}} + + + + + {3.40282e+38, 3.40282e+38} + {224.664, 10} + + ToolTip + + + + Anhaken, um anderen teleport-Benutzern die Steuerung Ihres Macs zu erlauben. + + + + + + + + + + Dies ist der Layout-Bereich. Verteilen Sie die zu steuernden Macs nach Belieben, um sie zu steuern. + + + + + + + + {{121, 361}, {295, 307}} + + + + + + {213, 107} + + + + + + + TPVersionTextField + + + + + + + + + + QWJvdXQgdGVsZXBvcnTigKY + + + + + + + + + + Anhaken, um teleport zu aktivieren. + + + + + + + + + + + + + + + + + + + + + + + + TPTableView + + + + + + + + + + + TPLinkTextField + + + + + + + + + + + + + + + + + + + + + + + RG9wcGVsYmVyw7xocnVuZyBiZWRldXRldCwgZGFzcyBkZXIgQmlsZHNjaGlybXJhbmQgendlaW1hbCBi +ZXLDvGhydCB3ZXJkZW4gbXVzcywgYmV2b3IgZGVyIFdlY2hzZWwgZHVyY2hnZWbDvGhydCB3aXJkLg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{523, 401}, {170, 103}} + + + + + + + + + + + + + + + + + + + + + {{550, 223}, {170, 103}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + RnVua3Rpb25pZXJ0IG51ciwgd2VubiBkaWUgUmVjaG5lciBwZXIgRXRoZXJuZXQgdmVyYnVuZGVuIHNp +bmQgdW5kIGRpZSBPcHRpb24gIkJlaSBhZG1pbmlzdHJhdGl2ZW4gRXRoZXJuZXQtWnVncmlmZmVuIGF1 +ZndhY2hlbiIgaW0gQmVyZWljaCAiRW5lcmdpZSBzcGFyZW4iIGRlciBTeXN0ZW1zdGV1ZXJ1bmcgYWt0 +aXZpZXJ0IGlzdC4 + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 300316 + + + + YES + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBUserSource + + + + + + + + YES + + YES + scroll: + setScaleFactor: + + + YES + id + + + + + + + + + IBProjectSource + TPLayoutView.h + + + + NSObject + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPUDPSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection_Private.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPDaemonManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPPreferencesManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTransfersManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSecureSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPConnectionsManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTableView.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPHotBorder.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSocket.h + + + + + NSTextField + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPLinkTextField.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPClosingWindow.h + + + + + + + YES + + YES + + + YES + + + + + + + + + + + + + NSPreferencePane + + + YES + + YES + addOther: + changeTabPanel: + + closeAddOther: + closeTrustedCertificate: + confirmAddOther: + deleteTrustedHost: + + showCertificate: + showTrustedHosts: + + + YES + + + + + + + + + + + + + + YES + + YES + + + + + + + addOtherAddressField + addOtherHeightField + addOtherNameField + addOtherSheet + addOtherWidthField + + certificateView + certificateWindow + + + + + + + + + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPVersionTextField.h + + + + + + + : + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + + + + + + + + + YES + + + + + + + + + + + + YES + + YES + + + + + + + + + + + + + + + YES + + + + + + + + + + + + + + + + + TPPreferencePane.h + + + + + 0 + ../../teleport.xcodeproj + 3 + + YnBsaXN0MDDUAAEAAgADAAQABQAGAAkAClgkdmVyc2lvblQkdG9wWSRhcmNoaXZlclgkb2JqZWN0cxIA +AYag0QAHAAhdSUIub2JqZWN0ZGF0YYABXxAPTlNLZXllZEFyY2hpdmVyrxEEBQALAAwAMQA1ADYAPAA9 +AEEARQCfAKcAtQC/AMAA2gDbAOMA5ADnAOsA7ADvAPAA9AD6APsA/wEEAQ4BFgALARcBIgEjASYBKgEr +ATABOAFBAUIBSgFLAUwBUQFiAWYBbgFyAX4BhwGIAZIBkwGYAaIBowGkAakBqwGwAbEBtAG3AboBwgHD +AcoBywHNAdUB1gHeAd8B5wHoAfAB8QIMAg0CEwIbAhwCHwIgAiICKwIsAjYCNwI7AjwA8wI9AkICQwJG +AkkCXwJnAnYCegKZApoCmwKcAqMCrQKuArECtwK4ArsCwQLTAtQC2wLcAt8C5ALlAugC8QLyAvcC+AL7 +AwQDBQMLAwwDEwMbAxwDIgMjAygDKQMzAzQDOQM6Az0DQANBA0QDUANRA1IDVQNeA18DYwNkA2UDaANw +A3EDeAN5A4EDggOJA4oA+AOLA44DjwORA5IDmAOgA6EDpgOrA7MDtAO1A70DvgPGA44DxwPIA8sD0QPS +A9kD2gPfA+MD/AQDBAQEDAQNBBQEFQQcBB0EJAQlBCwELQQ0BDUEPAQ9BEQERQRNBE4EVQRWBF4EXwRm +BGcEbwRwBHcEeASABIcEiASdBJ8EsgS3BLgEvQS+BL8EwgTGBMcEyATKBM0E1QTfBMcE4ATqBMcE6wT1 +BMcE9gUABMcFAQUDBQcFCgURBRIFGgUbBSIFIwUrBSwFMwU0BTwFPQVEBUUFTQVOBVUFVgVjBYUFogWj +BaQFpQWmBacFqAWpBaoFqwWsBa0FrgW5BbwFvQXCBcMFxwXKBc0F0QXSBdMF1wEbBdoA2AXfBeAF4wDv +BeYF6gXrBe4F7wX0BfUF+gX7BgIGAwYPBhEGGgTHBh4GIAYoBMcGMQTHBjoExwZDBMcGTAZTBlQGXAZd +BmQGZQZsBm0GbwZ2BncGfgZ/BoYGhwabBp0GoQaiBqUGqAavBrAGtwa4Br8GwAbIA4oDjgbJBsoGzAbN +BtIG0wbYBt8G5gbuBu8G+Ab5BwIHAwcOBw8HEwcUBxYHFwcYBx0HIgc4BzkHOgc9AAsHRAdOB1oHZAdl +B2YHZwdoB2kHagdrB2wHdgd6B3sHfgeBB4kHigeSB5YHlwehAbkHogejB6oHqwezB7QHvAe9B8IHzQfO +B88H2QfaB90H3gfoB+kH8QfyB/MH/Qf+CAYIBwgIAAsICQgKCAsIDAgPCBQIFQgaCB8IIAglCCYIKQgu +CDYIOgKaCDsIPQg/CEMIRAhJCEoITwhQCFUIVghbCFwIYQhiCGcIaAhtCG4Icwh0CHkIegh/CIAIhQiG +CIsIjAiRCJwInQEFCJ4IoAikAAsICQilCAsIpginCKwIsQjKCMsIzAjNCM4IzwjQCNEI0gjTCNQI1QjW +CNcI2AjZCNoI2wjcCN0I3gjfCOAI4gjnCOgI8gjzCPQI9Qj4CP8JAAkBCQIJCQkQCRcJHgkfCSAJIQko +CSkJKgkxCTIJMwk6CTsJPAlHCUgJSQlVCVYJVwlYCVkJWglhCWIJYwlsCXgJfwmACYEJiAmPCZAJkQmY +CZ8JoAmhCagJsQm9CcQJxQnGCc0JzgnPCdYJ3QnmCfIJ+Qn6CfsKAgoJCgoKEwofCiYKJwooCi8KMAox +CjoKOwpHClAKUQpSCl4KZQpmCmcKbgp3CngKeQqFCo0KjgqPCpUKlgqXCp4KpQqsCq0Krgq1CrYKvQrE +CsUKzArVCuEK6grrCvcK/gsFCwwLDQsWCyILKQswCzELMgs5CzoLQQtCC0kLSgtLC04LUwtUC1kLWgtf +C2ALZQtmC2sLbAvuC/EL8gv0DHYM+Q18DX0Nfg1/DYANgQ2CDYMNhA2FDYYNhw2IDYkNig2LDYwNjQ2O +DY8NkA2RDZINkw2UDZUNlg2XDZgNmQ2aDZsNnA2dDZ4Nnw2gDaENog2jDaQNpQ2mDacNqA2pDaoNqw2s +Da0Nrg2vDbANsQ2yDbMNtA21DbYNtw24DbkNug27DbwNvQ2+Db8NwA3BDcINww3EDcUNxg3HDcgNyQ3K +DcsNzA3NDc4Nzw3QDdEN0g3TDdQN1Q3WDdcN2A3ZDdoN2w3cDd0N3g3fDeAN4Q3iDeMN5A3lDeYHPA3n +DegN6Q3qDesN7A3tDe4N7w3wDfEN8g3zDfQN9Q32DfcN+A35DfoN+w4DDgsO5Q+/D8APwQ/CD8MPxA/F +D8YPxw/ID8kPyg/LD8wPzQ/OD88P0A/RD9IP0w/UD9UP1g/XD9gP2Q/aD9sHyA/cD90P3g/fD+AP4Q/i +D+MP5A/lD+YP5w/oD+kP6g/rD+wP7Q/uD+8P8A/xD/IP8w/0D/UP9g/3D/gP+Q/6D/sP/A/9D/4P/xAA +EAEQAhADEAQQBRAGEAcQCBAJEAoQCxAMEA0QDhAPEBAQERASEBMQFBAVEBYQFwGDEBgQGRAaEBsQHBAd +EB4QHxAgECEQIhAjECQQJQGfECYQJxAoECkQKhArECwQLRAuEC8QMBAxEDIQMxA0EDUQNhA3EDgCNRA5 +EDoQOxA8ED0QPhA/EEAQQRBCEEMQRBBFEEYQRxBIEEkQShBLEEwQTRBOEE8A1xBQEFEQUhBTEFQQVRBW +EFcQWBBZEFoQWxBcEF0QXhBfEGAQYRBiEGMQZBBlEGYQZxBoEGkQahBrEGwQbRBuEG8QcBBxEHIQcxB0 +EHUQdhB3EHgQeRB6EHsQfBB9EH4QfxCAEIEQghCDEIQQhRCGEIcQiBCJEIoQixCMEI0QjhCPEJAQkRCU +EJcQmlUkbnVsbN8QEgANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMA +JAAlACYAJwAoACkAKgArACwALQAuAC8AMFZOU1Jvb3RWJGNsYXNzXU5TT2JqZWN0c0tleXNfEA9OU0Ns +YXNzZXNWYWx1ZXNfEBlOU0FjY2Vzc2liaWxpdHlPaWRzVmFsdWVzXU5TQ29ubmVjdGlvbnNbTlNOYW1l +c0tleXNbTlNGcmFtZXdvcmtdTlNDbGFzc2VzS2V5c1pOU09pZHNLZXlzXU5TTmFtZXNWYWx1ZXNfEBlO +U0FjY2Vzc2liaWxpdHlDb25uZWN0b3JzXU5TRm9udE1hbmFnZXJfEBBOU1Zpc2libGVXaW5kb3dzXxAP +TlNPYmplY3RzVmFsdWVzXxAXTlNBY2Nlc3NpYmlsaXR5T2lkc0tleXNZTlNOZXh0T2lkXE5TT2lkc1Zh +bHVlc4ACgQQEgQKfgQMngQQDgAiBAqSABYEDJoEDKIECpYEEAYAAgAaBAqOBBAISAASVJIEDKdIADgAy +ADMANFtOU0NsYXNzTmFtZYAEgANfEBBUUFByZWZlcmVuY2VQYW5l0gA3ADgAOQA6WCRjbGFzc2VzWiRj +bGFzc25hbWWiADoAO15OU0N1c3RvbU9iamVjdFhOU09iamVjdF8QEElCQ29jb2FGcmFtZXdvcmvSAA4A +PgA/AEBaTlMub2JqZWN0c4AHoNIANwA4AEIAQ6MAQwBEADtcTlNNdXRhYmxlU2V0VU5TU2V00gAOAD4A +RgBHgFmvEFcASABJAEoASwBMAE0ATgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABh +AGIAYwBkAGUAZgBnAGgAaQBqAGsAbABtAG4AbwBwAHEAcgBzAHQAdQB2AHcAeAB5AHoAewB8AH0AfgB/ +AIAAgQCCAIMAhACFAIYAhwCIAIkAigCLAIwAjQCOAI8AkACRAJIAkwCUAJUAlgCXAJgAmQCaAJsAnACd +AJ6ACYAagCSAK4EBboEBcIEBgIEBgYEBv4EBwYEBwoEBxIEBx4EB0IEB0oEB1IEB1oEB2IEB2oEB3IEB +3oEB4IEB4oEB5IEB5oEB6IEB9YECD4ECEYECFoECGoECG4ECHIECHYECIYECJIECJ4ECKoECM4ECNoEC +OIECO4ECPIECP4ECQIECQ4ECRIECRoECSYECTIECTYECToECUIECU4ECVIECVoECWIECW4ECXoECYYEC +ZYECaIECaYECbYECc4ECdIECdYECeIECeoECe4ECfYECfoECgIECg4EChIEChYECh4ECiYECioECjYEC +j4ECkYEClYECl4ECmYECm4ECndQADgCgAKEAogCjAKQAHwCmXU5TRGVzdGluYXRpb25YTlNTb3VyY2VX +TlNMYWJlbIAZgAqAAoAY1wCoAA4AqQCqAKsArACtAK4ArwCwALEAsgCzAK5fEA9OU05leHRSZXNwb25k +ZXJXTlNGcmFtZVZOU0NlbGxYTlN2RmxhZ3NZTlNFbmFibGVkW05TU3VwZXJ2aWV3gAuAF4AMgA0RAQwJ +gAvXAKgADgC2AKsAtwAyAK0AuAC5ALoAuwC8AL0AuFpOU1N1YnZpZXdzW05TRnJhbWVTaXplgLyApYC+ +EQEtgQFpgKSAvF8QF3t7MTgzLCAxNTZ9LCB7MjI2LCAxOH193QDBAA4AwgDDAMQAxQDGAMcAyADJAMoA +ywDMAM0AzgDPANAA0QDSAM8A1ADVAKQA1wDYANlbTlNDZWxsRmxhZ3NfEBNOU0FsdGVybmF0ZUNvbnRl +bnRzXxASTlNQZXJpb2RpY0ludGVydmFsXk5TQnV0dG9uRmxhZ3MyXxAQTlNBbHRlcm5hdGVJbWFnZV8Q +D05TS2V5RXF1aXZhbGVudFpOU0NvbnRlbnRzWU5TU3VwcG9ydF1OU0NvbnRyb2xWaWV3XxAPTlNQZXJp +b2RpY0RlbGF5XE5TQ2VsbEZsYWdzMl1OU0J1dHRvbkZsYWdzE/////+EAf4AgBaAFRAZEAKAEoAVgA6A +D4AKEMgQABJIPFH/XxAeWndpc2NoZW5hYmxhZ2Ugc3luY2hyb25pc2llcmVu1AAOANwA3QDeAN8A4ADh +AOJWTlNTaXplVk5TTmFtZVhOU2ZGbGFnc4ARI0AqAAAAAAAAgBARBBRcTHVjaWRhR3JhbmRl0gA3ADgA +5QDmogDmADtWTlNGb2500gAOAOgA6QDqW05TSW1hZ2VOYW1lgBSAE1hOU1N3aXRjaNIANwA4AO0A7qIA +7gA7XxATTlNCdXR0b25JbWFnZVNvdXJjZVDSADcAOADxAPKkAPIA8wCqADtcTlNCdXR0b25DZWxsXE5T +QWN0aW9uQ2VsbNIANwA4APUA9qUA9gD3APgA+QA7WE5TQnV0dG9uWU5TQ29udHJvbFZOU1ZpZXdbTlNS +ZXNwb25kZXJfEBdzaGFyZVBhc3RlYm9hcmRDaGVja2JveNIANwA4APwA/aMA/QD+ADtfEBROU05pYk91 +dGxldENvbm5lY3Rvcl5OU05pYkNvbm5lY3RvctQADgCgAKEAogCjAQEAHwEDgBmAG4ACgCPYAKgADgCp +AKoAqwCsAQUArQEGAK8BCAEJAQoAswEMAQZYTlNXaW5kb3eAHIAXgB2AHxP/////gAABDAmAHoAc2ACo +AA4AtgCrALcBBQAyAK0BDwC5AREAuwESAQwAvQEPgC6ApYAwgKOAHoCkgC5fEBd7ezE3MiwgMjUwfSwg +ezE4MSwgMzJ9fdwAwQAOAMIAwwDEAMYAxwDIAMkAygDLAMwBGADOAM8A0AEbARwBHQDVAQEA1wEgASES +BAH+AIAWgBUQAYAhgCCAD4AbEggAAAAT/////4aCQP9vEBUAWgBlAHIAdABpAGYAaQBrAGEAdAAgAGEA +dQBzAHcA5ABoAGwAZQBuICbSAA4BJAElAO9ZTlMuc3RyaW5ngCLSADcAOAEnASijASgBKQA7XxAPTlNN +dXRhYmxlU3RyaW5nWE5TU3RyaW5nXxAXY2hvb3NlQ2VydGlmaWNhdGVCdXR0b27UAA4AoAChAKIAowEt +AB8BL4AZgCWAAoAq2ACoAA4AqQCqAKsArAEFAK0BMQCvATMBNACyALMBDAExgCaAF4AngCgJgB6AJtgA +qAAOALYAqwC3AQUAMgCtATkAuQE7ATwBPQEMAL0BOYEBcYClgQFyEQESgQF+gB6ApIEBcV8QFnt7MTgs +IDM4N30sIHsxMzgsIDE4fX3dAMEADgDCAMMAxADFAMYAxwDIAMkAygDLAMwBGADOAM8A0ADRANIAzwFH +ANUBLQDXANgA2YAWgBWAEoAVgCmAD4AlXxATdGVsZXBvcnQgYWt0aXZpZXJlbl8QEmFjdGl2YXRpb25D +aGVja2JveNQADgCgAKEAogCjAB8BTwFQgBmAAoAsgQFt3QCoAA4BUgFTAKkA5gC2AKsBBQFUAK0BVQFW +ATEBWAFZANgBWgDVAVwBPAEMALMBMQCzAWFeTlNUYWJWaWV3SXRlbXNZTlNUdkZsYWdzXxARTlNEcmF3 +c0JhY2tncm91bmRfEBZOU0FsbG93VHJ1bmNhdGVkTGFiZWxzXxAVTlNTZWxlY3RlZFRhYlZpZXdJdGVt +gCaBAWyAqYCogA+ALYAeCYAmCYC40gAOAD4ARgFkgFmhAQ+ALtcAqAAOAKkAtgCrAQUArQFPAWgBaQFq +AWsBDAFPgCyAp4CmgC8RAQCAHoAs0gAOAD4ARgFwgFmhAQaAHNIADgA+AEYBdIBZqQF1AQEBdwF4AXkB +egF7AXwBfYAxgBuAQIBFgEmATYBhgJuAn9gAqAAOAKkAqgCrAKwBBQCtAQYBgAGBAYIBgwCzAQwBBoAc +gD+AMoAzEQEkCYAegBxfEBZ7ezE4MiwgODd9LCB7MzQxLCAyOH192ADBAA4BiQDHAMgAyQDLAYoBGAGL +AYwBjQGOAXUBkAGRXxARTlNCYWNrZ3JvdW5kQ29sb3JbTlNUZXh0Q29sb3KAPoA2gDSANYAxEhBCAACA +O28QWQBFAGkAbgB0AHIA5ABnAGUAIABpAG4AIABkAGUAcgAgAEwAaQBzAHQAZQAgAGsA9gBuAG4AZQBu +ACAAZAB1AHIAYwBoACAAQQB1AHMAdwBhAGgAbAAgAHUAbgBkACAARAByAPwAYwBrAGUAbgAgAGQAZQBy +ACAATAD2AHMAYwBoAGUAbgAtAFQAYQBzAHQAZQAgAGUAbgB0AGYAZQByAG4AdAAgAHcAZQByAGQAZQBu +AC7UAA4A3ADdAN4A3wGVAOEBl4ARI0AmAAAAAAAAgBARDBzVAA4BmQGaAZsBnAGdAZ4BnwGgAaFXTlND +b2xvclxOU0NvbG9yU3BhY2VbTlNDb2xvck5hbWVdTlNDYXRhbG9nTmFtZYA6gDkQBoA4gDdWU3lzdGVt +XGNvbnRyb2xDb2xvctMADgGaAaUBnQGnAahXTlNXaGl0ZYA6EANLMC42NjY2NjY2OQDSADcAOAGqAZmi +AZkAO9UADgGZAZoBmwGcAZ0BrQGfAa4BoYA6gD2APIA3XxAQY29udHJvbFRleHRDb2xvctMADgGaAaUB +nQGnAbOAOkIwANIANwA4AbUBtqQBtgDzAKoAO18QD05TVGV4dEZpZWxkQ2VsbNIANwA4AbgBuaUBuQD3 +APgA+QA7W05TVGV4dEZpZWxk2ACoAA4AqQCqAKsArAEFAK0BBgCvAb0BvgCyALMBDAEGgByAF4BBgEIJ +gB6AHF8QF3t7MTcyLCAyNTB9LCB7MTcyLCAzMn193ADBAA4AwgDDAMQAxgDHAMgAyQDKAMsAzAEYAM4A +zwDQARsBxgHHANUBdwDXASABIYAWgBWARIBDgA+AQG8QFABaAGUAcgB0AGkAZgBpAGsAYQB0ACAAYQBu +AHoAZQBpAGcAZQBuICbSAA4BJAElAO+AItgAqAAOAKkAqgCrAKwBBQCtAQYArwHQAdEAsgCzAQwBBoAc +gBeARoBHCYAegBxfEBd7ezE4MywgMjg2fSwgezE5MSwgMTh9fd0AwQAOAMIAwwDEAMUAxgDHAMgAyQDK +AMsAzAEYAM4AzwDQANEA0gDPAdsA1QF4ANcA2ADZgBaAFYASgBWASIAPgEVvEBoAVgBlAHIAcwBjAGgA +bAD8AHMAcwBlAGwAdQBuAGcAIABhAGsAdABpAHYAaQBlAHIAZQBu2ACoAA4AqQCqAKsArAEFAK0BBgGA +AeIB4wCyALMBDAEGgByAP4BKgEsJgB6AHF8QFnt7MjMsIDI4N30sIHsxNDgsIDE3fX3YAMEADgGJAMcA +yADJAMsBigEYAYsBjAHrANUBeQHuAZGAPoA2gEyAD4BJEgRAAACAO28QEABWAGUAcgBzAGMAaABsAPwA +cwBzAGUAbAB1AG4AZwA63xATAKgADgCpAfIB8wH0AYkB9QH2AOYB9wCrAKwBBQH4AK0B+QH6AfsBBgH9 +Af4B/wGnAgABjAICAgMA1QIFAYMAswEMARsBBgIJAgoCC1tOU1Byb3RvQ2VsbFlOU051bVJvd3NeTlNT +ZWxlY3RlZENlbGxbTlNDZWxsQ2xhc3NfEBVOU0NlbGxCYWNrZ3JvdW5kQ29sb3JaTlNDZWxsU2l6ZVlO +U051bUNvbHNfEBJOU0ludGVyY2VsbFNwYWNpbmddTlNNYXRyaXhGbGFnc1dOU0NlbGxzgByAYIBOgF2A +UIA2gFyAX4APgFoJgB6AHIBbEkQoIACAT18QFnt7MTg1LCAyM30sIHszMzUsIDU4fX3SAA4APgBGAg+A +WaMCAAIRAhKAUIBVgFfdAMEADgDCAMMAxADFAMYAxwDIAMkAygDLAMwAzQDOAhUA0ADYAhYCFQIYANUB +egDXANgA2YAWgFSAUoBUgFGAD4BNXxAtQWJmcmFnZSwgb2IgZGllIEFuZnJhZ2UgYW5nZW5vbWVuIHdl +cmRlbiBzb2xs0gAOAOgA6QIegBSAU11OU1JhZGlvQnV0dG9u0gAOASQBJQDvgCLeAMEADgDCAMMAxADF +AMYAxwDIAMkAygDLAMwCIwEYAM4CFQDQANgCFgIVAigA1QF6ANcA2ADZARtVTlNUYWeAFoBUgFKAVIBW +gA+ATV8QKUFid2Vpc2VuLCB3ZW5uIG5pY2h0IGJlcmVpdHMgaW4gZGVyIExpc3Rl3gDBAA4AwgDDAMQA +xQDGAMcAyADJAMoAywDMAiMBGADOAM8CLwDYAhYAzwIyANUBegI1ANgA2QDRgBaAFRBLgFKAFYBYgA+A +TREBkF8QFEF1dG9tYXRpc2NoIGFubmVobWVu0gA3ADgCOAI5owI5AjoAO15OU011dGFibGVBcnJheVdO +U0FycmF5WXszMzUsIDE4fVZ7NCwgMn3aAMEADgDDAMQAxQDHAMgAygDLAMwBGADOAi8A2AIWAkAA1QI1 +ANgA2YAWgFKAXoAPVVJhZGlv0wAOAZoBpQGdAacCRYA6QjEA0gA3ADgCRwJIpQJIAPcA+AD5ADtYTlNN +YXRyaXjfEA8AqAJKAA4AqQJLAkwCTQC2Ak4AqwEFAK0CTwJQAlEBBgJTAlQCVQDRAlYCVwJYAlkBPAEM +AQYCXAJdAl1bTlNIU2Nyb2xsZXJYTlNzRmxhZ3NcTlNDb3JuZXJWaWV3XxAQTlNIZWFkZXJDbGlwVmll +d1xOU1Njcm9sbEFtdHNbTlNWU2Nyb2xsZXJdTlNOZXh0S2V5Vmlld11OU0NvbnRlbnRWaWV3gByAlYCa +gJmAbYBqgGJPEBBBIAAAQSAAAEGYAABBmAAAgB6AHICRgGOAY9IADgA+AEYCYYBZpQJdAlwCUwJXAlaA +Y4CRgJWAaoBt2wCoAA4AqQJoALYAqwJpAQUCagCtAlABewJsAm0CbgJvAnACcQEMAnMBewJxWU5TY3ZG +bGFnc1lOU0RvY1ZpZXdZTlNCR0NvbG9ygGGAkICPEASAZBEJAIBlgB6Ae4BhgGXSAA4APgBGAniAWaEC +cYBl3xAWAKgCewAOAVMCfAJ9AYkCfgJMAn8CgAKBAKsAtwCsAQUAMgKCAoMArQKEAoUCXQDYAocCiAKJ +AooCAwCzAlYCjgEbAo8BawKQALMBDAKTApQClQJdApcCmF8QH05TRHJhZ2dpbmdTb3VyY2VNYXNrRm9y +Tm9uTG9jYWxfEBNOU09yaWdpbmFsQ2xhc3NOYW1lXE5TSGVhZGVyVmlld18QEk5TQWxsb3dzVHlwZVNl +bGVjdF8QF05TSW50ZXJjZWxsU3BhY2luZ1dpZHRoXxAZTlNDb2x1bW5BdXRvcmVzaXppbmdTdHlsZV8Q +GE5TSW50ZXJjZWxsU3BhY2luZ0hlaWdodFtOU0dyaWRDb2xvcl8QHE5TRHJhZ2dpbmdTb3VyY2VNYXNr +Rm9yTG9jYWxeTlNUYWJsZUNvbHVtbnNbTlNSb3dIZWlnaHSAY4COEhrAgACAZ4BpgF8JgG0jQAgAAAAA +AAAjQAAAAAAAAACAaAmAHoBmgIsQD4BjgHAjQDEAAAAAAABbVFBUYWJsZVZpZXdbTlNUYWJsZVZpZXda +ezMzMywgMTA0fdcAqAAOAKsAtwEFAK0CmgJXAp4BawKfAQwCVwJxgGqAbIBrgB6AaoBl2wCoAA4AqQJo +ALYAqwJpAQUCagCtAlABewJsAqYCbgKnAnACigEMAnMBewKKgGGAkICYgJeAaYAegHuAYYBpWXszMzMs +IDE3fdIANwA4Aq8CsKQCsAD4APkAO18QEU5TVGFibGVIZWFkZXJWaWV31gCoAA4AqQCrAQUArQF7ArMC +tAFrAQwBe4BhgG+AboAegGFfEBR7ey0yNiwgMH0sIHsxNiwgMTd9fdIANwA4ArkCuqQCugD4APkAO11f +TlNDb3JuZXJWaWV30gAOAD4ARgK9gFmjAr4CvwLAgHGAfoCD2gLCAA4CwwLEAsUCxgLHAsgCyQKaALMC +ywLMAs0CzgLPARsC0ALRAnFeTlNJc1Jlc2l6ZWFibGVcTlNIZWFkZXJDZWxsXE5TSWRlbnRpZmllcldO +U1dpZHRoWk5TRGF0YUNlbGxeTlNSZXNpemluZ01hc2taTlNNaW5XaWR0aFpOU01heFdpZHRoCYB9gHOA +ciNAYqAAAAAAAIB5I0BEAAAAAAAAI0CPQAAAAAAAgGVUbmFtZdcAwQAOAYkAxwDIAMsBigLVAtYC1wLY +AY4A2ALaEgSB/gCAeIB1gHSANYB2VE5hbWXTAA4BmgGlAZ0BpwLegDpLMC4zMzMzMzI5OQDVAA4BmQGa +AZsBnAGdAa0BnwLiAaGAOoA9gHeAN18QD2hlYWRlclRleHRDb2xvctIANwA4AuYC56UC5wG2APMAqgA7 +XxARTlNUYWJsZUhlYWRlckNlbGzYAMEADgGJAMcAyADJAMsBigLpAYsCcwLsAY4CcQLvAZESFCH+QIA+ +gHuAeoA1gGUSAAIIAIA7WVRleHQgQ2VsbNUADgGZAZoBmwGcAZ0BngGfAvUBoYA6gDmAfIA3XxAWY29u +dHJvbEJhY2tncm91bmRDb2xvctIANwA4AvkC+qIC+gA7XU5TVGFibGVDb2x1bW7aAsIADgLDAsQCxQLG +AscCyALJApoAswLLAv4C/wMAAwEBGwMCAtECcQmAfYCAgH8jQF0AAAAAAACAgiNAIAAAAAAAAIBlV2Fk +ZHJlc3PXAMEADgGJAMcAyADLAYoC1QLWAtcDCAGOANgC2oB4gHWAgYA1gHZXQWRyZXNzZdgAwQAOAYkA +xwDIAMkAywGKAukBiwJzAuwBjgJxAu8BkYA+gHuAeoA1gGWAO9gADgLDAsQCxQLGAsgCyQKaAssDFQMW +AxcDGAMZAtECcYB9gIWAhCNATYAAAAAAAICJI0BNSa/gAAAAgGVbY2VydGlmaWNhdGXXAMEADgGJAMcA +yADLAYoBGALWAx4DHwGOANgC2oB4gIeAhoA1gHZaWmVydGlmaWthdNUADgGZAZoBmwGcAZ0CAwGfAyYB +oYA6gF+AiIA3W2hlYWRlckNvbG9y3ADBAA4AwgDDAMQAxgDHAMgAyQDKAMsAzAEYAM4AzwIvAywAzwMu +AY4CcQI1AzEDMoAWgBUQJIAVgIqANYBlEggCAAAT/////4a2QP9XQW56ZWlnZdUADgGZAZoBmwGcAZ0D +NgGfAzcBoYA6gI2AjIA3WWdyaWRDb2xvctMADgGaAaUBnQGnAzyAOkQwLjUA0gA3ADgDPgM/ogM/ADte +TlNDbGFzc1N3YXBwZXJfEBV7ezEsIDE3fSwgezMzMywgMTA0fX3SADcAOANCA0OkA0MA+AD5ADtaTlND +bGlwVmlld9kAqANFAA4AqQCrAQUDRgCtA0cBewF7A0oDSwFrAQwDTQF7A09YTlNUYXJnZXRYTlNBY3Rp +b25ZTlNQZXJjZW50gGGAYYCUgJKAHoCTgGEjP+2C2CAAAABfEBl7ey0xMDAsIC0xMDB9LCB7MTUsIDE4 +MH19XF9kb1Njcm9sbGVyOtIANwA4A1MDVKUDVAD3APgA+QA7Wk5TU2Nyb2xsZXLaAKgDRQAOAKkCSwCr +AQUDRgCtA0cBewF7A0oDWQEbAWsBDANNAXsDXYBhgGGAlICWgB6Ak4BhIz/vsfsgAAAAXxAZe3stMTAw +LCAtMTAwfSwgezQ2MywgMTV9fdIADgA+AEYDYYBZoQKKgGlfEBN7ezEsIDB9LCB7MzMzLCAxN319XxAY +e3sxODUsIDExNn0sIHszMzUsIDEyMn190gA3ADgDZgNnpANnAPgA+QA7XE5TU2Nyb2xsVmlld9gAqAAO +AKkAqgCrAKwBBQCtAQYBgANrA2wBgwCzAQwBBoAcgD+AnICdCYAegBxfEBV7ezE0LCA2NH0sIHsxNTcs +IDE3fX3YAMEADgGJAMcAyADJAMsBigEYAYsBjAN0ANUBfAHuAZGAPoA2gJ6AD4CbgDtfEBRTdGV1ZXJ1 +bmdzLUFuZnJhZ2VuOtgAqAAOAKkAqgCrAKwBBQCtAQYBgAN8A30AsgCzAQwBBoAcgD+AoIChCYAegBxf +EBV7ezIsIDIwNH0sIHsxNjksIDM0fX3YAMEADgGJAMcAyADJAMsBigEYAYsBjAOFANUBfQHuAZGAPoA2 +gKKAD4CfgDtvEBgAVgBlAHIAdAByAGEAdQBlAG4AcwB3APwAcgBkAGkAZwBlACAASABvAHMAdABzADpa +ezU0OSwgMzE5fdIANwA4A4wDjaQDjQD4APkAO1xOU0N1c3RvbVZpZXdfEBZ7ezEwLCAzM30sIHs1NDks +IDMxOX190gA3ADgDkAD4owD4APkAO18QFnt7MTMsIDEwfSwgezU2OSwgMzY1fX3SAA4APgBGA5SAWaMD +lQFhA5eAqoC4gLrWAA4CxAD4A5kBmQCiA5oDmwOcAU8BjAOfWU5TVGFiVmlld4C3gKuArIAsgDaAtlZs +YXlvdXTVAKgADgCpALYAqwArAWgDpAOlAWuAAICngLWArdIADgA+AEYDqIBZogOpA6qAroCx1wCoAA4A +qQCrADIDrACtA5wAuQOvATwDsAC9A5xbTlNFeHRlbnNpb26ArIClgK+AsICkgKxfEBZ7ezIwLCAxN30s +IHs1MTEsIDI5Nn19XFRQTGF5b3V0Vmlld9cAqAAOAKkAqgCrAKwArQOcAYADuAO5A7oAswOcgKyAP4Cy +gLMRASIJgKxfEBZ7ezE3LCAtMTN9LCB7NTE3LCAyOH192ADBAA4BiQDHAMgAyQDLAYoBGAGLAYwDwQGO +A6oDxAGRgD6ANoC0gDWAsRIAQgAAgDtfEE9HZW1laW5zYW1lIE1hY3MgYXVzIGRlbSBvYmVyZW4gQmVy +ZWljaCBpbSB1bnRlcmVuIHZlcnRlaWxlbiwgdW0gc2llIHp1IHN0ZXVlcm4uVkxheW91dNIANwA4A8kD +yqIDygA7XU5TVGFiVmlld0l0ZW3VAA4A+AOZAZkAogOaAQ8BTwGMA9CAt4AugCyANoC5WlNpY2hlcmhl +aXTWAA4CxAD4A5kBmQCiA5oD1AC4AU8BjAPYgLeAu4C8gCyANoEBa1dvcHRpb25z1QCoAA4AqQC2AKsA +KwFoA90D3gFrgACAp4EBaoC90gAOAD4ARgPhgFmhAK6AC9IADgA+AEYD5YBZrxAWA+YD5wPoA+kD6gPr +A+wApAPuA+8D8APxA/ID8wP0A/UD9gP3A/gD+QP6A/uAv4DDgMeAy4DPgNOA14AKgNuA3oD+gQECgQEG +gQEKgQEOgQE7gQFMgQFQgQFVgQFZgQFhgQFl1wCoAA4AqQCqAKsArACtAK4ArwP/BAAAsgCzAK6AC4AX +gMCAwQmAC18QF3t7MjExLCAxMzJ9LCB7MTU2LCAxOH193QDBAA4AwgDDAMQAxQDGAMcAyADJAMoAywDM +ARgAzgDPANAA0QDSAM8ECQDVA+YA1wDYANmAFoAVgBKAFYDCgA+Av18QFU51ciwgd2VubiBrbGVpbmVy +IGFsc9cAqAAOAKkAqgCrAKwArQCuAYAEEAQRALIAswCugAuAP4DEgMUJgAtfEBZ7ezQzLCAxODF9LCB7 +MTI4LCAxN3192ADBAA4BiQDHAMgAyQDLAYoBGAGLAYwEGADVA+cB7gGRgD6ANoDGgA+Aw4A7bgDcAGIA +ZQByAHQAcgBhAGcAdQBuAGcAZQBuADrXAKgADgCpAKoAqwCsAK0ArgGABCAEIQCyALMAroALgD+AyIDJ +CYALXxAVe3s0MywgMTd9LCB7MTI4LCAxN3192ADBAA4BiQDHAMgAyQDLAYoBGAGLAYwEKADVA+gB7gGR +gD6ANoDKgA+Ax4A7bxAQAFYAZQByAHMAaQBvAG4AcwBwAHIA/ABmAHUAbgBnADrXAKgADgCpAKoAqwCs +AK0ArgGABDAEMQCyALMAroALgD+AzIDNCYALXxAWe3sxNywgMjUzfSwgezE1NCwgNTF9fdgAwQAOAYkA +xwDIAMkAywGKARgBiwGMBDgA1QPpAe4BkYA+gDaAzoAPgMuAO18QEkJpbGRzY2hpcm13ZWNoc2VsOtcA +qAAOAKkAqgCrAKwArQCuAK8EQARBALIAswCugAuAF4DQgNEJgAtfEBZ7ezE4MywgNzR9LCB7MjEwLCAx +OH193QDBAA4AwgDDAMQAxQDGAMcAyADJAMoAywDMARgAzgDPANAA0QDSAM8ESgDVA+oA1wDYANmAFoAV +gBKAFYDSgA+Az28QHQBTAHQAYQB0AHUAcwAgAGkAbgAgAE0AZQBuAPwAbABlAGkAcwB0AGUAIABhAG4A +egBlAGkAZwBlAG7XAKgADgCpAKoAqwCsAK0ArgCvBFEEUgCyALMAroALgBeA1IDVCYALXxAXe3sxODMs +IDE4MH0sIHszNTAsIDE4fX3dAMEADgDCAMMAxADFAMYAxwDIAMkAygDLAMwAzQDOAM8A0ADRANIAzwRb +ANUD6wDXANgA2YAWgBWAEoAVgNaAD4DTbxAwAEQAYQB0AGUAaQBlAG4AIABwAGUAcgAgAEQAcgBhAGcA +IAAmACAARAByAG8AcAAgAHoAdwBpAHMAYwBoAGUAbgAgAE0AYQBjAHMAIAD8AGIAZQByAHQAcgBhAGcA +ZQBu1wCoAA4AqQCqAKsArACtAK4ArwRiBGMAsgCzAK6AC4AXgNiA2QmAC18QF3t7MjExLCAxMDh9LCB7 +MTgwLCAxOH193QDBAA4AwgDDAMQAxQDGAMcAyADJAMoAywDMARgAzgDPANAA0QDSAM8EbADVA+wA1wDY +ANmAFoAVgBKAFYDagA+A128QGABOAHUAcgAgAGIAZQBpACAAZwBlAGQAcgD8AGMAawB0AGUAcgAgAFQA +YQBzAHQAZdcAqAAOAKkAqgCrAKwArQCuAK8EcwR0ALIAswCugAuAF4DcgN0JgAtfEBd7ezE4MywgMjg2 +fSwgezE4MCwgMTh9fd0AwQAOAMIAwwDEAMUAxgDHAMgAyQDKAMsAzAEYAM4AzwDQANEA0gDPBGwA1QPu +ANcA2ADZgBaAFYASgBWA2oAPgNvXAKgADgCpAKoAqwCsAK0ArgSCBIMEhACyALMAroALgP2A34DgCYAL +XxAXe3szOTUsIDEwM30sIHsxMjksIDI2fX3fEBIAwQSJBIoAwgDDAA4AxADGAMgEiwDJBIwEjQSOAMoA +ywSPAMwEkACzARsAzwIvBJMBGwSUANUElgPvAacAswCzAjUEmgSbBJxfEBpOU01lbnVJdGVtUmVzcGVj +dEFsaWdubWVudF8QD05TQXJyb3dQb3NpdGlvblpOU01lbnVJdGVtXxAPTlNQcmVmZXJyZWRFZGdlXxAS +TlNVc2VzSXRlbUZyb21NZW51XU5TQWx0ZXJzU3RhdGVWTlNNZW51E/////+EQf5ACYAVgPyA4YAPgOKA +3gkJEQgAgOMSBoJA/9IADgEkASUA74Ai3ANFAA4EoAShBKIEowSkBKUEjwNGAiMEpgSEBKgEqQSqAM8E +rAStBK4EmwSwBLEBG1dOU1RpdGxlXxARTlNLZXlFcXVpdk1vZE1hc2taTlNLZXlFcXVpdl1OU01uZW1v +bmljTG9jWU5TT25JbWFnZVxOU01peGVkSW1hZ2VXTlNTdGF0ZYDggOyA5BIAEAAAgBUSf////4DlgOmA +44DrEBTTAA4EoASzBLQEtQS2W05TTWVudUl0ZW1zgPuA7YDubiMYACAAQgBlAGYAZQBoAGwAcwB0AGEA +cwB0AGXTAA4AMgS5BLoEuwS8Xk5TUmVzb3VyY2VOYW1lgOiA5oDnV05TSW1hZ2VfEA9OU01lbnVDaGVj +a21hcmvSADcAOATABMGiBMEAO18QEE5TQ3VzdG9tUmVzb3VyY2XTAA4AMgS5BLoEuwTFgOiA5oDqXxAQ +TlNNZW51TWl4ZWRTdGF0ZV8QEV9wb3BVcEl0ZW1BY3Rpb2460gA3ADgEyQSLogSLADvSAA4BJAElBMyA +IlpPdGhlclZpZXdz0gAOAD4ARgTPgFmlBJYE0QTSBNME1IDigO+A8oD1gPjbA0UADgSgBKEEogSjBKQE +pQSPA0YCIwSEBKgE2ASqAM8ErAStBK4EmwTdBN6A4IDsgPCAFYDlgOmA44DxEBNrIyUAIABXAGEAaABs +AHQAYQBzAHQAZdsDRQAOBKAEoQSiBKMEpASlBI8DRgIjBIQEqATjBKoAzwSsBK0ErgSbBOgE6YDggOyA +84AVgOWA6YDjgPQQEmwjAwAgAGMAdAByAGwALQBUAGEAcwB0AGXbA0UADgSgBKEEogSjBKQEpQSPA0YC +IwSEBKgE7gSqAM8ErAStBK4EmwTzBPSA4IDsgPaAFYDlgOmA44D3EBFtIecAIABTAGgAaQBmAHQALQBU +AGEAcwB0AGXbA0UADgSgBKEEogSjBKQEpQSPA0YCIwSEBKgE+QSqAM8ErAStBK4EmwT+BP+A4IDsgPmA +FYDlgOmA44D6EBBrIeoAIABDAGEAcABzACAATABvAGMAa9IANwA4BQIEj6IEjwA70gA3ADgFBAUFpgUF +BQYA8gDzAKoAO18QEU5TUG9wVXBCdXR0b25DZWxsXk5TTWVudUl0ZW1DZWxs0gA3ADgFCAUJpgUJAPYA +9wD4APkAO11OU1BvcFVwQnV0dG9u1wCoAA4AqQCqAKsArACtAK4BgAUNBQ4AsgCzAK6AC4A/gP+BAQAJ +gAtfEBZ7ezMyMywgMjYzfSwgezMzLCAxNH192ADBAA4BiQDHAMgAyQDLAYoBGAGLAYwFFQGOA/AFGAGR +gD6ANoEBAYA1gP4SEEAAAIA7VVNob3J01wCoAA4AqQCqAKsArACtAK4ArwUeBR8AsgCzAK6AC4AXgQED +gQEECYALXxAXe3sxODMsIDIxNH0sIHsyNzgsIDE4fX3dAMEADgDCAMMAxADFAMYAxwDIAMkAygDLAMwB +GADOAM8A0ADRANIAzwUoANUD8QDXANgA2YAWgBWAEoAVgQEFgA+BAQJfECVWZXJzdWNoZSwgc2NobGFm +ZW5kZSBNYWNzIGF1Znp1d2Vja2Vu1wCoAA4AqQCqAKsArACtAK4ArwUvBTAAsgCzAK6AC4AXgQEHgQEI +CYALXxAXe3sxODMsIDI2Mn0sIHsxNDYsIDE4fX3dAMEADgDCAMMAxADFAMYAxwDIAMkAygDLAMwBGADO +AM8A0ADRANIAzwU5ANUD8gDXANgA2YAWgBWAEoAVgQEJgA+BAQZvEA8ATQBpAHQAIABWAGUAcgB6APYA +ZwBlAHIAdQBuAGfXAKgADgCpAKoAqwCsAK0ArgCvBUAFQQCyALMAroALgBeBAQuBAQwJgAtfEBd7ezE4 +MywgMjM4fSwgezIwNCwgMTh9fd0AwQAOAMIAwwDEAMUAxgDHAMgAyQDKAMsAzAEYAM4AzwDQANEA0gDP +BUoA1QPzANcA2ADZgBaAFYASgBWBAQ2AD4EBCm8QEwBCAGUAaQAgAEQAbwBwAHAAZQBsAGIAZQByAPwA +aAByAHUAbgBn1wCoAA4AqQCqAKsArACtAK4BgAVRBVIAsgCzAK6AC4A/gQEPgQEQCYALXxAWe3szNzMs +IDEzMX0sIHs1OCwgMjJ9fdoAwQAOAYkAyADJBVcAywFUBVgBigVZAYsFWwDVA/QFXgVfALMFYQViW05T +Rm9ybWF0dGVyXxATTlNQbGFjZWhvbGRlclN0cmluZxP/////lHH+QYA+gQE3gA+BAQ6BARESBEAEAAmB +ATaBATnfEBEADgVkBWUFZgVnBWgFaQVqBWsFbAVtBW4FbwVwBXEFcgVzBXQFdQV2BXcAKwV5BXoFewV8 +BX0AswV/ACsFgQWCBYMFg1ZOUy5uaWxaTlMuZGVjaW1hbFZOUy5uYW5bTlMucm91bmRpbmdXTlMuemVy +b18QEE5TLm5lZ2F0aXZlYXR0cnNWTlMubWF4XU5TLmF0dHJpYnV0ZXNfEBFOUy5wb3NpdGl2ZWZvcm1h +dF8QD05TLmFsbG93c2Zsb2F0c18QEU5TLm5lZ2F0aXZlZm9ybWF0XxAQTlMucG9zaXRpdmVhdHRyc1tO +Uy50aG91c2FuZFZOUy5taW5cTlMubG9jYWxpemVkXxAPTlMuaGFzdGhvdXNhbmRzgQE1gQExgQEogQEz +gACBASKBASeBAS2BARKBASEJgQEjgACBAS+BAR8ICNMADgWGAD4FhwWIBZVXTlMua2V5c4EBMKwFiQWK +BYsFjAWNBY4FjwWQBZEFkgWTBZSBAROBARSBARWBARaBAReBARiBARmBARqBARuBARyBAR2BAR6sBYIF +fQV5BXoFdgWbBZwFnQV7BZ8FfwWBgQEfgQEhgQEigQEngQEogQEpgQEqgQEsgQEtgQEugQEjgQEvV21p +bmltdW1ecG9zaXRpdmVGb3JtYXRfEBdhdHRyaWJ1dGVkU3RyaW5nRm9yWmVyb18QH3RleHRBdHRyaWJ1 +dGVzRm9yTmVnYXRpdmVWYWx1ZXNfEBBkZWNpbWFsU2VwYXJhdG9yXxARZm9ybWF0dGVyQmVoYXZpb3JW +bG9jYWxlXGFsbG93c0Zsb2F0c1dtYXhpbXVtXxAVdXNlc0dyb3VwaW5nU2VwYXJhdG9yXm5lZ2F0aXZl +Rm9ybWF0XxARZ3JvdXBpbmdTZXBhcmF0b3LXAA4FrwWwBbEFsgWzBbQFtQWDANgA0QDYBbcFg1pOUy5j +b21wYWN0W05TLmV4cG9uZW50Xk5TLm1hbnRpc3NhLmJvWU5TLmxlbmd0aFtOUy5tYW50aXNzYVtOUy5u +ZWdhdGl2ZYEBIAhPEBAAAAAAAAAAAAAAAAAAAAAACNIANwA4BboFu6IFuwA7XxAaTlNEZWNpbWFsTnVt +YmVyUGxhY2Vob2xkZXJSMEvTAA4FvgEpBb8FwAV/XE5TQXR0cmlidXRlc4EBJoEBJIEBI1Ew0wAOBYYA +PgXEBcUFxoEBJaCg0gA3ADgFyAXJogXJADtcTlNEaWN0aW9uYXJ50gA3ADgFywXMogXMADtfEBJOU0F0 +dHJpYnV0ZWRTdHJpbmfTAA4FhgA+BcQFzwXQgQEloKBhAKARA+jSAA4F1AXVAM9dTlMuaWRlbnRpZmll +coEBK4AV0gA3ADgF2AXZogXZADtYTlNMb2NhbGXXAA4FrwWwBbEFsgWzBbQFtQCzANgA0QDRBd0Fg4EB +IAlPEBAAAEAAAAAAAAAAAAAAAAAACFEs0gA3ADgF4QXiowXiBckAO18QE05TTXV0YWJsZURpY3Rpb25h +cnnSAA4BKQW/BeWBASaBATLTAA4FvgEpBb8FwAXpgQEmgQEkgQE0U05hTtIANwA4BewF7aMF7QVXADtf +EBFOU051bWJlckZvcm1hdHRlclRzaXpl1QAOAZkBmgGbAZwBnQIDAZ8F8gGhgDqAX4EBOIA3XxATdGV4 +dEJhY2tncm91bmRDb2xvctUADgGZAZoBmwGcAZ0BrQGfBfgBoYA6gD2BATqAN1l0ZXh0Q29sb3LXAKgA +DgCpAKoAqwCsAK0ArgSCBf4F/wCyALMAroALgP2BATyBAT0JgAtfEBd7ezM2OCwgMjgxfSwgezEyOSwg +MjZ9fd8QEwDBBIkEigDCAMMADgDEAMYAyASLAMkEjASNBI4AygDLBI8AzAYEBJAAswEbAM8CLwSTARsG +CADVBgoD9QGnALMAswI1BJoGDgScARtfEA9OU1NlbGVjdGVkSW5kZXgJgBWA/IEBPoAPgQE/gQE7CQmB +AUDSAA4BJAElAO+AItwDRQAOBKAEoQSiBKMEpASlBI8DRgIjBKYF/wSoBNgEqgDPBKwErQSuBg4GGQTe +ARuBAT2A7IDwgBWA5YDpgQFAgQFB0wAOBKAEswS0BhwGHYD7gQFCgQFD0gAOASQBJQTMgCLSAA4APgBG +BiKAWaUGIwYKBiUGJgYngQFEgQE/gQFGgQFIgQFK2wNFAA4EoAShBKIEowSkBKUEjwNGAiMF/wSoBKkE +qgDPBKwErQSuBg4GMASxgQE9gOyA5IAVgOWA6YEBQIEBRdsDRQAOBKAEoQSiBKMEpASlBI8DRgIjBf8E +qATjBKoAzwSsBK0ErgYOBjkE6YEBPYDsgPOAFYDlgOmBAUCBAUfbA0UADgSgBKEEogSjBKQEpQSPA0YC +IwX/BKgE7gSqAM8ErAStBK4GDgZCBPSBAT2A7ID2gBWA5YDpgQFAgQFJ2wNFAA4EoAShBKIEowSkBKUE +jwNGAiMF/wSoBPkEqgDPBKwErQSuBg4GSwT/gQE9gOyA+YAVgOWA6YEBQIEBS9cAqAAOAKkAqgCrAKwA +rQCuAK8GTwZQALIAswCugAuAF4EBTYEBTgmAC18QFnt7MTgzLCA1MH0sIHszNjEsIDE4fX3dAMEADgDC +AMMAxADFAMYAxwDIAMkAygDLAMwBGADOAM8A0ADRANIAzwZZANUD9gDXANgA2YAWgBWAEoAVgQFPgA+B +AUxfEDNCZXplbCBiZWkgZGVyIFN0ZXVlcnVuZyBlaW5lcyBhbmRlcmVuIE1hY3MgYW56ZWlnZW7XAKgA +DgCpAKoAqwCsAK0ArgCvBmAGYQCyALMAroALgBeBAVGBAVIJgAtfEBV7ezQyMiwgN30sIHsxMTUsIDMy +fX3cAMEADgDCAMMAxADGAMcAyADJAMoAywDMARgAzgDPANABGwZoBmkA1QP3ANcBIAEhgBaAFYEBVIEB +U4APgQFQbABKAGUAdAB6AHQAIABwAHIA/ABmAGUAbtIADgEkASUA74Ai1wCoAA4AqQCqAKsArACtAK4B +gAZyBnMAsgCzAK6AC4A/gQFWgQFXCYALXxAVe3sxMDIsIDc1fSwgezY5LCAxN3192ADBAA4BiQDHAMgA +yQDLAYoBGAGLAYwGegDVA/gB7gGRgD6ANoEBWIAPgQFVgDtXU3RhdHVzOtcAqAAOAKkAqgCrAKwArQCu +BoEGggaDALIAswCugAuBAWCBAVqBAVsJgAtfEBZ7ezM2MSwgMjYxfSwgezgzLCAxNX193gaIAMEADgaJ +BooAxwDIAMkGiwaMAMsGjQaOBo8GkAEYBpEA2AEbBpIGkwP5BpUGlgaXBYMGmQWDV05TVmFsdWVfEBNO +U051bWJlck9mVGlja01hcmtzXxASTlNUaWNrTWFya1Bvc2l0aW9uWk5TTWF4VmFsdWVaTlNNaW5WYWx1 +ZVpOU1ZlcnRpY2FsXU5TQWx0SW5jVmFsdWVfEBpOU0FsbG93c1RpY2tNYXJrVmFsdWVzT25seSM/4AAA +AAAAAIEBX4EBXIEBXYEBWSM/8AAAAAAAACM/uZmZmZmZmhIAAgAACCMAAAAAAAAAAAjSAA4BJAElAO+A +ItQADgDcAN0A3gDfBp8GoAT/gBEjQCgAAAAAAACBAV5ZSGVsdmV0aWNh0gA3ADgGowakpAakAPMAqgA7 +XE5TU2xpZGVyQ2VsbNIANwA4BqYGp6UGpwD3APgA+QA7WE5TU2xpZGVy1wCoAA4AqQCqAKsArACtAK4B +gAarBqwAsgCzAK6AC4A/gQFigQFjCYALXxAWe3s0NDksIDI2M30sIHszMSwgMTR9fdgAwQAOAYkAxwDI +AMkAywGKARgBiwGMBrMBjgP6BRgBkYA+gDaBAWSANYEBYYA7VExvbmfXAKgADgCpAKoAqwCsAK0ArgCv +BrsGvACyALMAroALgBeBAWaBAWcJgAtfEBZ7ezE4MywgMTZ9LCB7MjMxLCAxOH193QDBAA4AwgDDAMQA +xQDGAMcAyADJAMoAywDMARgAzgDPANAA0QDSAM8GxQDVA/sA1wDYANmAFoAVgBKAFYEBaIAPgQFlbxAf +AEEAdQB0AG8AbQBhAHQAaQBzAGMAaABlACAAUAByAPwAZgB1AG4AZwAgAGIAZQBpAG0AIABTAHQAYQBy +AHRYT3B0aW9uZW7SADcAOAbLA5mkA5kA+AD5ADtYZGVsZWdhdGXUAA4AoAChAKIAowPyAB8G0YAZgQEG +gAKBAW9fEBVkZWxheWVkU3dpdGNoQ2hlY2tib3jUAA4AoAChAKIAowExAB8G14AZgCaAAoEBf9cAqAAO +ALYAqwC3AQUArQEMAWgG2wFrBtwBDAbegB6Ap4EB7oEB74AegQHw0gAOAD4ARgbhgFmkBuIG4wEtAU+B +AXOBAXeAJYAs2ACoAA4AqQCqAKsArAEFAK0BMQCvBukG6gCyALMBDAExgCaAF4EBdIEBdQmAHoAmXxAX +e3sxNzEsIDM4N30sIHsyMTksIDE4fX3dAMEADgDCAMMAxADFAMYAxwDIAMkAygDLAMwG8ADOAM8A0ADR +ANIAzwb1ANUG4gDXANgA2RIkAf4AgBaAFYASgBWBAXaAD4EBc18QG0RpZXNlbiBNYWMgZ2VtZWluc2Ft +IG51dHplbtgAqAAOAKkAqgCrAKwBBQCtATEArwb8Bv0G/gCzAQwBMYAmgBeBAXiBAXkRAQkJgB6AJl8Q +Fnt7NTQ1LCAzNzl9LCB7MzIsIDMyfX3dAMEADgDCBwQAwwDEAMYAxwDIAMkAygDLAMwBGADOAM8HBwDQ +BwgHCQcKANUG4wDXASAHDV1OU05vcm1hbEltYWdlgBaAFYEBexCCgQF9gQF6gA+BAXcSCERA/2YAQQBi +AG8AdQB0ICbTAA4AMgS5BLoEuwcSgOiA5oEBfFppY29uLWFib3V00gAOASQBJQDvgCJaezU5NSwgNDM4 +fVR2aWV31AAOAKAAoQCiAKMAHwJxAVCAGYACgGWBAW3UAA4AoAChAKIAowAfByABUIAZgAKBAYKBAW3c +ByMADgckByUHJgcnBygHKQcqBysHLActBy4HLwcwAM8HMgczBzQHNQDRARsHNgc3XE5TV2luZG93Vmll +d1xOU1NjcmVlblJlY3RdTlNXaW5kb3dUaXRsZVlOU1dURmxhZ3NdTlNXaW5kb3dDbGFzc1xOU1dpbmRv +d1JlY3RZTlNNYXhTaXplXxAPTlNXaW5kb3dCYWNraW5nXxARTlNXaW5kb3dTdHlsZU1hc2tZTlNNaW5T +aXplW05TVmlld0NsYXNzgQGGgQG+gQG7gBUScHgAAIEBhIEBg4EBvYEBvIEBhV8QGHt7MTk0LCAzNzV9 +LCB7Mjk1LCAzMDd9fV8QD1RQQ2xvc2luZ1dpbmRvd9IADgEkASUHPIAiVFZpZXfXAKgADgC2AKsAtwEF +AK0HPgFoB0ABawdBBz4HQ4EBh4CngQGIgQG5gQGHgQG60gAOAD4ARgdGgFmnB0cHSAdJB0oHSwdMB02B +AYmBAZiBAZ2BAaOBAaiBAa+BAbTaAKgADgCpB08AqgdQAKsArAEFAK0HLgdSB1MAswdVB1YBawCzBz4H +LlpOU0VkaXRhYmxlW05TRHJhZ1R5cGVzgQGGgQGXgQGSCYEBk4EBigmBAYeBAYbSAA4APgA/B1yAB6cH +XQdeB18HYAdhB2IHY4EBi4EBjIEBjYEBjoEBj4EBkIEBkV8QGUFwcGxlIFBERiBwYXN0ZWJvYXJkIHR5 +cGVfEBlBcHBsZSBQTkcgcGFzdGVib2FyZCB0eXBlXxAjQ29yZVBhc3RlYm9hcmRGbGF2b3JUeXBlIDB4 +NTA0RTQ3NjZfEBVOU0ZpbGVuYW1lc1Bib2FyZFR5cGVfEDFOZVhUIEVuY2Fwc3VsYXRlZCBQb3N0U2Ny +aXB0IHYxLjIgcGFzdGVib2FyZCB0eXBlXxAeTmVYVCBUSUZGIHY0LjAgcGFzdGVib2FyZCB0eXBlXxAa +QXBwbGUgUElDVCBwYXN0ZWJvYXJkIHR5cGVfEBd7ezIwLCAxNTl9LCB7MjU1LCAxMjh9fdgAwQAOB20A +xwduB28AywdwB3EHcgDYB3MA2ADYB3QFg1dOU1N0eWxlV05TQWxpZ25XTlNTY2FsZVpOU0FuaW1hdGVz +EiAB/gCBAZaBAZQSAgAAAAjTAA4AMgS5BLoEuwd5gOiA5oEBlVh0ZWxlcG9ydNIANwA4B3wHfaMHfQCq +ADtbTlNJbWFnZUNlbGzSADcAOAd/B4ClB4AA9wD4APkAO1tOU0ltYWdlVmlld9gAqAAOAKkAqgCrAKwB +BQCtBy4BgAeEB4UBawCzBz4HLoEBhoA/gQGZgQGaCYEBh4EBhl8QFnt7MTcsIDEyOX0sIHsyNjEsIDIy +fX3YAMEADgGJAMcAyADJAMsBigEYAYsBjAd5B44HSAeQAZGAPoA2gQGVgQGbgQGYEghAAACAO9QADgDc +AN0A3gDfB5QHlQT/gBEjQDIAAAAAAACBAZxfEBFMdWNpZGFHcmFuZGUtQm9sZNoAqAAOAKkCfACqAKsA +rAEFADIArQcuAocHmgebB5wBawCzBz4HnwcugQGGgI6BAaCBAZ+BAaEJgQGHgQGegQGGXxASVFBWZXJz +aW9uVGV4dEZpZWxkXxAWe3sxNywgMTEzfSwgezI2MSwgMTR9fdgAwQAOAYkAxwDIAMkAywGKARgBiwGM +B6YBjgdJB5ABkYA+gDaBAaKANYEBnYA7WTx2ZXJzaW9uPtgAqAAOAKkAqgCrAKwBBQCtBy4BgAeuB68B +awCzBz4HLoEBhoA/gQGkgQGlCYEBh4EBhl8QFXt7MTIsIDQzfSwgezI3NCwgMzl9fdgAwQAOAYkAxwDI +AMkAywGKARgBiwGMB7cHuAdKB7oBkYA+gDaBAaaBAaeBAaMSAEAAAIA7bxB7AKkAIAAyADAAMAAzAC0A +MgAwADAAOAAgAGEAYgB5AHMAcwBvAGYAdAAKAEIAYQBzAGkAZQByAHQAIABhAHUAZgAgAGUAaQBuAGUA +cgAgAEkAZABlAGUAIAB2AG8AbgAgAEQAYQB2AGUAIABTAGEAZwAgAHUAbgBkACAASgBlAHIAZQBtAHkA +IABRAHUAaQBuAG4ALgAKAEIAZQBzAG8AbgBkAGUAcgBlAHIAIABEAGEAbgBrACAAZwBlAGgAdAAgAGEA +bgAgAE0AYQBuAHUAMAAyACwAIABKAHUAbABpAGUAbgAgAHUAbgBkACAAUABhAHUAbAAu1AAOANwA3QDe +AN8HvwDhB8GAESNAJAAAAAAAAIAQEQsb2gCoAA4AqQJ8AKoAqwCsAQUAMgCtBy4ChwfFB5sHxwfIALMH +PgfLBy6BAYaAjoEBqoEBn4EBqxEBAgmBAYeBAamBAYZfEA9UUExpbmtUZXh0RmllbGRfEBN7ezEyLCA4 +fSwgezY4LCAxN3192gDBAA4BiQDHAMgAyQDLAVQFWAGKB9ABiwGMB9MH1AdLBRgAswfXAZESBCH+AYA+ +gDaBAayBAa2BAagJgQGugDtpJ6QAIAB3AGUAYgBzAGkAdABl1AAOANwA3QDeAN8BlQDhBP+AEYAQXxAc +aHR0cDovL3RlbGVwb3J0LmFieXNzb2Z0LmNvbdoAqAAOAKkCfACqAKsArAEFADIArQcuAocH4QebB+MH +yACzBz4HywcugQGGgI6BAbCBAZ+BAbEJgQGHgQGpgQGGXxAUe3sxMTksIDh9LCB7NjksIDE3fX3ZAMEA +DgGJAMcAyADJAMsFWAGKARgBiwGMB+wH1AdMBRgH7wGRgD6ANoEBsoEBrYEBr4EBs4A7ZyekACAAZgBv +AHIAdQBtXxAxaHR0cDovL3d3dy5hYnlzc29mdC5jb20vc29mdHdhcmUvdGVsZXBvcnQvZm9ydW1zL9oA +qAAOAKkCfACqAKsArAEFADIArQcuAocH9gebB/gHyACzBz4HywcugQGGgI6BAbWBAZ+BAbYJgQGHgQGp +gQGGXxAUe3syMjgsIDh9LCB7NjcsIDE3fX3ZAMEADgGJAMcAyADJAMsFWAGKARgBiwGMCAEH1AdNBRgI +BAGRgD6ANoEBt4EBrYEBtIEBuIA7aSekACAAcwBwAGUAbgBkAGUAbl8QrWh0dHBzOi8vd3d3LnBheXBh +bC5jb20vY2dpLWJpbi93ZWJzY3I/Y21kPV94Y2xpY2smYnVzaW5lc3M9anVsJTQwbWFjJTJlY29tJml0 +ZW1fbmFtZT10ZWxlcG9ydCZub19zaGlwcGluZz0wJm5vX25vdGU9MCZ0YXg9MCZjdXJyZW5jeV9jb2Rl +PVVTRCZjaGFyc2V0PVVURiUyZDgmY2hhcnNldD1VVEYlMmQ4WnsyOTUsIDMwN31fEBZ7ezAsIDB9LCB7 +MTY4MCwgMTAyOH19WnsyMTMsIDEyOX1fEBp7My40MDI4MmUrMzgsIDMuNDAyODJlKzM4fdIANwA4CA0I +DqIIDgA7XxAQTlNXaW5kb3dUZW1wbGF0ZdQADgCgAKEAogCjBuIAHwgTgBmBAXOAAoEBwF8QFGFsbG93 +Q29udHJvbENoZWNrYm941AAOAKAAoQCiAKMAHwOpAVCAGYACgK6BAW3UAA4AoAChAKIAowOpAB8IHoAZ +gK6AAoEBw1psYXlvdXRWaWV31AAOAKAAoQCiCCEAHwF3CCSBAcaAAoBAgQHFXxAWc2hvd0NlcnRpZmlj +YXRlVmlld2VyOtIANwA4CCcIKKMIKAD+ADtfEBVOU05pYkNvbnRyb2xDb25uZWN0b3LUAA4AoAChAKIA +owJxCCwILYAZgGWBAciBAc/UAA4ILwgwCDEIMggzCDQINV8QD19OU01hbmFnZWRQcm94eV8QEU5TT2Jq +ZWN0Q2xhc3NOYW1lXk5TRGVjbGFyZWRLZXlzgQHOgQHMgQHLgQHJ0gAOAD4ARgg4gFmhCDmBAcpfEBRu +dW1iZXJPZlNlbGVjdGVkUm93c9EADgg8gQHN0gA3ADgIPggvoggvADvSADcAOAhACEGjCEEIQgA7XxAS +TlNPYmplY3RDb250cm9sbGVyXE5TQ29udHJvbGxlcldjb250ZW501AAOAKAAoQCiCCEAHwbjCEiBAcaA +AoEBd4EB0V8QD3Nob3dBYm91dFNoZWV0OtQADgCgAKEAogghAB8D9whOgQHGgAKBAVCBAdNdY2hlY2tW +ZXJzaW9uOtQADgCgAKEAogCjAnEAHwhUgBmAZYACgQHVXxAVdHJ1c3RlZEhvc3RzVGFibGVWaWV31AAO +AKAAoQCiAKMBdwAfCFqAGYBAgAKBAddfEBVzaG93Q2VydGlmaWNhdGVCdXR0b27UAA4AoAChAKIIIQAf +AQEIYIEBxoACgBuBAdlfEBdzaG93Q2VydGlmaWNhdGVDaG9vc2VyOtQADgCgAKEAogCjA+oAHwhmgBmA +z4ACgQHbXnN0YXR1c0NoZWNrYm941AAOAKAAoQCiAKMDqQAfCGyAGYCugAKBAd1fEA9faW5pdGlhbEtl +eVZpZXfUAA4AoAChAKIAowdJAB8IcoAZgQGdgAKBAd9fEBB2ZXJzaW9uVGV4dEZpZWxk1AAOAKAAoQCi +AKMD7gAfCHiAGYDbgAKBAeFfEBJyZXF1aXJlS2V5Q2hlY2tib3jUAA4AoAChAKIAowAfAnEIfoAZgAKA +ZYEB41pkYXRhU291cmNl1AAOAKAAoQCiAKMHIAAfCISAGYEBgoACgQHlW2Fib3V0V2luZG931AAOAKAA +oQCiAKMDqQAfCIqAGYCugAKBAedcX2xhc3RLZXlWaWV31AAOAKAAoQCiAKMIjgAfCJCAGYEB6YACgQH0 +3AcjAA4HJAclByYHJwcoBykHKgcrBywHLQE5By8IlAiVCJYIlwiYCJkA0QKVCJoIm4EBcYEBvoEB8YEB +6xJAeAAAgQHsgQHqgQHzgQHygQHtXxAYe3s1MTcsIDQ1MH0sIHs1OTUsIDQyOX19XxAVPDwgZG8gbm90 +IGxvY2FsaXplID4+0gAOASQBJQc8gCLSAA4APgBGCKKAWaEBMYAmWns1OTUsIDQyOX1dezIyNC42NjQs +IDMyfVdfd2luZG931AAOAKAAoQCiAKMAHwiqCC2AGYACgQH2gQHP1AAOCC8HTwgxCDIIrgCzCLCBAc6B +Ag4JgQH30gAOAD4ARgizgFmvEBYItAi1CLYItwi4CLkIugi7CLwIvQi+CL8IwAjBCMIIwwjECMUIxgjH +CMgIyYEB+IEB+YEB+oEB+4EB/IEB/YEB/oEB/4ECAIECAYECAoECA4ECBIECBYECBoECB4ECCIECCYEC +CoECC4ECDIECDV8QEnByZWZzLmFsbG93Q29udHJvbF8QFXByZWZzLnNoYXJlUGFzdGVib2FyZF8QEHBy +ZWZzLnJlcXVpcmVLZXlfEBNwcmVmcy5kZWxheWVkU3dpdGNoXxAUcHJlZnMuc2hvd1N0YXR1c0l0ZW1f +EBlwcmVmcy5saW1pdFBhc3RlYm9hcmRTaXplXxAYcHJlZnMubWF4UGFzdGVyYm9hcmRTaXplVmFjdGl2 +ZV8QFnByZWZzLmF1dG9jaGVja1ZlcnNpb25fECJwcmVmcy5yZWplY3RBdXRoZW50aWNhdGlvblJlcXVl +c3RzXxAWcHJlZnMuZW5hYmxlRW5jcnlwdGlvbl8QEnByZWZzLnN3aXRjaEtleVRhZ18QD3ByZWZzLmNv +cHlGaWxlc18QGnByZWZzLnJlcXVpcmVQYXN0ZWJvYXJkS2V5XxAWcHJlZnMucGFzdGVib2FyZEtleVRh +Z18QGnByZWZzLnRydXN0UmVxdWVzdEJlaGF2aW9yXxAPcHJlZnMud2FrZU9uTEFOXxARcHJlZnMuc3dp +dGNoRGVsYXlfEBpsb2NhbEhvc3Quc3VwcG9ydERyYWdORHJvcF8QF3ByZWZzLm1heFBhc3RlYm9hcmRT +aXplXxAWcHJlZnMuaGlkZUNvbnRyb2xCZXplbF8QGXByZWZzLnN3aXRjaFdpdGhEb3VibGVUYXDRAA4I +PIEBzdQADgCgAKEAogCjA6kAHwjmgBmAroACgQIQXV9maXJzdEtleVZpZXfXAA4AoAjpCOoAoQCiCOsI +7AiqCO4I7wPvCPEA0VlOU0tleVBhdGhZTlNCaW5kaW5nXxAcTlNOaWJCaW5kaW5nQ29ubmVjdG9yVmVy +c2lvboECFYEB9oECFIECE4DegQISXxAtc2VsZWN0ZWRUYWc6IHNlbGVjdGlvbi5wcmVmcy5wYXN0ZWJv +YXJkS2V5VGFnW3NlbGVjdGVkVGFnXxAgc2VsZWN0aW9uLnByZWZzLnBhc3RlYm9hcmRLZXlUYWfSADcA +OAj2CPejCPcA/gA7XxAVTlNOaWJCaW5kaW5nQ29ubmVjdG9y1wAOAKAI6QjqAKEAogjrCOwIqgj7CPwD +6gj+ANGBAhWBAfaBAhmBAhiAz4ECF18QGWVuYWJsZWQ6IHNlbGVjdGlvbi5hY3RpdmVXZW5hYmxlZF8Q +EHNlbGVjdGlvbi5hY3RpdmXXAA4AoAjpCOoAoQCiCOsI7AiqCPsI/APzCP4A0YECFYEB9oECGYECGIEB +CoECF9cADgCgCOkI6gChAKII6wjsCKoI+wj8A/EI/gDRgQIVgQH2gQIZgQIYgQECgQIX1wAOAKAI6Qjq +AKEAogjrCOwIqgj7CPwApAj+ANGBAhWBAfaBAhmBAhiACoECF9cADgCgCOkI6gChAKII6wjsCKoJGgkb +A+4JHQDRgQIVgQH2gQIggQIfgNuBAh5fECF2YWx1ZTogc2VsZWN0aW9uLnByZWZzLnJlcXVpcmVLZXlV +dmFsdWVfEBpzZWxlY3Rpb24ucHJlZnMucmVxdWlyZUtledcADgCgCOkI6gChAKII6wjsCKoJJAkbA+YJ +JwDRgQIVgQH2gQIjgQIfgL+BAiJfECp2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmxpbWl0UGFzdGVib2Fy +ZFNpemVfECNzZWxlY3Rpb24ucHJlZnMubGltaXRQYXN0ZWJvYXJkU2l6ZdcADgCgCOkI6gChAKII6wjs +CKoJLQkbA/IJMADRgQIVgQH2gQImgQIfgQEGgQIlXxAkdmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5kZWxh +eWVkU3dpdGNoXxAdc2VsZWN0aW9uLnByZWZzLmRlbGF5ZWRTd2l0Y2jXAA4AoAjpCOoAoQCiCOsI7Aiq +CTYI/APsCTkA0YECFYEB9oECKYECGIDXgQIoXxAoZW5hYmxlZDogc2VsZWN0aW9uLnByZWZzLnNoYXJl +UGFzdGVib2FyZF8QH3NlbGVjdGlvbi5wcmVmcy5zaGFyZVBhc3RlYm9hcmTZAA4AoAjpCOoJPQChAKIJ +PgjrCOwIqgj7CUIAbAPsCUUJRgDRXxATTlNQcmV2aW91c0Nvbm5lY3RvcllOU09wdGlvbnOBAhWBAfaB +AhmBAiyBAieA14ECK4ECLV8QGmVuYWJsZWQyOiBzZWxlY3Rpb24uYWN0aXZlWGVuYWJsZWQy0wAOBYYA +PgXECUsJUIEBJaQJTAlNCU4JT4ECLoECL4ECMIECMaQJUQlRCVEJUYECMoECMoECMoECMl8QEU5TTnVs +bFBsYWNlaG9sZGVyXxAaTlNOb3RBcHBsaWNhYmxlUGxhY2Vob2xkZXJfEBhOU05vU2VsZWN0aW9uUGxh +Y2Vob2xkZXJfEBtOU011bHRpcGxlVmFsdWVzUGxhY2Vob2xkZXIT///////////XAA4AoAjpCOoAoQCi +COsI7AiqCV0I/AF3CWAA0YECFYEB9oECNYECGIBAgQI0XxApZW5hYmxlZDogc2VsZWN0aW9uLnByZWZz +LmVuYWJsZUVuY3J5cHRpb25fECBzZWxlY3Rpb24ucHJlZnMuZW5hYmxlRW5jcnlwdGlvbtkADgCgCOkI +6gk9AKEAogk+COsI7AiqCPsJQgBuAXcJRQlrANGBAhWBAfaBAhmBAiyBAjOAQIECK4ECN9MADgWGAD4F +xAluCXOBASWkCUwJTQlOCU+BAi6BAi+BAjCBAjGkCVEJUQlRCVGBAjKBAjKBAjKBAjLXAA4AoAjpCOoA +oQCiCOsI7AiqCXsJGwPzCX4A0YECFYEB9oECOoECH4EBCoECOV8QKnZhbHVlOiBzZWxlY3Rpb24ucHJl +ZnMuc3dpdGNoV2l0aERvdWJsZVRhcF8QI3NlbGVjdGlvbi5wcmVmcy5zd2l0Y2hXaXRoRG91YmxlVGFw +1wAOAKAI6QjqAKEAogjrCOwIqgj7CPwD7gj+ANGBAhWBAfaBAhmBAhiA24ECF9cADgCgCOkI6gChAKII +6wjsCKoJiwkbA+oJjgDRgQIVgQH2gQI+gQIfgM+BAj1fECV2YWx1ZTogc2VsZWN0aW9uLnByZWZzLnNo +b3dTdGF0dXNJdGVtXxAec2VsZWN0aW9uLnByZWZzLnNob3dTdGF0dXNJdGVt1wAOAKAI6QjqAKEAogjr +COwIqgj7CPwD+wj+ANGBAhWBAfaBAhmBAhiBAWWBAhfXAA4AoAjpCOoAoQCiCOsI7AiqCZsJGwPrCZ4A +0YECFYEB9oECQoECH4DTgQJBXxAgdmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5jb3B5RmlsZXNfEBlzZWxl +Y3Rpb24ucHJlZnMuY29weUZpbGVz1wAOAKAI6QjqAKEAogjrCOwIqgk2CPwD5gk5ANGBAhWBAfaBAimB +AhiAv4ECKNkADgCgCOkI6gk9AKEAogk+COsI7AiqCPsJQgB1A+YJRQmwANGBAhWBAfaBAhmBAiyBAkOA +v4ECK4ECRdMADgWGAD4FxAmzCbiBASWkCUwJTQlOCU+BAi6BAi+BAjCBAjGkCVEJUQlRCVGBAjKBAjKB +AjKBAjLXAA4AoAjpCOoAoQCiCOsI7AiqCcAI7wF6CcMA0YECFYEB9oECSIECE4BNgQJHXxAxc2VsZWN0 +ZWRUYWc6IHNlbGVjdGlvbi5wcmVmcy50cnVzdFJlcXVlc3RCZWhhdmlvcl8QJHNlbGVjdGlvbi5wcmVm +cy50cnVzdFJlcXVlc3RCZWhhdmlvctcADgCgCOkI6gChAKII6wjsCKoJyQkbA/kJzADRgQIVgQH2gQJL +gQIfgQFZgQJKXxAidmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5zd2l0Y2hEZWxheV8QG3NlbGVjdGlvbi5w +cmVmcy5zd2l0Y2hEZWxhedcADgCgCOkI6gChAKII6wjsCKoI+wj8A/II/gDRgQIVgQH2gQIZgQIYgQEG +gQIX1wAOAKAI6QjqAKEAogjrCOwIqgldCPwBAQlgANGBAhWBAfaBAjWBAhiAG4ECNNkADgCgCOkI6gk9 +AKEAogk+COsI7AiqCPsJQgB6AQEJRQnlANGBAhWBAfaBAhmBAiyBAk2AG4ECK4ECT9MADgWGAD4FxAno +Ce2BASWkCUwJTQlOCU+BAi6BAi+BAjCBAjGkCVEJUQlRCVGBAjKBAjKBAjKBAjLXAA4AoAjpCOoAoQCi +COsI7AiqCfUJGwP0CfgA0YECFYEB9oECUoECH4EBDoECUV8QKHZhbHVlOiBzZWxlY3Rpb24ucHJlZnMu +bWF4UGFzdGVib2FyZFNpemVfECFzZWxlY3Rpb24ucHJlZnMubWF4UGFzdGVib2FyZFNpemXXAA4AoAjp +COoAoQCiCOsI7AiqCPsI/AF4CP4A0YECFYEB9oECGYECGIBFgQIX1wAOAKAI6QjqAKEAogjrCOwIqgkt +CPwD+QoIANGBAhWBAfaBAiaBAhiBAVmBAlVfECZlbmFibGVkOiBzZWxlY3Rpb24ucHJlZnMuZGVsYXll +ZFN3aXRjaNkADgCgCOkI6gk9AKEAogk+COsI7AiqCPsJQgB+A/kJRQoSANGBAhWBAfaBAhmBAiyBAlSB +AVmBAiuBAlfTAA4FhgA+BcQKFQoagQElpAlMCU0JTglPgQIugQIvgQIwgQIxpAlRCVEJUQlRgQIygQIy +gQIygQIy1wAOAKAI6QjqAKEAogjrCOwIqgoiCRsD8QolANGBAhWBAfaBAlqBAh+BAQKBAllfECB2YWx1 +ZTogc2VsZWN0aW9uLnByZWZzLndha2VPbkxBTl8QGXNlbGVjdGlvbi5wcmVmcy53YWtlT25MQU7XAA4A +oAjpCOoAoQCiCOsI7AiqCisI/APvCi4A0YECFYEB9oECXYECGIDegQJcXxAtZW5hYmxlZDogc2VsZWN0 +aW9uLnByZWZzLnJlcXVpcmVQYXN0ZWJvYXJkS2V5XxAkc2VsZWN0aW9uLnByZWZzLnJlcXVpcmVQYXN0 +ZWJvYXJkS2V52QAOAKAI6QjqCT0AoQCiCT4I6wjsCKoJNglCAIED7wo4CjkA0YECFYEB9oECKYECLIEC +W4DegQJfgQJgXxApZW5hYmxlZDI6IHNlbGVjdGlvbi5wcmVmcy5zaGFyZVBhc3RlYm9hcmTTAA4FhgA+ +BcQKPQpCgQElpAlMCU0JTglPgQIugQIvgQIwgQIxpAlRCVEJUQlRgQIygQIygQIygQIy2QAOAKAI6Qjq +CT0AoQCiCT4I6wjsCKoI+wpLAIID7wpOCk8A0YECFYEB9oECGYECY4ECXoDegQJigQJkXxAaZW5hYmxl +ZDM6IHNlbGVjdGlvbi5hY3RpdmVYZW5hYmxlZDPTAA4FhgA+BcQKVApZgQElpAlMCU0JTglPgQIugQIv +gQIwgQIxpAlRCVEJUQlRgQIygQIygQIygQIy1wAOAKAI6QjqAKEAogjrCOwIqgphCO8D9QpkANGBAhWB +AfaBAmeBAhOBATuBAmZfEClzZWxlY3RlZFRhZzogc2VsZWN0aW9uLnByZWZzLnN3aXRjaEtleVRhZ18Q +HHNlbGVjdGlvbi5wcmVmcy5zd2l0Y2hLZXlUYWfXAA4AoAjpCOoAoQCiCOsI7AiqCPsI/APrCP4A0YEC +FYEB9oECGYECGIDTgQIX2QAOAKAI6QjqCT0AoQCiCT4I6wjsCKoKcQlCAIUD6wp1CnYA0YECFYEB9oEC +a4ECLIECaIDTgQJqgQJsXxAuZW5hYmxlZDI6IHNlbGVjdGlvbi5sb2NhbEhvc3Quc3VwcG9ydERyYWdO +RHJvcF8QJHNlbGVjdGlvbi5sb2NhbEhvc3Quc3VwcG9ydERyYWdORHJvcNMADgWGAD4FxAp7CoCBASWk +CUwJTQlOCU+BAi6BAi+BAjCBAjGkCVEJUQlRCVGBAjKBAjKBAjKBAjLYAA4AoAjpCOoAoQCiCT4I6wjs +CKoKiAkbA/YKiwqMANGBAhWBAfaBAm+BAh+BAUyBAm6BAnBfECd2YWx1ZTogc2VsZWN0aW9uLnByZWZz +LmhpZGVDb250cm9sQmV6ZWxfECBzZWxlY3Rpb24ucHJlZnMuaGlkZUNvbnRyb2xCZXplbNMADgWGAD4F +xAqRCpOBASWhCpKBAnGhCpSBAnJfEBZOU1ZhbHVlVHJhbnNmb3JtZXJOYW1lXxAPTlNOZWdhdGVCb29s +ZWFu1wAOAKAI6QjqAKEAogjrCOwIqgj7CPwD9gj+ANGBAhWBAfaBAhmBAhiBAUyBAhfXAA4AoAjpCOoA +oQCiCOsI7AiqCPsI/AJxCP4A0YECFYEB9oECGYECGIBlgQIX1wAOAKAI6QjqAKEAogjrCOwIqgqoCRsG +4gqrANGBAhWBAfaBAneBAh+BAXOBAnZfECN2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmFsbG93Q29udHJv +bF8QHHNlbGVjdGlvbi5wcmVmcy5hbGxvd0NvbnRyb2zXAA4AoAjpCOoAoQCiCOsI7AiqCTYJGwCkCrQA +0YECFYEB9oECKYECH4AKgQJ5XxAmdmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5zaGFyZVBhc3RlYm9hcmTX +AA4AoAjpCOoAoQCiCOsI7AiqCPsI/AOqCP4A0YECFYEB9oECGYECGICxgQIX1wAOAKAI6QjqAKEAogjr +COwIqgj7CRsBLQrDANGBAhWBAfaBAhmBAh+AJYECfF8QF3ZhbHVlOiBzZWxlY3Rpb24uYWN0aXZl1wAO +AKAI6QjqAKEAogjrCOwIqgk2CPwD9Ak5ANGBAhWBAfaBAimBAhiBAQ6BAijZAA4AoAjpCOoJPQChAKIJ +PgjrCOwIqgj7CUIAjgP0CUUK1ADRgQIVgQH2gQIZgQIsgQJ9gQEOgQIrgQJ/0wAOBYYAPgXECtcK3IEB +JaQJTAlNCU4JT4ECLoECL4ECMIECMaQJUQlRCVEJUYECMoECMoECMoECMtkADgCgCOkI6gk9AKEAogk+ +COsI7AiqCSQKSwCPA/QK6ArpANGBAhWBAfaBAiOBAmOBAn6BAQ6BAoGBAoJfEC1lbmFibGVkMzogc2Vs +ZWN0aW9uLnByZWZzLmxpbWl0UGFzdGVib2FyZFNpemXTAA4FhgA+BcQK7QrygQElpAlMCU0JTglPgQIu +gQIvgQIwgQIxpAlRCVEJUQlRgQIygQIygQIygQIy1wAOAKAI6QjqAKEAogjrCOwIqgj7CPwG4gj+ANGB +AhWBAfaBAhmBAhiBAXOBAhfXAA4AoAjpCOoAoQCiCOsI7AiqCPsI/AF6CP4A0YECFYEB9oECGYECGIBN +gQIX1wAOAKAI6QjqAKEAogjrCOwIqgkaCPwD9QsLANGBAhWBAfaBAiCBAhiBATuBAoZfECNlbmFibGVk +OiBzZWxlY3Rpb24ucHJlZnMucmVxdWlyZUtledkADgCgCOkI6gk9AKEAogk+COsI7AiqCPsJQgCTA/UJ +RQsVANGBAhWBAfaBAhmBAiyBAoWBATuBAiuBAojTAA4FhgA+BcQLGAsdgQElpAlMCU0JTglPgQIugQIv +gQIwgQIxpAlRCVEJUQlRgQIygQIygQIygQIy1wAOAKAI6QjqAKEAogjrCOwIqgj7CPwD9wj+ANGBAhWB +AfaBAhmBAhiBAVCBAhfXAA4AoAjpCOoAoQCiCOsI7AiqCywJGwP7Cy8A0YECFYEB9oECjIECH4EBZYEC +i18QJ3ZhbHVlOiBzZWxlY3Rpb24ucHJlZnMuYXV0b2NoZWNrVmVyc2lvbl8QIHNlbGVjdGlvbi5wcmVm +cy5hdXRvY2hlY2tWZXJzaW9u1wAOAKAI6QjqAKEAogjrCOwIqgorCRsD7As4ANGBAhWBAfaBAl2BAh+A +14ECjl8QK3ZhbHVlOiBzZWxlY3Rpb24ucHJlZnMucmVxdWlyZVBhc3RlYm9hcmRLZXnXAA4AoAjpCOoA +oQCiCOsI7AiqCV0JGwF4C0AA0YECFYEB9oECNYECH4BFgQKQXxAndmFsdWU6IHNlbGVjdGlvbi5wcmVm +cy5lbmFibGVFbmNyeXB0aW9u1AAOAKALQwtEC0UG4gtHC0hYTlNNYXJrZXJWTlNGaWxlgQKUgQFzgQKT +gQKSXxAQTlNUb29sVGlwSGVscEtleV8QTEFuaGFrZW4sIHVtIGFuZGVyZW4gdGVsZXBvcnQtQmVudXR6 +ZXJuIGRpZSBTdGV1ZXJ1bmcgSWhyZXMgTWFjcyB6dSBlcmxhdWJlbi7SADcAOAtMC02iC00AO18QEU5T +SUJIZWxwQ29ubmVjdG9y1AAOAKALQwtEC0UG4wtRC0iBApSBAXeBApaBApJvEA8AQQBiAG8AdQB0ACAA +dABlAGwAZQBwAG8AcgB0ICbUAA4AoAtDC0QLRQEtC1cLSIEClIAlgQKYgQKSXxAjQW5oYWtlbiwgdW0g +dGVsZXBvcnQgenUgYWt0aXZpZXJlbi7UAA4AoAtDC0QLRQOpC10LSIEClICugQKagQKSXxBjRGllcyBp +c3QgZGVyIExheW91dC1CZXJlaWNoLiBWZXJ0ZWlsZW4gU2llIGRpZSB6dSBzdGV1ZXJuZGVuIE1hY3Mg +bmFjaCBCZWxpZWJlbiwgdW0gc2llIHp1IHN0ZXVlcm4u1AAOAKALQwtEC0UD8QtjC0iBApSBAQKBApyB +ApJfEL9GdW5rdGlvbmllcnQgbnVyLCB3ZW5uIGRpZSBSZWNobmVyIHBlciBFdGhlcm5ldCB2ZXJidW5k +ZW4gc2luZCB1bmQgZGllIE9wdGlvbiAiQmVpIGFkbWluaXN0cmF0aXZlbiBFdGhlcm5ldC1adWdyaWZm +ZW4gYXVmd2FjaGVuIiBpbSBCZXJlaWNoICJFbmVyZ2llIHNwYXJlbiIgZGVyIFN5c3RlbXN0ZXVlcnVu +ZyBha3RpdmllcnQgaXN0LtQADgCgC0MLRAtFA/MLaQtIgQKUgQEKgQKegQKSbxBzAEQAbwBwAHAAZQBs +AGIAZQByAPwAaAByAHUAbgBnACAAYgBlAGQAZQB1AHQAZQB0ACwAIABkAGEAcwBzACAAZABlAHIAIABC +AGkAbABkAHMAYwBoAGkAcgBtAHIAYQBuAGQAIAB6AHcAZQBpAG0AYQBsACAAYgBlAHIA/ABoAHIAdAAg +AHcAZQByAGQAZQBuACAAbQB1AHMAcwAsACAAYgBlAHYAbwByACAAZABlAHIAIABXAGUAYwBoAHMAZQBs +ACAAZAB1AHIAYwBoAGcAZQBmAPwAaAByAHQAIAB3AGkAcgBkAC7SAA4APgttC26BAqKvEH8GYQEGA/QG +4wP6Ar8D9gcgBNQHSwHRB1UBNAEJBiMBfQasBAAG/QPpBJsFQQF5BNIFDgdKB00GJgIAA/sHSAGCAwEG +6gdJBg4D5gCxAsAD8AF4AS0BTwPvAhIGCgPrCCwGcwfjAb4DnAcuCI4B4wOXBVID5wOpAXsD8wF1BiUD +7AP1A/kBYQH/A/cCzwK+BGMEQQPyB5wBegf4BNMDlQIRAKQD6AaDAooD6gP4ATEFHwPuA/EDbAvKBFID +fQF8BNEHTAEPBlAG4gJcBHQCcQQhA7kFXgC4CKoHxwE5BrwHhQOqAQEEMQQRAXcCUwX/AK4EhAMYB0cH +rwYnBJYFMIEBUoAcgQEOgQF3gQFhgH6BAUyBAYKA+IEBqIBHgQGTgCiAH4EBRICfgQFjgMGBAXmAy4Dj +gQEMgEmA8oEBAIEBo4EBtIEBSIBQgQFlgQGYgDOAgoEBdYEBnYEBQIC/gA2Ag4D+gEWAJYAsgN6AV4EB +P4DTgQHIgQFXgQGxgEKArIEBhoEB6YBLgLqBARCAw4CugGGBAQqAMYEBRoDXgQE7gQFZgLiAXYEBUIB5 +gHGA2YDRgQEGgQGhgE2BAbaA9YCqgFWACoDHgQFbgGmAz4EBVYAmgQEEgNuBAQKAnYECoIDVgKGAm4Dv +gQGvgC6BAU6BAXOAkYDdgGWAyYCzgQERgLyBAfaBAauBAXGBAWeBAZqAsYAbgM2AxYBAgJWBAT2AC4Dg +gImBAYmBAaWBAUqA4oEBCNIADgAyADML8IAEgQKhXU5TQXBwbGljYXRpb27SADcAOAvzAjqiAjoAO9IA +DgA+C20L9oECoq8QfwP3AQ8ArgExAK4CcQCuAB8EmwcuAXgHRwEtAQEGDgEGA/oD5gbjAK4EhAPzAQYE +mwPwBy4HLgYOAXoArgcuAXUCvwbiBy4F/wCuAKQCcQCuAQYBMQExAK4BegYOAK4AHwP4B0wBdwOVByAA +HwF5AU8D9ACuA5wBBgCuAQYGDgCuAK4ArgFPAXoArgK+AnED7APqAK4HSQEGB00EmwFPAXoArgCuA/kB +ewCuAK4BOQPxAK4ArgF8AB8D6wF9AQYEmwcuAWED9gExAXsD7gF7A+gDqgVSA5cAHwdLCI4D+wdIA5wB +BgPpA+cBBgF7A/UAuAPvAsAHLgdKBg4EmwPygQFQgC6AC4AmgAuAZYALgAKA44EBhoBFgQGJgCWAG4EB +QIAcgQFhgL+BAXeAC4DggQEKgByA44D+gQGGgQGGgQFAgE2AC4EBhoAxgH6BAXOBAYaBAT2AC4AKgGWA +C4AcgCaAJoALgE2BAUCAC4ACgQFVgQGvgECAqoEBgoACgEmALIEBDoALgKyAHIALgByBAUCAC4ALgAuA +LIBNgAuAcYBlgNeAz4ALgQGdgByBAbSA44AsgE2AC4ALgQFZgGGAC4ALgQFxgQECgAuAC4CbgAKA04Cf +gByA44EBhoC4gQFMgCaAYYDbgGGAx4CxgQEQgLqAAoEBqIEB6YEBZYEBmICsgByAy4DDgByAYYEBO4C8 +gN6Ag4EBhoEBo4EBQIDjgQEG0gAOAD4LbQx4gQKirxCABmEBBgP0BuMD+gK/AB8HIAP2B0sE1AHRB1UB +NAEJBiMBfQasBAAHSgb9A+kBeQVBB00FDgSbBNIGJgIAB0kHSAP7AYIG6gMBBg4D5gCxAsAD8AF4AS0B +TwPvAhIGCgPrCCwH4wZzAb4HLgOcCI4B4wOXBVID5wOpAXsD8wF1BiUD7AP1AWED+QH/A/cCzwK+A/IE +YwRBB5wH+AF6BNMDlQIRAKQD6AaDAooD6gP4ATEFHwPuA/EDbAvKBFIDfQF8B0wE0QEPBlAG4gJcBHQC +cQQhA7kFXgC4CKoHxwE5BrwHhQOqAQEBdwQxBBECUwX/AK4EhAMYB0cHrwYnBJYFMIEBUoAcgQEOgQF3 +gQFhgH6AAoEBgoEBTIEBqID4gEeBAZOAKIAfgQFEgJ+BAWOAwYEBo4EBeYDLgEmBAQyBAbSBAQCA44Dy +gQFIgFCBAZ2BAZiBAWWAM4EBdYCCgQFAgL+ADYCDgP6ARYAlgCyA3oBXgQE/gNOBAciBAbGBAVeAQoEB +hoCsgQHpgEuAuoEBEIDDgK6AYYEBCoAxgQFGgNeBATuAuIEBWYBdgQFQgHmAcYEBBoDZgNGBAaGBAbaA +TYD1gKqAVYAKgMeBAVuAaYDPgQFVgCaBAQSA24EBAoCdgQKggNWAoYCbgQGvgO+ALoEBToEBc4CRgN2A +ZYDJgLOBARGAvIEB9oEBq4EBcYEBZ4EBmoCxgBuAQIDNgMWAlYEBPYALgOCAiYEBiYEBpYEBSoDigQEI +0gAOAD4LbQz7gQKirxCADPwM/Qz+DP8NAA0BDQINAw0EDQUNBg0HDQgNCQ0KDQsNDA0NDQ4NDw0QDREN +Eg0TDRQNFQ0WDRcNGA0ZDRoNGw0cDR0NHg0fDSANIQ0iDSMNJA0lDSYNJw0oDSkNKg0rDSwNLQ0uDS8N +MA0xDTINMw00DTUNNg03DTgNOQ06DTsNPA09DT4NPw1ADUENQg1DDUQNRQ1GDUcNSA1JDUoNSw1MDU0N +Tg1PDVANUQ1SDVMNVA1VDVYNVw1YDVkNWg1bDVwNXQ1eDV8NYA1hDWINYw1kDWUNZg1nDWgNaQ1qDWsN +bA1tDW4Nbw1wDXENcg1zDXQNdQ12DXcNeA15DXoNe4ECpoECp4ECqIECqYECqoECq4ECrIECrYECroEC +r4ECsIECsYECsoECs4ECtIECtYECtoECt4ECuIECuYECuoECu4ECvIECvYECvoECv4ECwIECwYECwoEC +w4ECxIECxYECxoECx4ECyIECyYECyoECy4ECzIECzYECzoECz4EC0IEC0YEC0oEC04EC1IEC1YEC1oEC +14EC2IEC2YEC2oEC24EC3IEC3YEC3oEC34EC4IEC4YEC4oEC44EC5IEC5YEC5oEC54EC6IEC6YEC6oEC +64EC7IEC7YEC7oEC74EC8IEC8YEC8oEC84EC9IEC9YEC9oEC94EC+IEC+YEC+oEC+4EC/IEC/YEC/oEC +/4EDAIEDAYEDAoEDA4EDBIEDBYEDBoEDB4EDCIEDCYEDCoEDC4EDDIEDDYEDDoEDD4EDEIEDEYEDEoED +E4EDFIEDFYEDFoEDF4EDGIEDGYEDGoEDG4EDHIEDHYEDHoEDH4EDIIEDIYEDIoEDI4EDJIEDJW8QGgBC +AHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAEoAZQB0AHoAdAAgAHAAcgD8AGYAZQBuACldQ3VzdG9tIFZp +ZXctMlpUZXh0IEZpZWxkbxAVAEIAZQB2AGUAbAAgAEIAdQB0AHQAbwBuACAAKABBAGIAbwB1AHQgJgAp +XxASU3RhdGljIFRleHQgKExvbmcpXxAWVGFibGUgQ29sdW1uIChhZGRyZXNzKVxGaWxlJ3MgT3duZXJV +QWJvdXRfED9DaGVjayBCb3ggKEJlemVsIGJlaSBkZXIgU3RldWVydW5nIGVpbmVzIGFuZGVyZW4gTWFj +cyBhbnplaWdlbilvEBsATABpAG4AawAgAFQAZQB4AHQAIABGAGkAZQBsAGQAIAAoJ6QAIAB3AGUAYgBz +AGkAdABlAClvEBcATQBlAG4AdQAgAEkAdABlAG0AIAAoIeoAIABDAGEAcABzACAATABvAGMAawApbxAo +AEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgAVgBlAHIAcwBjAGgAbAD8AHMAcwBlAGwAdQBuAGcAIABh +AGsAdABpAHYAaQBlAHIAZQBuAClfEBVJbWFnZSBDZWxsICh0ZWxlcG9ydClfECFCdXR0b24gQ2VsbCAo +dGVsZXBvcnQgYWt0aXZpZXJlbilvECMAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABaAGUAcgB0AGkA +ZgBpAGsAYQB0ACAAYQB1AHMAdwDkAGgAbABlAG4gJgApbxAcAE0AZQBuAHUAIABJAHQAZQBtACAAKCMY +ACAAQgBlAGYAZQBoAGwAcwB0AGEAcwB0AGUAKQAtADFvECYAUwB0AGEAdABpAGMAIABUAGUAeAB0ACAA +KABWAGUAcgB0AHIAYQB1AGUAbgBzAHcA/AByAGQAaQBnAGUAIABIAG8AcwB0AHMAOgApXxAWVGV4dCBG +aWVsZCBDZWxsIChMb25nKV8QI0J1dHRvbiBDZWxsIChOdXIsIHdlbm4ga2xlaW5lciBhbHMpbxCJAFMA +dABhAHQAaQBjACAAVABlAHgAdAAgACgAqQAgADIAMAAwADMALQAyADAAMAA4ACAAYQBiAHkAcwBzAG8A +ZgB0AAoAQgBhAHMAaQBlAHIAdAAgAGEAdQBmACAAZQBpAG4AZQByACAASQBkAGUAZQAgAHYAbwBuACAA +RABhAHYAZQAgAFMAYQBnACAAdQBuAGQAIABKAGUAcgBlAG0AeQAgAFEAdQBpAG4AbgAuAAoAQgBlAHMA +bwBuAGQAZQByAGUAcgAgAEQAYQBuAGsAIABnAGUAaAB0ACAAYQBuACAATQBhAG4AdQAwADIALAAgAEoA +dQBsAGkAZQBuACAAdQBuAGQAIABQAGEAdQBsAC4AKW8QFABCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAo +AEEAYgBvAHUAdCAmAClfECBTdGF0aWMgVGV4dCAoQmlsZHNjaGlybXdlY2hzZWw6KW8QHgBTAHQAYQB0 +AGkAYwAgAFQAZQB4AHQAIAAoAFYAZQByAHMAYwBoAGwA/ABzAHMAZQBsAHUAbgBnADoAKW8QIQBCAHUA +dAB0AG8AbgAgAEMAZQBsAGwAIAAoAEIAZQBpACAARABvAHAAcABlAGwAYgBlAHIA/ABoAHIAdQBuAGcA +KW8QGwBMAGkAbgBrACAAVABlAHgAdAAgAEYAaQBlAGwAZAAgACgnpAAgAHMAcABlAG4AZABlAG4AKV8Q +F1RleHQgRmllbGQgQ2VsbCAoU2hvcnQpXxARTWVudSAoT3RoZXJWaWV3cylvEBgATQBlAG4AdQAgAEkA +dABlAG0AIAAoIwMAIABjAHQAcgBsAC0AVABhAHMAdABlAClvEBsATQBlAG4AdQAgAEkAdABlAG0AIAAo +IecAIABTAGgAaQBmAHQALQBUAGEAcwB0AGUAKQAtADFfEDtCdXR0b24gQ2VsbCAoQWJmcmFnZSwgb2Ig +ZGllIEFuZnJhZ2UgYW5nZW5vbWVuIHdlcmRlbiBzb2xsKV8QHlZlcnNpb24gVGV4dCBGaWVsZCAoPHZl +cnNpb24+KV8QFlN0YXRpYyBUZXh0ICh0ZWxlcG9ydClvECsAQwBoAGUAYwBrACAAQgBvAHgAIAAoAEEA +dQB0AG8AbQBhAHQAaQBzAGMAaABlACAAUAByAPwAZgB1AG4AZwAgAGIAZQBpAG0AIABTAHQAYQByAHQA +KW8QawBUAGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgARQBpAG4AdAByAOQAZwBlACAAaQBu +ACAAZABlAHIAIABMAGkAcwB0AGUAIABrAPYAbgBuAGUAbgAgAGQAdQByAGMAaAAgAEEAdQBzAHcAYQBo +AGwAIAB1AG4AZAAgAEQAcgD8AGMAawBlAG4AIABkAGUAcgAgAEwA9gBzAGMAaABlAG4ALQBUAGEAcwB0 +AGUAIABlAG4AdABmAGUAcgBuAHQAIAB3AGUAcgBkAGUAbgAuAClfEClCdXR0b24gQ2VsbCAoRGllc2Vu +IE1hYyBnZW1laW5zYW0gbnV0emVuKV8QG1RleHQgRmllbGQgQ2VsbCAoVGV4dCBDZWxsKV8QE01lbnUg +KE90aGVyVmlld3MpLTFfECFDaGVjayBCb3ggKE51ciwgd2VubiBrbGVpbmVyIGFscylfECxCdXR0b24g +Q2VsbCAoWndpc2NoZW5hYmxhZ2Ugc3luY2hyb25pc2llcmVuKV8QGlRhYmxlIENvbHVtbiAoY2VydGlm +aWNhdGUpXxATU3RhdGljIFRleHQgKFNob3J0KW8QJgBDAGgAZQBjAGsAIABCAG8AeAAgACgAVgBlAHIA +cwBjAGgAbAD8AHMAcwBlAGwAdQBuAGcAIABhAGsAdABpAHYAaQBlAHIAZQBuAClfEB9DaGVjayBCb3gg +KHRlbGVwb3J0IGFrdGl2aWVyZW4pXxArVG9wIFRhYiBWaWV3IChMYXlvdXQsIFNpY2hlcmhlaXQsIE9w +dGlvbmVuKW8QHQBQAG8AcAB1AHAAIABCAHUAdAB0AG8AbgAgACgjGAAgAEIAZQBmAGUAaABsAHMAdABh +AHMAdABlAClfECJCdXR0b24gQ2VsbCAoQXV0b21hdGlzY2ggYW5uZWhtZW4pbxAZAE0AZQBuAHUAIABJ +AHQAZQBtACAAKCMlACAAVwBhAGgAbAB0AGEAcwB0AGUAKQAtADFvEDwAQwBoAGUAYwBrACAAQgBvAHgA +IAAoAEQAYQB0AGUAaQBlAG4AIABwAGUAcgAgAEQAcgBhAGcAIAAmACAARAByAG8AcAAgAHoAdwBpAHMA +YwBoAGUAbgAgAE0AYQBjAHMAIAD8AGIAZQByAHQAcgBhAGcAZQBuAClfEBFUcnVzdGVkSG9zdHNUYWJs +ZW8QGQBUAGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgnpAAgAGYAbwByAHUAbQApXxAZVGV4 +dCBGaWVsZCBDZWxsIChTdGF0dXM6KW8QIgBCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAFoAZQByAHQA +aQBmAGkAawBhAHQAIABhAG4AegBlAGkAZwBlAG4gJgApXkNvbnRlbnQgVmlldy0xVlZpZXctMlhQcmVm +UGFuZW8QIgBUAGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgAVgBlAHIAcwBjAGgAbAD8AHMA +cwBlAGwAdQBuAGcAOgApXxAYVGFiIFZpZXcgSXRlbSAoT3B0aW9uZW4pXxAPVGV4dCBGaWVsZCBDZWxs +bxAcAFMAdABhAHQAaQBjACAAVABlAHgAdAAgACgA3ABiAGUAcgB0AHIAYQBnAHUAbgBnAGUAbgA6AClb +TGF5b3V0IFZpZXdbU2Nyb2xsIFZpZXdvEB8AQwBoAGUAYwBrACAAQgBvAHgAIAAoAEIAZQBpACAARABv +AHAAcABlAGwAYgBlAHIA/ABoAHIAdQBuAGcAKW8QZwBTAHQAYQB0AGkAYwAgAFQAZQB4AHQAIAAoAEUA +aQBuAHQAcgDkAGcAZQAgAGkAbgAgAGQAZQByACAATABpAHMAdABlACAAawD2AG4AbgBlAG4AIABkAHUA +cgBjAGgAIABBAHUAcwB3AGEAaABsACAAdQBuAGQAIABEAHIA/ABjAGsAZQBuACAAZABlAHIAIABMAPYA +cwBjAGgAZQBuAC0AVABhAHMAdABlACAAZQBuAHQAZgBlAHIAbgB0ACAAdwBlAHIAZABlAG4ALgApbxAa +AE0AZQBuAHUAIABJAHQAZQBtACAAKCMDACAAYwB0AHIAbAAtAFQAYQBzAHQAZQApAC0AMW8QJABDAGgA +ZQBjAGsAIABCAG8AeAAgACgATgB1AHIAIABiAGUAaQAgAGcAZQBkAHIA/ABjAGsAdABlAHIAIABUAGEA +cwB0AGUAKW8QGgBQAG8AcAB1AHAAIABCAHUAdAB0AG8AbgAgACgjJQAgAFcAYQBoAGwAdABhAHMAdABl +AClfEBpUYWIgVmlldyBJdGVtIChTaWNoZXJoZWl0KV8QEUhvcml6b250YWwgU2xpZGVyXxAnUHJvdG90 +eXBlIFByb3RvdHlwZSBCdXR0b24gQ2VsbCAoUmFkaW8pbxAaAFAAdQBzAGgAIABCAHUAdAB0AG8AbgAg +ACgASgBlAHQAegB0ACAAcAByAPwAZgBlAG4AKV8QHVRleHQgRmllbGQgQ2VsbCAoVGV4dCBDZWxsKS0x +XxATVGFibGUgQ29sdW1uIChuYW1lKW8QGwBDAGgAZQBjAGsAIABCAG8AeAAgACgATQBpAHQAIABWAGUA +cgB6APYAZwBlAHIAdQBuAGcAKW8QJgBCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAE4AdQByACAAYgBl +AGkAIABnAGUAZAByAPwAYwBrAHQAZQByACAAVABhAHMAdABlAClvECsAQgB1AHQAdABvAG4AIABDAGUA +bABsACAAKABTAHQAYQB0AHUAcwAgAGkAbgAgAE0AZQBuAPwAbABlAGkAcwB0AGUAIABhAG4AegBlAGkA +ZwBlAG4AKV8QG1RleHQgRmllbGQgQ2VsbCAoPHZlcnNpb24+KW8QGwBUAGUAeAB0ACAARgBpAGUAbABk +ACAAQwBlAGwAbAAgACgnpAAgAHMAcABlAG4AZABlAG4AKVZNYXRyaXhvEBkATQBlAG4AdQAgAEkAdABl +AG0AIAAoIecAIABTAGgAaQBmAHQALQBUAGEAcwB0AGUAKV8QFlRhYiBWaWV3IEl0ZW0gKExheW91dClf +EDdCdXR0b24gQ2VsbCAoQWJ3ZWlzZW4sIHdlbm4gbmljaHQgYmVyZWl0cyBpbiBkZXIgTGlzdGUpXxAq +Q2hlY2sgQm94IChad2lzY2hlbmFibGFnZSBzeW5jaHJvbmlzaWVyZW4pbxAeAFMAdABhAHQAaQBjACAA +VABlAHgAdAAgACgAVgBlAHIAcwBpAG8AbgBzAHAAcgD8AGYAdQBuAGcAOgApW1NsaWRlciBDZWxsXxAR +VGFibGUgSGVhZGVyIFZpZXdvECkAQwBoAGUAYwBrACAAQgBvAHgAIAAoAFMAdABhAHQAdQBzACAAaQBu +ACAATQBlAG4A/ABsAGUAaQBzAHQAZQAgAGEAbgB6AGUAaQBnAGUAbgApXxAVU3RhdGljIFRleHQgKFN0 +YXR1czopW0N1c3RvbSBWaWV3XxAzQnV0dG9uIENlbGwgKFZlcnN1Y2hlLCBzY2hsYWZlbmRlIE1hY3Mg +YXVmenV3ZWNrZW4pbxAmAEMAaABlAGMAawAgAEIAbwB4ACAAKABOAHUAcgAgAGIAZQBpACAAZwBlAGQA +cgD8AGMAawB0AGUAcgAgAFQAYQBzAHQAZQApAC0AMV8QMUNoZWNrIEJveCAoVmVyc3VjaGUsIHNjaGxh +ZmVuZGUgTWFjcyBhdWZ6dXdlY2tlbilfECZUZXh0IEZpZWxkIENlbGwgKFN0ZXVlcnVuZ3MtQW5mcmFn +ZW46KVtBcHBsaWNhdGlvbm8QPgBCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAEQAYQB0AGUAaQBlAG4A +IABwAGUAcgAgAEQAcgBhAGcAIAAmACAARAByAG8AcAAgAHoAdwBpAHMAYwBoAGUAbgAgAE0AYQBjAHMA +IAD8AGIAZQByAHQAcgBhAGcAZQBuAClvECoAVABlAHgAdAAgAEYAaQBlAGwAZAAgAEMAZQBsAGwAIAAo +AFYAZQByAHQAcgBhAHUAZQBuAHMAdwD8AHIAZABpAGcAZQAgAEgAbwBzAHQAcwA6AClfECJTdGF0aWMg +VGV4dCAoU3RldWVydW5ncy1BbmZyYWdlbjopbxAZAEwAaQBuAGsAIABUAGUAeAB0ACAARgBpAGUAbABk +ACAAKCekACAAZgBvAHIAdQBtAClvEBcATQBlAG4AdQAgAEkAdABlAG0AIAAoIyUAIABXAGEAaABsAHQA +YQBzAHQAZQApVlZpZXctMV8QQUJ1dHRvbiBDZWxsIChCZXplbCBiZWkgZGVyIFN0ZXVlcnVuZyBlaW5l +cyBhbmRlcmVuIE1hY3MgYW56ZWlnZW4pXxAnQ2hlY2sgQm94IChEaWVzZW4gTWFjIGdlbWVpbnNhbSBu +dXR6ZW4pXxARVmVydGljYWwgU2Nyb2xsZXJvECgAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABOAHUA +cgAgAGIAZQBpACAAZwBlAGQAcgD8AGMAawB0AGUAcgAgAFQAYQBzAHQAZQApAC0AMV8QJlRhYmxlIFZp +ZXcgKE5hbWUsIEFkcmVzc2UsIFplcnRpZmlrYXQpbxAiAFQAZQB4AHQAIABGAGkAZQBsAGQAIABDAGUA +bABsACAAKABWAGUAcgBzAGkAbwBuAHMAcAByAPwAZgB1AG4AZwA6AClfEGFUZXh0IEZpZWxkIENlbGwg +KEdlbWVpbnNhbWUgTWFjcyBhdXMgZGVtIG9iZXJlbiBCZXJlaWNoIGltIHVudGVyZW4gdmVydGVpbGVu +LCB1bSBzaWUgenUgc3RldWVybi4pXxAQTnVtYmVyIEZvcm1hdHRlcl8QElByZWZQYW5lQ29udHJvbGxl +cm8QGwBUAGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgnpAAgAHcAZQBiAHMAaQB0AGUAKVxD +b250ZW50IFZpZXdvEC0AQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABBAHUAdABvAG0AYQB0AGkAcwBj +AGgAZQAgAFAAcgD8AGYAdQBuAGcAIABiAGUAaQBtACAAUwB0AGEAcgB0AClfEBpUZXh0IEZpZWxkIENl +bGwgKHRlbGVwb3J0KV8QXVN0YXRpYyBUZXh0IChHZW1laW5zYW1lIE1hY3MgYXVzIGRlbSBvYmVyZW4g +QmVyZWljaCBpbSB1bnRlcmVuIHZlcnRlaWxlbiwgdW0gc2llIHp1IHN0ZXVlcm4uKW8QIwBQAHUAcwBo +ACAAQgB1AHQAdABvAG4AIAAoAFoAZQByAHQAaQBmAGkAawBhAHQAIABhAHUAcwB3AOQAaABsAGUAbiAm +AClvECIAUAB1AHMAaAAgAEIAdQB0AHQAbwBuACAAKABaAGUAcgB0AGkAZgBpAGsAYQB0ACAAYQBuAHoA +ZQBpAGcAZQBuICYAKV8QJFRleHQgRmllbGQgQ2VsbCAoQmlsZHNjaGlybXdlY2hzZWw6KW8QIABUAGUA +eAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgA3ABiAGUAcgB0AHIAYQBnAHUAbgBnAGUAbgA6AClf +EBNIb3Jpem9udGFsIFNjcm9sbGVybxAgAFAAbwBwACAAVQBwACAAQgB1AHQAdABvAG4AIABDAGUAbABs +ACAAKCMlACAAVwBhAGgAbAB0AGEAcwB0AGUAKV1DdXN0b20gVmlldy0xbxAjAFAAbwBwACAAVQBwACAA +QgB1AHQAdABvAG4AIABDAGUAbABsACAAKCMYACAAQgBlAGYAZQBoAGwAcwB0AGEAcwB0AGUAKV8QFUJ1 +dHRvbiBDZWxsIChBbnplaWdlKV8QFUltYWdlIFZpZXcgKHRlbGVwb3J0KW8QjQBUAGUAeAB0ACAARgBp +AGUAbABkACAAQwBlAGwAbAAgACgAqQAgADIAMAAwADMALQAyADAAMAA4ACAAYQBiAHkAcwBzAG8AZgB0 +AAoAQgBhAHMAaQBlAHIAdAAgAGEAdQBmACAAZQBpAG4AZQByACAASQBkAGUAZQAgAHYAbwBuACAARABh +AHYAZQAgAFMAYQBnACAAdQBuAGQAIABKAGUAcgBlAG0AeQAgAFEAdQBpAG4AbgAuAAoAQgBlAHMAbwBu +AGQAZQByAGUAcgAgAEQAYQBuAGsAIABnAGUAaAB0ACAAYQBuACAATQBhAG4AdQAwADIALAAgAEoAdQBs +AGkAZQBuACAAdQBuAGQAIABQAGEAdQBsAC4AKW8QGQBNAGUAbgB1ACAASQB0AGUAbQAgACgh6gAgAEMA +YQBwAHMAIABMAG8AYwBrACkALQAxbxAaAE0AZQBuAHUAIABJAHQAZQBtACAAKCMYACAAQgBlAGYAZQBo +AGwAcwB0AGEAcwB0AGUAKW8QHQBCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAE0AaQB0ACAAVgBlAHIA +egD2AGcAZQByAHUAbgBnACnSAA4APgttDf2BAqKlB0sHTAdNAnEHSYEBqIEBr4EBtIBlgQGd0gAOAD4L +bQ4FgQKipQfLB8sHywKTB5+BAamBAamBAamAZoEBntIADgA+C20ODYECoq8Q1wZhAQYD9ACXBuMD+gK/ +AB8HIAB1BNQB0QdVAJEAUwCMBiMAiwBNAJsGrACdBv0BeQCJBUEE0gdNAFoAXgIAB0gAhAbqBg4D5gB9 +AXgBLQFPAFUAVwISAHMGCgPrCCwAcQBwA5wHLgBbAEgIjgVSA+cDqQF7A/MBdQYlAGUAjgP5AWEAWALP +Ar4EYwecAXoH+ABfAJUDlQCCAKQAkAPoBoMATwP4ATEAigCcBR8D7gRSA30AXAdMAI8G4gJcAH8AfAB5 +AHgEdABOAnEAfgVeAG8HxwE5BrwAbQX/AK4EhABqAxgHRwBJAGgATABRBJYFMABUAIUD9gCWB0sAlACS +AFkBNAEJAI0BfQQABJsD6QdKBQ4GJgCHA/sHSQGCAwEAsQLAA/AASgCeA+8ASwB0AGMAYAZzB+MBvgBh +AeMDlwBSAGYD7AP1AGQB/wP3AJgAXQBuAIEAYgPyBEEAVgBsAJoE0wCZAhECigPqAIgDbAPxC8oAkwCG +AXwE0QCDAQ8GUACAAHsAdwQhAHYDuQBQAHIAuAiqB4UDqgEBBDEEEQF3AlMAawBpAHoHrwBnBieBAVKA +HIEBDoECjYEBd4EBYYB+gAKBAYKBAkOA+IBHgQGTgQKDgQHEgQJ6gQFEgQJ4gQFwgQKXgQFjgQKbgQF5 +gEmBAnSBAQyA8oEBtIEB2oEB4oBQgQGYgQJlgQF1gQFAgL+BAlOARYAlgCyBAdCBAdSAV4ECP4EBP4DT +gQHIgQI7gQI4gKyBAYaBAdyACYEB6YEBEIDDgK6AYYEBCoAxgQFGgQIWgQJ9gQFZgLiBAdaAeYBxgNmB +AaGATYEBtoEB5IECiYCqgQJegAqBAoCAx4EBW4EBgYEBVYAmgQJ1gQKZgQEEgNuA1YChgQHegQGvgQJ+ +gQFzgJGBAlaBAlCBAkyBAkmA3YEBgIBlgQJUgQERgQI2gQGrgQFxgQFngQIqgQE9gAuA4IECIYCJgQGJ +gBqBAhyBAW6BAcGA4oEBCIEBx4ECaIEBTIECioEBqIECh4EChIEB2IAogB+BAnuAn4DBgOOAy4EBo4EB +AIEBSIECbYEBZYEBnYAzgIKADYCDgP6AJIECnYDegCuBAkCBAg+BAeaBAVeBAbGAQoEB6IBLgLqBAcKB +AhqA14EBO4ECEYBdgQFQgQKPgQHggQIzgQJbgQH1gQEGgNGBAdKBAieBApWA9YECkYBVgGmAz4ECc4Cd +gQECgQKggQKFgQJpgJuA74ECYYAugQFOgQJYgQJOgQJGgMmBAkSAs4EBv4ECPIC8gQH2gQGagLGAG4DN +gMWAQICVgQIkgQIdgQJNgQGlgQIbgQFK0gAOAD4LbQ7ngQKirxDXDugO6Q7qDusO7A7tDu4O7w7wDvEO +8g7zDvQO9Q72DvcO+A75DvoO+w78Dv0O/g7/DwAPAQ8CDwMPBA8FDwYPBw8IDwkPCg8LDwwPDQ8ODw8P +EA8RDxIPEw8UDxUPFg8XDxgPGQ8aDxsPHA8dDx4PHw8gDyEPIg8jDyQPJQ8mDycPKA8pDyoPKw8sDy0P +Lg8vDzAPMQ8yDzMPNA81DzYPNw84DzkPOg87DzwPPQ8+Dz8PQA9BD0IPQw9ED0UPRg9HD0gPSQ9KD0sP +TA9ND04PTw9QD1EPUg9TD1QPVQ9WD1cPWA9ZD1oPWw9cD10PXg9fD2APYQ9iD2MPZA9lD2YPZw9oD2kP +ag9rD2wPbQ9uD28PcA9xD3IPcw90D3UPdg93D3gPeQ96D3sPfA99D34Pfw+AD4EPgg+DD4QPhQ+GD4cP +iA+JD4oPiw+MD40Pjg+PD5APkQ+SD5MPlA+VD5YPlw+YD5kPmg+bD5wPnQ+eD58PoA+hD6IPow+kD6UP +pg+nD6gPqQ+qD6sPrA+tD64Prw+wD7EPsg+zD7QPtQ+2D7cPuA+5D7oPuw+8D70PvoEDKoEDK4EDLIED +LYEDLoEDL4EDMIEDMYEDMoEDM4EDNIEDNYEDNoEDN4EDOIEDOYEDOoEDO4EDPIEDPYEDPoEDP4EDQIED +QYEDQoEDQ4EDRIEDRYEDRoEDR4EDSIEDSYEDSoEDS4EDTIEDTYEDToEDT4EDUIEDUYEDUoEDU4EDVIED +VYEDVoEDV4EDWIEDWYEDWoEDW4EDXIEDXYEDXoEDX4EDYIEDYYEDYoEDY4EDZIEDZYEDZoEDZ4EDaIED +aYEDaoEDa4EDbIEDbYEDboEDb4EDcIEDcYEDcoEDc4EDdIEDdYEDdoEDd4EDeIEDeYEDeoEDe4EDfIED +fYEDfoEDf4EDgIEDgYEDgoEDg4EDhIEDhYEDhoEDh4EDiIEDiYEDioEDi4EDjIEDjYEDjoEDj4EDkIED +kYEDkoEDk4EDlIEDlYEDloEDl4EDmIEDmYEDmoEDm4EDnIEDnYEDnoEDn4EDoIEDoYEDooEDo4EDpIED +pYEDpoEDp4EDqIEDqYEDqoEDq4EDrIEDrYEDroEDr4EDsIEDsYEDsoEDs4EDtIEDtYEDtoEDt4EDuIED +uYEDuoEDu4EDvIEDvYEDvoEDv4EDwIEDwYEDwoEDw4EDxIEDxYEDxoEDx4EDyIEDyYEDyoEDy4EDzIED +zYEDzoEDz4ED0IED0YED0oED04ED1IED1YED1oED14ED2IED2YED2oED24ED3IED3YED3oED34ED4IED +4YED4oED44ED5IED5YED5oED54ED6IED6YED6oED64ED7IED7YED7oED74ED8IED8YED8oED84ED9IED +9YED9oED94ED+IED+YED+oED+4ED/IED/YED/oED/4EEABIAAYfwEgAElRsRAUYRAaUQrBEBwRD5EgAE +lR0QoREBShEBnxIAAYgdEgABh0QRAVcSAASU+xEBsBEBiBEBQRDKEgAElSASAAGIYRIABJUiEgABh0wS +AASVCREBrxIABJUPEQGgEQElEQEKEQG1EKURAZESAAGHKBEBhhEBRREBfxEBfRCuELoQrREBBBEBtxEB +ZBEBiREBkhEBlhEBXBIABJUREL0QohDpEM0QDBIAAYfmEQFrEIkQ9hIABJUOEQG5EQGHEQFeEQFLEQG/ +EQFlEgAElPkSAAGHmBD4EgABiDsSAAGHRhEBshIAAYfFEKoRAWAQuxEBpxC/EQFbEQFvEgABiF8QqxEB +cBDJEQFAEgAElSESAAGIWxDCEgABiDISAAGIChC3EQFaEIgSAAGHlhEBxBEBxxEBXREBwhIAAYdiEQED +EPcRAcMRAU4RAYMSAAGHwxIAAYgBEQGkEgABiCUSAASVHBIAAYg8EQFJEQFpEKQSAASU+hEBWBEBEBC4 +EQGhEgABh68RAZcRAZMSAASVBxEBYxEBIxEBxhIABJT8EgABh04SAASU+BEBVhEBahIAAYflEQGdEQFu +EKgSAAGIYBEBixIABJUMEQFhEKYSAAGIWRIAAYeZEgABh18RAWgRAcAQrxIABJUjEQGcEQGVEOgQ6hIA +AYgQEgABh8QSAAGIIRAaEgAElQoQvBCREgAElRIRAZsRAYURAakSAAGIUhEBUBEBgBDOEQGCEQGmEQEq +EQEPEgABh6gRAVERAaMSAASVHxEBohIABJUeEQG2EgAElNYRAQgSAASVDRIAAYhYEQG7E//////////9 +EQGPEQHFEQG4EQGeEQGoEQFmEgAElQgRAb4SAASVBhEBuhIAAYgPEQFZEgABhzgQnBEBRBC+EQEpEgAB +h0UQmBIABJT3EgABiA4SAAGICxEBgRIAAw42EQFDEQFCEgAElQUSAAGHSBEBvREBitIADgA+AEYQk4BZ +oNIADgA+C20QloECoqDSAA4APgttEJmBAqKg0gA3ADgQmxCcohCcADteTlNJQk9iamVjdERhdGEACAAZ +ACIAJwAxADoAPwBEAFIAVABmCHQIegjFCMwI0wjhCPMJDwkdCSkJNQlDCU4JXAl4CYYJmQmrCcUJzwnc +Cd4J4QnkCecJ6gnsCe8J8Qn0CfcJ+gn9Cf8KAQoECgcKDAoPChgKJAomCigKOwpECk0KWApdCmwKdQqI +CpEKnAqeCp8KqAqvCrwKwgrLCs0LfguAC4ILhAuGC4kLjAuPC5ILlQuYC5sLnguhC6QLpwuqC60LsAuz +C7YLuQu8C78LwgvFC8gLywvOC9EL1AvXC9oL3QvgC+ML5gvpC+wL7wvyC/UL+Av7C/4MAQwEDAcMCgwN +DBAMEwwWDBkMHAwfDCIMJQwoDCsMLgwxDDQMNww6DD0MQAxDDEYMSQxMDE8MUgxVDFgMWwxeDGEMZAxn +DGoMbQxwDHMMdgx5DHwMfwyQDJ4MpwyvDLEMswy1DLcM1AzmDO4M9Qz+DQgNFA0WDRgNGg0cDR8NIA0i +DT8NSg1WDVgNWg1cDV8NYg1kDWYNgA21DcEN1w3sDfsODg4gDisONQ5DDlUOYg5wDnkOew59Dn8OgQ6D +DoUOhw6JDosOjQ6PDpQOtQ7GDs0O1A7dDt8O6A7qDu0O+g8DDwgPDw8YDyQPJg8oDzEPOg8/D1UPVg9f +D2gPdQ+CD4sPlg+fD6kPsA+8D9YP3w/mD/0QDBAdEB8QIRAjECUQRhBPEFEQUxBVEFcQYBBhEGMQZRCG +EIgQihCMEI4QkBCSEJQQrhDfEOQQ5hDoEOoQ7BDuEPAQ8hD3EQARLRE2EUARQhFLEVIRZBFtEYcRmBGa +EZwRnhGgEcERwxHFEccRyRHKEcwRzhHvEfIR9BH3EfoR/RH/EgESBBIdElISVBJWElgSWhJcEl4SYBJ2 +EosSnBKeEqASohKlEtoS6RLzEwcTIBM4EzoTPRM/E0ETQxNFE0cTSBNKE0sTTRNWE1gTWxNdE3oTfBN+ +E4ATghOFE4cTiROSE5QTlxOZE6ITpBO3E7kTuxO9E78TwRPDE8UTxxPJE+oT7BPuE/AT8hP1E/YT+BP6 +FBMUNBRIFFQUVhRYFFoUXBReFGMUZRUaFSsVLRU2FTgVOxVQFVgVZRVxFX8VgRWDFYUVhxWJFZAVnRWq +FbIVtBW2FcIVyxXQFeUV5xXpFesV7RYAFg0WDxYSFhsWJBY2Fj8WShZWFncWeRZ7Fn0WfxaAFoIWhBae +Fs8W0RbTFtUW1xbZFtsXBhcPFxEXMhc0FzYXOBc6FzsXPRc/F1kXjheQF5IXlBeWF5gXmhecF9MX9Bf2 +F/gX+hf8F/0X/xgBGBoYOxg9GD8YQRhDGEUYShhMGG8YvhjKGNQY4xjvGQcZEhkcGTEZPxlHGUkZSxlN +GU8ZURlTGVUZVxlZGVsZXBleGWAZYhlnGWkZghmLGY0ZlBmWGZgZmhnPGdEZ0xnVGdcZ2RnbGd0aDRoW +GhgaGhooGjEaMxpsGnIadBp2Gngaehp8Gn4agBqsGuUa5xrpGusa7RrvGvEa8xr1GvgbDxsYGx8bLhs2 +G0AbRxtwG3IbdBt2G3gbfhuLG40bkBuZG6QbrRvsG/gcARwOHCEcLhw6HEgcVhxYHFocXBxeHGAcYhxk +HHcceRx7HH0cfxyBHIocjByXHJkcmxydHJ8coRzOHNgc4hzsHO4c8BzyHPQc9hz5HPsc/Rz/HQEdAx0M +HQ4dER0THW4dkB2mHbMdyB3iHf4eGR4lHkQeUx5fHmEeYx5oHmoebB5uHm8ecR56HoMehR6GHogeih6M +Ho4ekB6SHpsepx6zHr4e2x7dHt8e4R7jHuUe5x8UHxYfGB8aHxwfHh8gHyIfJB8mHzAfOR9CH1Yfbx9x +H3MfdR93H3kfkB+ZH6IfsB+5H7sfwh/EH8YfyB/xIAAgDSAaICIgLSA8IEcgUiBTIFUgVyBZIGIgZCBt +IHYgeCB9IJognyChIKMgpSCnIKkgriC7IL0gySDeIOAg4iDkIOYg+CEBIQwhICFBIUYhSCFKIUwhTiFQ +IVUhVyFhIXYheCF6IXwhfiGXIaAhpSGzIdwh3SHfIeEh4yHsIe4h9yH5IgEiHiIgIiIiJCImIigiMCJR +IlMiVSJXIlkiWyJdIn4igCKCIoQijSKPIpgimiKmIsMixSLHIskiyyLNItgi7SLvIvEi8yL1IwEjMiM0 +IzYjOCM6IzwjPiNAI0UjTiNWI2sjbSNvI3EjcyN9I4ojjCORI5ojnyOuI8YjzyPYI+MkCCQRJBokJCQm +JCgkKiQsJC4kMCQyJDskVyRkJG0keCSDJKwkriSwJLIktCS2JLgkuiTDJN8k6CTqJO0k7yUFJSAlKSUy +JT8lYCViJWQlZiVoJWklayVtJYUlpiWoJaolrCWuJbAlsiXJJeol7CXuJfAl8iXzJfUl9yYPJjAmMiY0 +JjYmOCY6JjwmbyZ6JoMmjCaZJrImuybCJtsm5CbmJu0m7ybxJvMnDCcWJxgnGiccJx4nICciJyknPidA +J0InRCdGJ08nUSdWJ1gnWid3J4MnhSeHJ4kniyeNJ48nqCe1J9In1CfWJ9gn2ifdJ94n4Cf5KBooHCge +KCAoIigkKCkoKyh9KIQojSiSKKAotSi3KLkouyi9KL8oyijjKOUo5yjpKOso7SjwKPgpDSkPKREpFCkW +KR8pISkkKSYpLykxKWApYilkKWYpaClqKWwpbilwKXIpdCl2KXkpfCl/KYIphSmIKYspjimRKZQplym0 +KbYpuCm6KbwpvSm/KdkqDioQKhIqFCoWKhgqGiocKjQqUSpTKlUqVypZKloqXCp1KpYqmCqaKpwqniqg +KqIqvyrcKt4q4CriKuQq5SrnKv8rICsiKyQrJisoKyorLCtPK2wrbitwK3IrdCt1K3crkCuxK7MrtSu3 +K7kruyu9K9Ir7yvxK/Mr9Sv3K/gr+iwTLEgsSixMLE4sUCxSLFQsViyTLLAssiy0LLYsuCy5LLss1S0K +LQwtDi0QLRItFC0WLRgtey2YLZotnC2eLaAtoS2jLb0t8i30LfYt+C36Lfwt/i4ALjMuUC5SLlQuVi5Y +LlkuWy51LqourC6uLrAusi60LrYuuC7VLtcu2S7bLt0u3i7gLvovRS9iL3Qvfy+RL6YvtC+7L8QvxS/H +L8kvyy/NL88v0S/SL9Mv1i/YL90v5i/oMBkwITA1MEAwTjBYMGUwbTBvMHEwczB4MHowfzCBMIMwhTCH +MIkwljCiMKQwpjCoMMUw0jDhMOMw5TDnMO8xATEKMQ8xIjEvMTExMzE1MUgxXDFlMWoxczF1MYAxiTGL +MZYxmDGaMZwxnjGgMc0xzzHRMdMx1THXMdkx2zHdMd8x9jIjMiUyJzIpMisyLTIvMjEyMzI1Mk4yezJ9 +Mn8ygTKDMoUyhzKJMosyjTKoMtUy1zLZMtsy3TLfMuEy4zLlMucy/jMHMwwzFTMiMzYzRTNOM1szaTOG +M4gzijOMM48zkDOSM6szzDPOM9Az0zPVM9cz3DPeM+Q0ATQDNAU0CDQLNAw0DjQoNF00XzRhNGM0ZTRo +NGo0bTSVNLI0tDS2NLk0vDS9NL802TUONRA1EjUUNRY1GTUbNR41PzVcNV41YDVjNWY1ZzVpNYM1uDW6 +Nbw1vjXANcM1xTXINfE2DjYQNhI2FTYYNhk2GzY0Nl02aTZ/Nog2ijaNNo82kjaVNpo2mzaeNqE26Dbv +Nvo3ATcNNxU3KDcvNz03UTdjN3c3ijeWN503qje8N783wjfFN8g3yjfNN9A30zfWN9k32jfdN9834jfl +N+Y35zf0N/w3/zgYOBs4HjghOCQ4JzgqOC04MDgzODY4OTg8OFU4WDhbOF44YThkOGc4ajhtOHA4czh2 +OHk4gTiQOKo4zDjfOPM4+jkHOQ85Jzk2OUo5ZzlyOX45jTmXOaM5rzmyObM5xjnHOdA51TnyOfU6AjoP +OhI6FToYOho6JzoqOis6LDo1Ojo6RzpQOlU6ajp3Ono6ezp8On86gjqLOpk6nDqeOqc6rDq1OtI61TrW +Ouk66jrsOvU6/DsSOxs7HjshOy47MTs0Ozc7OztEO0s7XztkO3k7ezt9O4A7gjuYO607rzuxO7Q7tjvA +O9073zvhO+Q75zvoO+o8BDxTPGU8ZjxoPGo8bTxvPHI8dTx2PHc8ejyDPIU8tjy5PLs8vTy/PME8wzzG +PMk81jzYPNs83jznPOk88jz0PP89Aj0FPQg9Cz0OPTs9Pj1APUI9RD1GPUg9Sz1OPXs9fj2APYI9hD2G +PYg9iz2OPbs9vj3APcI9xD3GPcg9yz3OPfs9/j4APgI+BD4GPgg+Cz4OPis+LT4vPjI+NT42Pjg+UT6G +Pog+ij6MPo4+kT6TPpY+zD7pPus+7T7wPvM+9D72Pw4/Pz9BP0M/Rj9JP0s/Tj9nP3A/cj+PP5E/kz+W +P5k/mj+cP7Q/1T/XP9k/3D/eP+E/4z/rQAhACkANQBBAE0AUQBZAL0BoQHBAhkCbQKZAsUC8QMpA50Dw +QPNA9kD5QPxBBUEOQRNBFEEdQR5BJ0EpQTpBPEFFQUhBUkFbQWRBcUF6QYVBjkGrQa1Br0GyQbVBtkG4 +QdFB8kH0QfZB+UH7Qf5CAEIFQiJCJEImQilCLEItQi9CSEJ9Qn9CgUKDQoVCiEKKQo1CzkLXQuBC6ULy +QwNDBUMIQwpDDUMlQzZDOEM6QzxDP0NcQ15DYENjQ2ZDaENrQ3RDdkN/Q4JDhUOHQ4lDqkOsQ65DsUO0 +Q7VDt0O5Q9NECEQNRA9EEUQTRBVEGEQaRB1EO0RcRF5EYERjRGZEaURqRGxEbkSHRLxEykTMRM5E0UTT +RNZE2UTbRN5E40TwRP1E/0UBRQRFD0UYRRpFJUUqRTtFPUU/RUFFREVVRVdFWUVcRV9FkEWdRapFuEXC +RdBF3UXnRflGDUYXRiNGJkYpRixGLkYzRjZGOUY8Rj9GQkZdRm9GeEZ6Rn9GnEafRqFGpEanRqpGrUa2 +RrhGx0bKRs1G0EbTRtZG2UbcRwVHEEccRx9HIkclRyZHKUcsRy1HMEczRzxHPkdNR1BHU0dWR1lHXEdf +R2JHfkeaR8BH2EgMSC1ISkhkSIVIjUiVSJ1IqEitSLBIs0i4SLlIxkjISMpIzUjWSN9I5kjySPtJBkkS +STNJNkk4STtJPkk/SUJJRUleSX9JgUmDSYZJiUmMSZFJk0mkSaZJr0myScZJ70nySfRJ90n6Sf1J/koB +SgRKB0ocSjVKVkpYSlpKXUpfSmJKZEpuSo9KkkqUSpdKmkqbSp5KoUq5StpK3EreSuFK5ErnSuxK7kvn +S/hL+kwDTAVMCEwxTDRMNkw5TDxMP0xCTENMRkxJTExMXkx0TJ1MokykTKZMqUysTK9MsEyzTLVMyEzZ +TNtM3Uz8TSVNKE0qTS1NME0zTTRNN006TT1NVE15TXtNfU2ATYNNhk2JTYtNmk3OTfdN+k38Tf9OAk4F +TgZOCU4MTg9OJk5LTk1OT05STlVOWE5bTl1OcE8gTytPRE9PT2xPdU96T41Pnk+gT6NPpU+oT79P0E/S +T9RP1k/ZT+pP7E/uT/BP80/+UA9QElAUUBZQGVAyUDtQQlBaUGtQbVBvUHJQdVCGUJhQrFC7UL5QwVDE +UMdQ0FDSUNVQ2FDvUPRQ91EAUQVRDlEVUSpRN1E/UVBRU1FVUVhRW1FtUX5RgVGDUYZRiVGXUahRqlGs +Ua5RsVHJUdpR3FHeUeBR41H7UgxSD1IRUhNSFlIwUkFSQ1JFUkdSSlJZUmpSbFJuUnBSc1KFUpZSmFKb +Up1SoFKzUsRSxlLIUspSzVLiUvNS9VL3UvlS/FMHUxhTGlMdUx9TIlMuUz9TQVNDU0VTSFNVU2ZTaFNr +U21TcFOhU6RTp1OqU61TslO1U7hTu1O+U8FT3FP0U/1T/1QIVApUDVQPVBpUKFQwVEFUQ1RFVEhUS1Rc +VF9UYlRjVGZUb1RxVKBUo1SmVKlUrFSvVLJUtVS4VLtUvlTBVMRUx1TKVM1U0FTTVNZU2VTcVN9U4lT3 +VQ9VIlU4VU9Va1WGVY1VplXLVeRV+VYLVihWQVZeVnBWhFahVrtW1FbwVvVW+FcJVwtXDVcPVxJXIFc9 +V0dXUVdwV3NXdld5V3xXfleBV7FXvVfgV+lX8FgIWCVYKFgrWC5YMVgzWDZYUlhaWG1YiliNWJBYk1iW +WJlYnFi5WLxYv1jCWMVYyFjLWOhY61juWPFY9Fj2WPlZFlkZWRxZH1kiWSRZJ1lLWVFZblmLWY5ZkVmU +WZdZmVmcWclZ71oMWg9aEloVWhhaG1oeWkVaZVqCWoVaiFqLWo5akFqTWr5a4FsFWxtbJVsoWytbLlsx +WzRbNls5WzxbWVtiW29bclt7W35bgVuEW4dbkFuTW5ZbmVucW7BbzVvoXAZcD1wsXC9cMlw1XDhcOlw9 +XGlcjFyxXLRct1y6XL1cwFzCXMVcyFzVXNhc4VzkXOdc6lztXPZc+Vz8XP9dAl0fXSJdJV0oXStdLl0x +XV5dhF2hXaRdp12qXa1dr12yXc9d0l3VXdhd213dXeBeCF4pXkZeSV5MXk9eUl5VXlhedV54Xntefl6B +XoNehl6pXsVe4l7lXuhe617uXvBe818YXxtfHl8hXyRfJ18pXyxfL188Xz9fSF9LX05fUV9UX11fYF9j +X2ZfaV+GX4lfjF+PX5JflF+XX8tf8mAPYBJgFWAYYBtgHmAhYEZgZGCBYIRgh2CKYI1gkGCTYLBgs2C2 +YLlgvGC+YMFg5mDpYOxg72DyYPVg92D6YP1hCmENYRZhGWEcYR9hImErYS5hMWE0YTdhVGFXYVphXWFg +YWNhZmGRYbVh0mHVYdhh22HeYeBh42IAYgNiBmIJYgxiD2ISYjtiYGJjYmZiaWJsYm9icmJ1YnhihWKI +YpFilGKXYppinWKmYqlirGKvYrJiz2LSYtVi2GLbYt5i4WMEYyBjPWNAY0NjRmNJY0tjTmN+Y6VjymPN +Y9Bj02PWY9lj22PeY+FkDWQaZB1kJmQpZCxkL2QyZDtkPmRBZERkR2RsZG9kcmR1ZHhke2R9ZIBkg2Sg +ZKlktmS5ZMJkxWTIZMtkzmTXZNpk3WTgZONlAGUDZQZlCWUMZQ9lEmU+ZV1lemV9ZYBlg2WGZYhli2Ww +ZbNltmW5Zbxlv2XBZcRlx2X4Zh9mLGYvZjhmO2Y+ZkFmRGZNZlBmU2ZWZllmemZ9ZoBmg2aGZolmjGaP +Zrlm3GbpZuxm72byZvVm+GcRZyNnQGdDZ0ZnSWdMZ09nUmdvZ3JndWd4Z3tnfWeAZ51noGejZ6ZnqWes +Z69n1Wf0aBFoFGgXaBpoHWgfaCJoS2hoaGtobmhxaHRodmh5aJZomWicaJ9oomikaKdowWjeaOFo5Gjn +aOpo7WjwaRVpGGkbaR5pIWkkaSdpKmktaTppPWlGaUlpTGlPaVJpW2leaWFpZGlnaYxpj2mSaZVpmGmb +aZ5poWmkadRp4Wnkae1p8GnzafZp+WoCagVqCGoLag5qK2ouajFqNGo3ajpqPWpaal1qYGpjamZqaGpr +aohqi2qOapFqlGqXappqwGrlauhq62ruavFq9Gr3avpq/WsKaw1rFmsZaxxrH2siaytrLmsxazRrN2tU +a1drWmtda2BrY2tma4NrhmuJa4xrj2uSa5Vrv2via/9sAmwFbAhsC2wNbBBsPmxbbF5sYWxkbGdsaWxs +bJZsp2ywbLdsumy9bMBsw2zWbSVtLm0zbUdtWG1bbV5tYW1kbYVtlm2ZbZttnm2hbcdt2G3bbd1t4G3j +bkluWm5dbmBuY25mbyhvOW88bz9vQm9FcC5wN3A6cTtxPnFAcUNxRnFJcUtxTnFRcVNxVnFYcVtxXXFf +cWJxZHFncWlxbHFucXBxc3F1cXdxenF9cYBxg3GFcYhxi3GNcY9xknGVcZhxmnGccZ5xoHGicaRxpnGo +capxrXGvcbJxtXG4cbpxvHG/ccJxxHHGcclxy3HNcc9x0nHUcddx2XHccd9x4XHjceZx6HHqcexx7nHx +cfRx9nH5cftx/XH/cgFyA3IGcghyCnINcg9yEnIUchdyGXIcch5yIHIiciRyJ3IpcixyL3IxcjNyNXI3 +cjlyPHI+ckFyRHJHckpyTXJPclFyU3JVcldyWXJccl5yYHJicmVyaHJrcm1ycHJ5cntyfnKMcpVymnKj +cqZzp3Oqc6xzrnOwc7JztHO2c7hzunO9c79zwnPEc8ZzyXPLc85z0HPTc9Vz13Pac9xz3nPgc+Nz5nPp +c+tz7XPwc/Jz9HP3c/pz/XP/dAF0A3QFdAd0CXQLdA10D3QSdBR0FnQZdBx0HnQgdCN0JXQndCl0LHQu +dDB0MnQ0dDZ0OXQ7dD10P3RBdEN0RXRHdEl0S3RNdE90UnRUdFd0WXRbdF10X3RhdGR0ZnRodGp0bXRw +dHJ0dHR2dHh0enR8dH50gHSDdIV0iHSKdIx0jnSQdJJ0lHSXdJl0m3SedKF0pHSndKl0q3StdK90sXSz +dLZ0uHS6dLx0v3TCdMV0x3TKdNN01nXZddx13nXhdeR153Xpdet17nXxdfR19nX4dft1/XX/dgJ2BHYH +dgl2DHYPdhF2E3YWdhl2HHYediB2I3Yldih2K3YudjB2M3Y1djh2OnY8dj52QHZCdkR2RnZIdkp2TXZP +dlJ2VXZYdlp2XXZfdmJ2ZHZmdml2a3Ztdm92cnZ0dnd2eXZ8dn52gXaDdoZ2iHaKdo12j3aRdpR2l3aZ +dpt2nXafdqF2o3amdqh2qnatdq92sna0drd2uXa8dr52wHbCdsV2x3bJdsx2z3bRdtN21XbXdtl23Hbe +duF25Hbndup27XbvdvF283b1dvd2+Xb8dv53AHcCdwV3CHcLdw13EHcZdxx4H3gieCV4KHgreC54MXg0 +eDd4Ong9eEB4Q3hGeEl4THhPeFJ4VXhYeFt4XnhheGR4Z3hqeG14cHhzeHZ4eXh8eH94gniFeIh4i3iO +eJF4lHiXeJp4nXigeKN4pnipeKx4r3iyeLV4uHi7eL54wXjEeMd4ynjNeNB403jWeNl43HjfeOJ45Xjo +eOt47njxePR493j6eP15AHkDeQZ5CXkMeQ95EnkVeRh5G3keeSF5JHkneSp5LXkweTN5Nnk5eTx5P3lC +eUV5SHlLeU55UXlUeVd5WnldeWB5Y3lmeWl5bHlveXJ5dXl4eXt5fnmBeYR5h3mKeY15kHmTeZZ5mXmc +eZ951nnkee96HHoxekp6V3pdep962HsJe1x7dHuYe+F8HHxrfIR8qn2/fep+DX5MfpF+yn7kfvh/K39k +f6J/w3/cgDWBDoE6gViBboGSgcGB3oH0gkOCZYKTgtCC9YMqg6WDuYPuhAqEUYRghGeEcIS3hNKE5IUf +hSuFN4V4hkmGgIbLhwKHH4czh12HlIe0h8qIA4hSiKuIyYkCiQmJPolXiZGJvon9igmKHYpyioqKlorM +ixuLT4t4i4SMA4xajH+MtIzljOyNMI1ajW6NwY3qjjGOlY6ojr2O9o8Dj2CPfY/dkCaQbZCUkNeQ7ZEw +kT6Rh5GfkbeS1JMJk0CTfZOGk4mTlJOXk5qTnZOfk6KTq5Ouk7mTvJO/k8KTxJPHk9CT05WElYeViZWM +lY+VkpWVlZeVmZWclZ+VoZWjlaaVqZWsla+VspW1lbiVu5W+lcGVxJXGlcmVzJXOldGV1JXXldmV3JXf +leKV5ZXnleqV7JXulfCV85X2lfiV+5X+lgCWA5YGlgmWC5YOlhGWE5YWlhmWG5Ydlh+WIpYklieWKpYt +ljCWMpY1ljeWOZY7lj6WQJZDlkaWSZZLlk6WUJZTllWWWJZbll6WYJZjlmaWaZZrlm2Wb5ZylnWWeJZ7 +ln2WgJaDloaWiZaLlo6WkJaTlpaWmZaclp+WopallqiWqpaslq+WsZa0lraWuZa8lr+WwZbElseWypbN +ltCW05bWltmW3JbeluCW45bllueW6Zbrlu6W8Zb0lveW+pb9lv+XAZcDlwWXB5cJlwyXDpcQlxOXFpcZ +lxyXH5chlySXJpcolyuXLpcwlzOXNpc4lzuXPpdBl0SXR5dKl02XT5dSl1WXWJdal12XX5dhl2OXZpdo +l2uXbpdxl3SXdpd4l3uXfZeAl4OXhpeJl4uXjpeQl5OXlpeYl5uXnpegl6KXpJeml6iXqpetl7CXs5e2 +l7mXvJfFl8iZeZl8mX+ZgpmFmYiZi5mOmZGZlJmXmZqZnZmgmaOZppmpmayZr5mymbWZuJm7mb6ZwZnE +mceZypnNmdCZ05nWmdmZ3JnfmeKZ5ZnomeuZ7pnxmfSZ95n6mf2aAJoDmgaaCZoMmg+aEpoVmhiaG5oe +miGaJJonmiqaLZowmjOaNpo5mjyaP5pCmkWaSJpLmk6aUZpUmleaWppdmmCaY5pmmmmabJpvmnKadZp4 +mnuafpqBmoSah5qKmo2akJqTmpaamZqcmp+aopqlmqiaq5qumrGatJq3mrqavZrAmsOaxprJmsyaz5rS +mtWa2Jrbmt6a4Zrkmuea6prtmvCa85r2mvma/Jr/mwKbBZsImwubDpsRmxSbF5samx2bIJsjmyabKZss +my+bMps1mzibO5s+m0GbRJtHm0qbTZtQm1ObVptZm1ybX5tim2WbaJtrm26bcZt0m3ebept9m4Cbg5uG +m4mbjJuPm5KblZuYm5ubnpuhm6Sbp5uqm62bsJuzm7abuZu8m7+bwpvFm8iby5vOm9Gb1JvXm9qb3Zvg +m+Ob5pvpm+yb75vym/Wb+Jv7m/6cA5wInAucDpwQnBOcFZwanBycH5winCecLJwvnDScN5w6nD2cP5xE +nEmcTpxTnFicW5xgnGOcZpxpnGycbpxxnHaceZx8nH+cgpyEnIaciJyLnI6ckZyUnJecmpydnKKcpJym +nKicqpysnLGctJy2nLicvZzAnMOcxpzJnMycz5zUnNmc25zgnOWc6JztnO+c8pz0nPec+Zz8nP+dBJ0G +nQmdC50OnROdGJ0anR+dJJ0mnSmdK50wnTOdNp05nTydQZ1EnUadSZ1MnU+dVJ1ZnVydYZ1mnWudbp1x +nXOdeJ17nX6dgJ2DnYidi52OnZOdlp2ZnZydoZ2mnaudrp2xnbaduZ28nb6dw53Gncudzp3QndWd2p3f +neKd5Z3nneyd753ynfSd9p37ngCeBZ4HngyeDp4QnhWeGJ4bnh6eI54mnimeK54unjGeNJ43njyeP55C +nkeeSp5PnlKeV55anl+eZJ5nnnCec552nnmefJ5/noKeh56Kno+ekp6Xnpqen56hnqSepp6pnq6esJ61 +nrqev57Cnseeyp7NntKe157ant2e5p7onume8p71nvae/58CnwOfDJ8RAAAAAAAAAgIAAAAAAAAQnQAA +AAAAAAAAAAAAAAAAnyA + + + diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..b9001cf --- /dev/null +++ b/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + teleport + CFBundleGetInfoString + + CFBundleIconFile + teleport.icns + CFBundleIdentifier + com.abyssoft.teleport + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + tlpt + CFBundleVersion + 1.2 + TPRevision + DEFINED_BY_GIT + NSMainNibFile + MainMenu + NSPrincipalClass + TPApplication + NSUIElement + 1 + SUFeedURL + http://www.abyssoft.com/software/teleport/versions.xml + + diff --git a/Italian.lproj/InfoPlist.strings b/Italian.lproj/InfoPlist.strings new file mode 100644 index 0000000..9d8ee73 --- /dev/null +++ b/Italian.lproj/InfoPlist.strings @@ -0,0 +1,5 @@ +/* Localized versions of Info.plist keys */ + +NSHumanReadableCopyright = "Copyright (c) 2003-2008, abyssoft."; +CFBundleGetInfoString = "Versione 1.1"; +TPTranslationInfoString = "Versione italiana di Massimiliano Origgi"; diff --git a/Italian.lproj/Localizable.strings b/Italian.lproj/Localizable.strings new file mode 100644 index 0000000..103fbaa --- /dev/null +++ b/Italian.lproj/Localizable.strings @@ -0,0 +1,215 @@ +/* No comment provided by engineer. */ +"%.1fG" = "%.1fG"; + +/* No comment provided by engineer. */ +"%.1fK" = "%.1fK"; + +/* No comment provided by engineer. */ +"%.1fM" = "%.1fM"; + +/* No comment provided by engineer. */ +"< 1K" = "< 1K"; + +/* Title of the dialog that appears when dragging to a file that already exists in the Finder */ +"A file already exists with this name." = "Un file con lo stesso nome è già presente."; + +/* Explanation text for the dialog that appears when dragging a file to a location where a file with the same name already exists. */ +"A file named \\U201C%@\\U201D already exists in \\U201C%@\\U201D. You can choose to use a unique name for the new file, replace the old file, or cancel the drop." = "Il file “%1$@” è già presente in “%2$@”. Puoi scegliere di usare un nome unico per il nuovo file, sostituire il vecchio file, o cancellare l'operazione."; + +/* No comment provided by engineer. */ +"A new version of teleport is available!" = "Una nuova versione di teleport è ora disponibile!"; + +/* No comment provided by engineer. */ +"Accept" = "Accetta"; + +/* No comment provided by engineer. */ +"Accept and Reject Others" = "Accetta e rifiuta gli altri"; + +/* Trust request dialog */ +"Asking host \\U201C%@\\U201D for trust" = "Sto chiedendo l'autorizzazione al sistema “%@”"; + +/* Text in dialog about accessibility */ +"Assistive Device" = "Dispositivo di assistenza"; + +/* No comment provided by engineer. */ +"Betatester!" = "Betatester!"; + +/* No comment provided by engineer. */ +"Build %@" = "Build %@"; + +/* Generic cancel button in dialog */ +"Cancel" = "Cancella"; + +/* Title in certificate selection panel */ +"Choose certificate" = "Seleziona il certificato"; + +/* No comment provided by engineer. */ +"Configure\\U2026" = "Configura…"; + +/* Title for connection failure */ +"Connection to \\U201C%@\\U201D failed." = "La connessione a “%@” è fallita."; + +/* No comment provided by engineer. */ +"controlled by %@" = "controllato da %@"; + +/* No comment provided by engineer. */ +"controlling %@" = "sto controllando %@"; + +/* Button in dialog */ +"Deactivate teleport" = "Disattiva teleport"; + +/* No comment provided by engineer. */ +"Delete pairing" = "Cancella accoppiamento"; + +/* Button title */ +"Disable Encryption" = "Disabilita crittografia"; + +/* Text in dialog about accessibility */ +"Disabled access for assistive devices" = "Disabilita l'accesso per i dispositivi di assistenza"; + +/* No comment provided by engineer. */ +"Don't show again" = "Non mostrare nuovamente"; + +/* No comment provided by engineer. */ +"Drag & Drop files" = "Drag & drop dei file"; + +/* Button title */ +"Enable" = "Abilita"; + +/* Text in dialog for certificate missing error */ +"Encryption requires a certificate" = "La crittografia richiede un certificato"; + +/* No comment provided by engineer. */ +"File not sent" = "File non inviato"; + +/* No comment provided by engineer. */ +"File sent (%@)" = "File inviato (%@)"; + +/* Title for control failure */ +"Host \\U201C%@\\U201D rejected control." = "Il sistema “%@” ha rifiutato il controllo."; + +/* Reason for trust failure */ +"Host not accepting any more trusted hosts." = "Il sistema non accetta ulteriori sistemi autorizzati."; + +/* Reason for control failure */ +"Host not controllable." = "Il sistema non è controllabile."; + +/* Reason for control failure */ +"Host not trusted." = "Il sistema non è autorizzato."; + +/* Explanation in dialog for certificate missing error */ +"In order to activate encryption, teleport needs a valid certificate, but couldn't find one in your Keychain.\nYou'll need to create a certificate and re-activate encryption. Note that the certificate algorithm and key size must match between the server and the clients.\n\nWould you like to launch Certificate Assistant to create a new self-signed certificate?\nRecommended settings: RSA 1024 bits, all others to default values." = "Per attivare la crittografia teleport ha bisogno di un certificato valido, ma non ha potuto trovarne uno nel tuo Portachiavi.\nHai bisogno di creare un certificato e riattivare la crittografia. Nota che l'algoritmo di certificazione e la dimensione della chiave devono corrispondere tra il server ed i clienti.\n\nVuoi lanciare l'Assistente per i Certificati per creare un nuovo certificato self-signed?\nImpostazioni raccomandate: RSA 1024 bit, tutti gli altri valori immutati."; + +/* Button title */ +"Launch Assistant" = "Avvia l'assistente"; + +/* No comment provided by engineer. */ +"No" = "No"; + +/* No comment provided by engineer. */ +"No shared Mac found on local network. Please check \\U201CShare this Mac\\U201D on the Macs you want to control." = "Nessun Mac condiviso è presente sulla rete locale. Per favore seleziona \\U201Condividi questo Mac\\U201D sui Mac che vuoi controllare."; + +/* No comment provided by engineer. */ +"OK" = "OK"; + +/* No comment provided by engineer. */ +"Pasteboard not sent" = "Appunti non inviati"; + +/* No comment provided by engineer. */ +"Pasteboard sent (%@)" = "Appunti inviati (%@)"; + +/* Button title */ +"Re-enable" = "Riabilita"; + +/* No comment provided by engineer. */ +"Reject" = "Rifiuta"; + +/* Replace button to replace an existing file. */ +"Replace" = "Sostituisci"; + +/* No comment provided by engineer. */ +"Screen %d - %.0fx%.0f" = "Schermo %1$d - %2$.0fx%3$.0f"; + +/* No comment provided by engineer. */ +"Screen to share" = "Schermo da condividere"; + +/* Informative text in certificate selection panel */ +"Select the certificate to be used for encryption. It must have the same encryption algorithm as the one used on the other Macs." = "Seleziona il certificato da usare per la condivisione. Deve usare lo stesso algoritmo di crittografia usato per gli altri Mac."; + +/* No comment provided by engineer. */ +"Share this Mac" = "Condividi questo Mac"; + +/* No comment provided by engineer. */ +"Synchronize pasteboard" = "Sincronizza gli appunti"; + +/* No comment provided by engineer. */ +"teleport %@ is now available.\n%@\n\nWould you like to download the new version now?" = "teleport %1$@ è ora disponibile.\n%2$@\n\nVuoi scaricare la nuova versione adesso?"; + +/* No comment provided by engineer. */ +"teleport is up-to-date." = "teleport è aggiornato."; + +/* Explanation in dialog for accessibility */ +"The access for assistive devices has been disabled although teleport needs this to operate.\n\nWould you like to re-enable it now or deactivate teleport?" = "L'accesso per i dispositivi di assistenza è disabilitato, ma teleport ne ha bisogno per funzionare.\n\nVuoi riabilitarlo adesso o disattivare teleport?"; + +/* No comment provided by engineer. */ +"The computer you tried to control rejected your request. You should try on a computer you own." = "Il computer che hai provato a controllare ha rifiutato la tua richiesta. Devi provare con un computer che puoi controllare."; + +/* Title for trust failure */ +"The host \\U201C%@\\U201D rejected your trust request." = "Il sistema “%@” ha rifiutato la tua richiesta di autorizzazione."; + +/* No comment provided by engineer. */ +"The host has probably removed this Mac from its trusted hosts list. Do you want to ask for trust again?" = "Il sistema ha probabilmente rimosso questo Mac dalla lista di quelli autorizzati. Vuoi chiedere ancora l'autorizzazione?"; + +/* Invalid protocol error message */ +"The Mac you tried to control is using teleport protocol v%d, but you're using v%d." = "Il Mac cha hai provato a controllare sta usando il protocollo teleport v%1$d, ma tu stai usando v%2$d."; + +/* No comment provided by engineer. */ +"The server may be down, or an encryption problem may have occured. If encryption is enabled, please check that the certificate algorithms match." = "Il server può essere inattivo, o ci può essere un problema di crittografia. Se la crittografia è abilitata, per favore controlla che gli algoritmi di crittografia corrispondano."; + +/* No comment provided by engineer. */ +"This will allow the demanding host to take control of your mouse and keyword. All keystrokes and transfers will be encrypted with this host.\nIf you didn't request the control, you should Reject it." = "Questo permetterà al sistema che invia la richiesta di controllare i tuoi mouse e tastiera. Tutti i tasti battuti ed i trasferimenti saranno crittografati con questo sistema.\nSe non hai richiesto il controllo, dovresti rifiutarlo."; + +/* No comment provided by engineer. */ +"This will allow the demanding host to take control of your mouse and keyword. Warning: the keystrokes and transfers will be sent clear on your network. You should enable encryption on both Macs to encrypt these.\nIf you didn't request the control, you should Reject it." = "Questo permetterà al sistema che invia la richiesta di controllare i tuoi mouse e tastiera. Attenzione: Tutti i tasti battuti ed i trasferimenti saranno effettuati in chiaro sulla tua rete. Dovresti abilitare la crittografia su entrambi i Mac per codificarli.\nSe non hai richiesto il controllo, dovresti rifiutarlo."; + +/* teleport trust request title - certified version */ +"Trust request from certified host \\U201C%@\\U201D." = "Richiesta di autorizzazione dal sistema certificato “%@”."; + +/* teleport trust request title - uncertified version */ +"Trust request from uncertified host \\U201C%@\\U201D." = "Richiesta di autorizzazione dal sistema non certificato “%@”."; + +/* Unique button in dialog, to make a unique name of an existing filename */ +"Unique" = "Unico"; + +/* Address of unknown trusted host */ +"unknown address" = "indirizzo sconosciuto"; + +/* Name of unknown trusted host */ +"unknown name" = "nome sconosciuto"; + +/* Name for unamed server */ +"unnamed" = "senza nome"; + +/* Reason for trust failure */ +"User rejected your trust request." = "L'utente ha rifiutato la tua richiesta di autorizzazione."; + +/* Invalid protocol error title */ +"Wrong protocol version" = "Versione del protocollo sbagliata"; + +/* No comment provided by engineer. */ +"Yes" = "Si"; + +/* No comment provided by engineer. */ +"You have the most recent version of teleport." = "Hai la versione più recente di teleport."; + +/* Explanation in dialog for accessibility */ +"You must enable access for assistive devices to activate teleport. You may need administrator privileges to do this.\n\nWould you like to enable this now?" = "Devi abilitare l'accesso per i dispositivi di assistenza per attivare teleport. Puoi aver bisogno dei privilegi di amministratore per farlo.\n\nVuoi abilitarlo adesso?"; + +/* Trust request message */ +"You should now grant the trust on your other Mac." = "Adesso dovresti dare l'autorizzazione anche all'altro Mac."; + +/* No comment provided by engineer. */ +"Your build (%d) is newer than the latest available (%d), so you must be a betatester. Congratulations!" = "Il tuo build (%1$d) è più recente dell'ultimo disponibile (%2$d), così devi essere un betatester. Congratulazioni!"; + +/* No comment provided by engineer. */ +"zero" = "zero"; diff --git a/Italian.lproj/MainMenuApp.nib/classes.nib b/Italian.lproj/MainMenuApp.nib/classes.nib new file mode 100644 index 0000000..8eab51a --- /dev/null +++ b/Italian.lproj/MainMenuApp.nib/classes.nib @@ -0,0 +1,38 @@ + + + + + IBClasses + + + CLASS + TPPrefPaneAppController + LANGUAGE + ObjC + OUTLETS + + window + NSWindow + + SUPERCLASS + NSObject + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Italian.lproj/MainMenuApp.nib/info.nib b/Italian.lproj/MainMenuApp.nib/info.nib new file mode 100644 index 0000000..21ddea8 --- /dev/null +++ b/Italian.lproj/MainMenuApp.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 629 + IBLastKnownRelativeProjectPath + ../teleport.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 265 + + IBSystem Version + 9A569 + targetFramework + IBCocoaFramework + + diff --git a/Italian.lproj/MainMenuApp.nib/keyedobjects.nib b/Italian.lproj/MainMenuApp.nib/keyedobjects.nib new file mode 100644 index 0000000..0d5ee09 Binary files /dev/null and b/Italian.lproj/MainMenuApp.nib/keyedobjects.nib differ diff --git a/Italian.lproj/teleportPref.xib b/Italian.lproj/teleportPref.xib new file mode 100644 index 0000000..9c0af06 --- /dev/null +++ b/Italian.lproj/teleportPref.xib @@ -0,0 +1,5888 @@ + + + + 1050 + 9C31 + 629 + 949.26 + 352.00 + + YES + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + TPPreferencePane + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{517, 450}, {595, 429}} + 1081606144 + PDwgZG8gbm90IGxvY2FsaXplID4+A + NSWindow + + View + + + + 256 + + YES + + + 274 + + YES + + + 268 + {{144, 387}, {158, 18}} + + + YES + + 604110336 + 0 + Condividi questo Mac + + LucidaGrande + 1.300000e+01 + 1044 + + + 1211912703 + 2 + + NSSwitch + + + + 200 + 25 + + + + + 265 + {{545, 379}, {32, 32}} + + + YES + + 67239424 + 134217728 + QWJvdXTigKY + + + 138690815 + 130 + + NSImage + icon-about + + + + + + 200 + 25 + + + + + 268 + {{18, 387}, {112, 18}} + + + YES + + 67239424 + 0 + Attiva teleport + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 274 + + YES + + + 256 + + YES + + + 274 + {{20, 17}, {511, 296}} + + + TPLayoutView + NSView + + + + 290 + {{17, 1}, {495, 14}} + + + YES + + 67239424 + 4325376 + Disponi i Mac condivisi in alto intorno al tuo schermo nella sezione centrale per controllarli. + + + 1.100000e+01 + 3100 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2OQA + + + + 6 + + controlTextColor + + 3 + MAA + + + + + + {{10, 33}, {549, 319}} + + + + + {{13, 10}, {569, 365}} + + + + YES + + layout + + Organizzazione + + + + + + + 256 + + YES + + + 301 + + YES + + + 292 + {{182, 87}, {341, 28}} + + YES + + 67239424 + 272760832 + Per rimuovere un sistema autorizzato, selezionalo e premi il tasto backspace. + + + + + + + + + -2147483380 + {{179, 250}, {169, 32}} + + YES + + 67239424 + 134217728 + U2NlZ2xpIGlsIGNlcnRpZmljYXRv4oCmA + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{179, 250}, {162, 32}} + + YES + + 67239424 + 134217728 + Mostra il certificato + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{183, 286}, {137, 18}} + + YES + + 67239424 + 0 + Abilita crittografia + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{23, 287}, {148, 17}} + + YES + + 67239424 + 71303168 + Crittografia: + + + + + + + + + 292 + {{185, 23}, {309, 58}} + + YES + 3 + 1 + + YES + + -2080244224 + 0 + Q2hpZWRpbWkgc2UgaWwgc2lzdGVtYSBwdcOyIGVzc2VyZSBhdXRvcml6emF0bw + + + 1211912703 + 0 + + NSRadioButton + + + + + + 200 + 25 + + + 67239424 + 0 + UmlmaXV0YSBzZSBpbCBzaXN0ZW1hIG5vbiDDqCBnacOgIGF1dG9yaXp6YXRvA + + 1 + + 1211912703 + 0 + + + + 200 + 25 + + + 67239424 + 0 + Accetta automaticamente + + 2 + + 1211912703 + 0 + + + + 400 + 75 + + + {309, 18} + {4, 2} + 1143480320 + NSActionCell + + 67239424 + 0 + Radio + + 1211912703 + 0 + + 400 + 75 + + + + + 3 + MQA + + + + + + 274 + + YES + + + 2304 + + YES + + + 256 + {333, 104} + + YES + + + 256 + {333, 17} + + + + + + 256 + {{-26, 0}, {16, 17}} + + + + YES + + name + 1.490000e+02 + 4.000000e+01 + 1.000000e+03 + + 75628032 + 0 + Nome + + + 3 + MC4zMzMzMzI5OQA + + + 6 + + headerTextColor + + + + + 337772096 + 133120 + Text Cell + + + + 6 + + controlBackgroundColor + + + + + 1 + YES + + + + address + 1.160000e+02 + 8.000000e+00 + 1.000000e+03 + + 75628032 + 0 + Indirizzo + + + + + + 337772096 + 133120 + + + + + + + 1 + YES + + + + certificate + 5.900000e+01 + 5.857568e+01 + 1.000000e+03 + + 67239424 + 0 + Certificato + + + 6 + + headerColor + + + + + + 67239424 + 134348800 + Vedi + + + -2034876161 + 36 + + + 400 + 75 + + + + + 3.000000e+00 + 2.000000e+00 + + + 6 + + gridColor + + 3 + MC41AA + + + 1.700000e+01 + 448823296 + 1 + 15 + 0 + YES + + + {{1, 17}, {333, 104}} + + + + + 4 + + + + 256 + {{-100, -100}, {15, 180}} + + + _doScroller: + 9.222222e-01 + + + + 256 + {{-100, -100}, {463, 15}} + + 1 + + + 9.904762e-01 + + + + 2304 + + YES + + + {{1, 0}, {333, 17}} + + + + + 4 + + + + {{185, 116}, {335, 122}} + + + 2 + + + + + + QSAAAEEgAABBmAAAQZgAAA + + + + 292 + {{14, 64}, {157, 17}} + + YES + + 67239424 + 71303168 + Richieste di controllo: + + + + + + + + + 268 + {{8, 221}, {163, 17}} + + YES + + 67239424 + 71303168 + Sistemi autorizzati: + + + + + + + + {549, 319} + + + + + {{10, 33}, {549, 319}} + + Impostazioni di sicurezza + + + + + options + + + 256 + + YES + + + 301 + + YES + + + 268 + {{169, 132}, {156, 18}} + + YES + + 67239424 + 0 + U29sbyBzZSBwacO5IHBpY2NvbG8gZGk + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{34, 164}, {95, 34}} + + YES + + 67239424 + 71303168 + Trasferimenti: + + + + + + + + + 268 + {{27, 17}, {102, 17}} + + YES + + 67239424 + 71303168 + Aggiornamenti: + + + + + + + + + 268 + {{72, 287}, {57, 17}} + + YES + + 67239424 + 71303168 + Cambio: + + + + + + + + + 268 + {{141, 74}, {251, 18}} + + YES + + 67239424 + 0 + Mostra lo stato nella barra del menu + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{141, 180}, {160, 18}} + + YES + + -2080244224 + 0 + RHJhZyAmIERyb3AgdHJhIGkgTWFjA + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{169, 108}, {188, 18}} + + YES + + 67239424 + 0 + U29sbyBzZSBpbCB0YXN0byDDqCBwcmVtdXRvOg + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{141, 156}, {167, 18}} + + YES + + -2080244224 + 0 + Sincronizza gli appunti + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{141, 286}, {239, 18}} + + YES + + 67239424 + 0 + Q2FtYmlhIHNvbG8gc2UgaWwgdGFzdG8gw6ggcHJlbXV0bzo + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{360, 103}, {141, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + 4oyYIENvbW1hbmQ + + 1048576 + 2147483647 + 1 + + + NSMenuCheckmark + + + + NSMenuMixedState + + _popUpItemAction: + 20 + + + YES + + + OtherViews + + + YES + + + + 4oylIE9wdGlvbg + + 1048576 + 2147483647 + + + _popUpItemAction: + 19 + + + + + 4oyDIENvbnRyb2w + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + 4oenIFNoaWZ0A + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + 4oeqIENhcHMgTG9jaw + + 1048576 + 2147483647 + + + _popUpItemAction: + 16 + + + + + 3 + YES + YES + 1 + + + + + 268 + {{315, 260}, {34, 17}} + + YES + + 67239424 + 272629760 + Corto + + + + + + + + + 268 + {{141, 214}, {213, 18}} + + YES + + 67239424 + 0 + Prova a svegliare i Mac in stop + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{141, 262}, {168, 18}} + + YES + + 67239424 + 0 + Cambia con un ritardo: + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{141, 238}, {341, 18}} + + YES + + 67239424 + 0 + Cambia quando premi due volte il tasto del mouse + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{331, 131}, {58, 22}} + + YES + + -1804468671 + 71304192 + + + + YES + + YES + allowsFloats + attributedStringForZero + decimalSeparator + formatterBehavior + groupingSeparator + locale + maximum + minimum + negativeFormat + positiveFormat + textAttributesForNegativeValues + usesGroupingSeparator + + + YES + + + 0 + + YES + + YES + + + YES + + + + wqA + + , + + + + + 0 + 2 + NO + YES + 2 + AABAAAAAAAAAAAAAAAAAAA + + + 0 + 0 + NO + NO + 2 + AAAAAAAAAAAAAAAAAAAAAA + + + 0K + + YES + + YES + + + YES + + + + + + + + + + + + + + + NaN + + + + + + + + NO + NO + YES + + dimensioni + + YES + + 6 + + textBackgroundColor + + + + 6 + + textColor + + + + + + + 268 + {{383, 281}, {141, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + 20 + + + YES + + + OtherViews + + + YES + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 19 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 16 + + + + + 3 + YES + YES + 1 + + + + + 268 + {{141, 50}, {264, 18}} + + YES + + 67239424 + 0 + Mostra il bezel quando controlli il Mac + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{389, 7}, {123, 32}} + + YES + + 67239424 + 134217728 + Controlla ora + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{88, 75}, {41, 17}} + + YES + + 67239424 + 71303168 + Stato: + + + + + + + + + 268 + {{353, 261}, {83, 15}} + + YES + + 67239424 + 131072 + + + + + Helvetica + 1.200000e+01 + 16 + + + 1.000000e+00 + 1.000000e-01 + 5.000000e-01 + 0.000000e+00 + 0 + 1 + NO + NO + + + + + 268 + {{441, 260}, {38, 17}} + + YES + + 67239424 + 272629760 + Lungo + + + + + + + + + 268 + {{141, 16}, {248, 18}} + + YES + + 67239424 + 0 + Q29udHJvbGxhIGF1dG9tYXRpY2FtZW50ZSBhbGwnYXZ2aW8 + + + 1211912703 + 2 + + + + 200 + 25 + + + + {549, 319} + + + + + {{10, 33}, {549, 319}} + + Opzioni + + + + + + + 0 + YES + YES + + + {595, 438} + + + + + + {595, 429} + + + + {{0, 0}, {1680, 1028}} + {224.664, 32} + {3.40282e+38, 3.40282e+38} + + + 1 + 2 + {{194, 375}, {295, 307}} + 1886912512 + + TPClosingWindow + + View + + + + 256 + + YES + + + 256 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + CorePasteboardFlavorType 0x504E4766 + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{20, 159}, {255, 128}} + + + YES + + 537001472 + 33554432 + + + teleport + + 0 + 0 + 0 + NO + + YES + + + + 256 + {{17, 129}, {261, 22}} + + + YES + + 67239424 + 138412032 + teleport + + LucidaGrande-Bold + 1.800000e+01 + 16 + + + + + + + + + 256 + {{17, 113}, {261, 14}} + + + YES + + 67239424 + 138412032 + PHZlcnNpb24+A + + + + + + + + + 256 + {{12, 43}, {274, 39}} + + + YES + + 67239424 + 4194304 + wqkgMjAwMy0yMDA4IGFieXNzb2Z0CkJhc2F0byBzdSB1bidpZGVhIGRpIERhdmUgU2FnIGUgSmVyZW15 +IFF1aW5uLgpSaW5ncmF6aWFtZW50aSBzcGVjaWFsaSBhIE1hbnUwMiwgSnVsaWVuIGFuZCBQYXVsLg + + + 1.000000e+01 + 2843 + + + + + + + + + 258 + {{12, 8}, {69, 17}} + + + YES + + 69336577 + 272629760 + 4p6kIHNpdG8gd2ViA + + + 1.100000e+01 + 16 + + http://teleport.abyssoft.com + + YES + + + + + + + 258 + {{119, 8}, {57, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIGZvcnVtA + + http://www.abyssoft.com/software/teleport/forums/ + + + + + + + + 258 + {{228, 8}, {58, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIGRvbmE + + aHR0cHM6Ly93d3cucGF5cGFsLmNvbS9jZ2ktYmluL3dlYnNjcj9jbWQ9X3hjbGljayZidXNpbmVzcz1q +dWwlNDBtYWMlMmVjb20maXRlbV9uYW1lPXRlbGVwb3J0Jm5vX3NoaXBwaW5nPTAmbm9fbm90ZT0wJnRh +eD0wJmN1cnJlbmN5X2NvZGU9VVNEJmNoYXJzZXQ9VVRGJTJkOCZjaGFyc2V0PVVURiUyZDg + + + + + + + {295, 307} + + + + {{0, 0}, {1680, 1028}} + {213, 129} + {3.40282e+38, 3.40282e+38} + + + + YES + prefs.allowControl + prefs.sharePasteboard + prefs.requireKey + prefs.delayedSwitch + prefs.showStatusItem + prefs.limitPasteboardSize + prefs.maxPasterboardSize + active + prefs.autocheckVersion + prefs.rejectAuthenticationRequests + prefs.enableEncryption + prefs.switchKeyTag + prefs.copyFiles + prefs.requirePasteboardKey + prefs.pasteboardKeyTag + prefs.trustRequestBehavior + prefs.wakeOnLAN + prefs.switchDelay + localHost.supportDragNDrop + prefs.maxPasteboardSize + prefs.hideControlBezel + prefs.switchWithDoubleTap + + YES + + + + + YES + numberOfSelectedRows + + NSTableView + + + + + + YES + + + _window + + + + 26 + + + + layoutView + + + + 145 + + + + allowControlCheckbox + + + + 156 + + + + aboutWindow + + + + 170 + + + + delegate + + + + 171 + + + + showAboutSheet: + + + + 173 + + + + activationCheckbox + + + + 175 + + + + versionTextField + + + + 183 + + + + + + + + 184 + + + + + + + + 200 + + + + view + + + + 202 + + + + sharePasteboardCheckbox + + + + 205 + + + + requireKeyCheckbox + + + + 206 + + + + _firstKeyView + + + + 232 + + + + _initialKeyView + + + + 233 + + + + _lastKeyView + + + + 234 + + + + dataSource + + + + 258 + + + + + + + + 259 + + + + trustedHostsTableView + + + + 260 + + + + statusCheckbox + + + + 266 + + + + delayedSwitchCheckbox + + + + 272 + + + + content + + + + 298 + + + + value: selection.prefs.allowControl + + + + + + + value + selection.prefs.allowControl + 2 + + + 320 + + + + value: selection.prefs.sharePasteboard + + + + + + + + selection.prefs.sharePasteboard + 2 + + + 321 + + + + value: selection.prefs.requireKey + + + + + + + + selection.prefs.requireKey + 2 + + + 322 + + + + value: selection.prefs.delayedSwitch + + + + + + + + selection.prefs.delayedSwitch + 2 + + + 323 + + + + value: selection.prefs.showStatusItem + + + + + + + + selection.prefs.showStatusItem + 2 + + + 324 + + + + value: selection.prefs.limitPasteboardSize + + + + + + + + selection.prefs.limitPasteboardSize + 2 + + + 329 + + + + enabled: selection.prefs.sharePasteboard + + + + + + + enabled + + 2 + + + 330 + + + + + + + + + + + + + 2 + + + 331 + + + + checkVersion: + + + + 337 + + + + value: selection.active + + + + + + + + selection.active + 2 + + + 342 + + + + enabled: selection.active + + + + + + + + + 2 + + + 343 + + + + + + + + + + + + + 2 + + + 344 + + + + enabled2: selection.active + + + + + + + enabled2 + + + YES + + YES + NSMultipleValuesPlaceholder + NSNoSelectionPlaceholder + NSNotApplicablePlaceholder + NSNullPlaceholder + + + YES + + + + + + + + 2 + + + 345 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 346 + + + + enabled3: selection.prefs.limitPasteboardSize + + + + + + + enabled3 + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 347 + + + + + + + + + + + + + 2 + + + 348 + + + + + + + + + + + + + 2 + + + 349 + + + + + + + + + + + + + 2 + + + 350 + + + + + + + + + + + + + 2 + + + 352 + + + + value: selection.prefs.autocheckVersion + + + + + + + + selection.prefs.autocheckVersion + 2 + + + 355 + + + + + + + + + + + + + 2 + + + 356 + + + + + + + + + + + + + 2 + + + 383 + + + + value: selection.prefs.enableEncryption + + + + + + + + selection.prefs.enableEncryption + 2 + + + 384 + + + + enabled: selection.prefs.enableEncryption + + + + + + + + + 2 + + + 386 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 387 + + + + enabled: selection.prefs.requireKey + + + + + + + + + 2 + + + 399 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 400 + + + + selectedTag: selection.prefs.switchKeyTag + + + + + + + selectedTag + selection.prefs.switchKeyTag + 2 + + + 401 + + + + + + + + + + + + + 2 + + + 403 + + + + value: selection.prefs.copyFiles + + + + + + + + selection.prefs.copyFiles + 2 + + + 405 + + + + + + + + 407 + + + + + + + + + + + + + 2 + + + 419 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 420 + + + + value: selection.prefs.requirePasteboardKey + + + + + + + + selection.prefs.requirePasteboardKey + 2 + + + 421 + + + + enabled: selection.prefs.requirePasteboardKey + + + + + + + + + 2 + + + 422 + + + + enabled2: selection.prefs.sharePasteboard + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 423 + + + + enabled3: selection.active + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 424 + + + + selectedTag: selection.prefs.pasteboardKeyTag + + + + + + + + selection.prefs.pasteboardKeyTag + 2 + + + 425 + + + + + + + + + + + + + 2 + + + 431 + + + + + + + + + + + + + 2 + + + 432 + + + + selectedTag: selection.prefs.trustRequestBehavior + + + + + + + + selection.prefs.trustRequestBehavior + 2 + + + 442 + + + + + + + + + + + + + 2 + + + 445 + + + + value: selection.prefs.wakeOnLAN + + + + + + + + selection.prefs.wakeOnLAN + 2 + + + 446 + + + + value: selection.prefs.switchDelay + + + + + + + + selection.prefs.switchDelay + 2 + + + 450 + + + + enabled: selection.prefs.delayedSwitch + + + + + + + + + 2 + + + 451 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 452 + + + + enabled2: selection.localHost.supportDragNDrop + + + + + + + + selection.localHost.supportDragNDrop + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 453 + + + + + + + + + + + + + 2 + + + 454 + + + + value: selection.prefs.maxPasteboardSize + + + + + + + + selection.prefs.maxPasteboardSize + 2 + + + 455 + + + + showCertificateButton + + + + 300281 + + + + chooseCertificateButton + + + + 300282 + + + + showCertificateViewer: + + + + 300283 + + + + showCertificateChooser: + + + + 300284 + + + + + + + + + + + + + 2 + + + 300293 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 300294 + + + + value: selection.prefs.hideControlBezel + + + + + + + + selection.prefs.hideControlBezel + + NSValueTransformerName + NSNegateBoolean + + 2 + + + 300300 + + + + + + + + + + + + + 2 + + + 300301 + + + + value: selection.prefs.switchWithDoubleTap + + + + + + + + selection.prefs.switchWithDoubleTap + 2 + + + 300305 + + + + + + + + + + + + + 2 + + + 300306 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + -3 + + + Application + + + 12 + + + YES + + + + PrefPane + + + 6 + + + YES + + + + + + 201 + + + YES + + + + + + + + + 136 + + + YES + + + + + + 172 + + + YES + + + + + + 174 + + + YES + + + + + + 186 + + + YES + + + + + + + + 187 + + + YES + + + + + + 189 + + + YES + + + + + + + 137 + + + + + 152 + + + YES + + + + + + 188 + + + YES + + + + + + 190 + + + YES + + + + + + 357 + + + YES + + + + + + 358 + + + YES + + + + + + 161 + + + YES + + + + About + + + 162 + + + YES + + + + + + + + + + + + 164 + + + YES + + + + + + 165 + + + YES + + + + + + 166 + + + YES + + + + + + 168 + + + YES + + + + + + 291 + + + YES + + + + + + 292 + + + YES + + + + + + 293 + + + YES + + + + + + 297 + + + PrefPaneController + + + 406 + + + TrustedHostsTable + + + 100136 + + + + + 100172 + + + + + 100174 + + + + + 100152 + + + + + 100164 + + + + + 100165 + + + + + 100166 + + + + + 100168 + + + + + 100291 + + + + + 100292 + + + + + 100293 + + + + + 300315 + + + YES + + + + + + + + + + + + + + 441 + + + YES + + + + + + 100441 + + + + + 300279 + + + YES + + + + + + 300280 + + + + + 385 + + + YES + + + + + + 100385 + + + + + 381 + + + YES + + + + + + 100381 + + + + + 300297 + + + YES + + + + + + 300298 + + + + + 434 + + + YES + + + + + + + + + 100434 + + + + + 439 + + + + + 438 + + + + + 437 + + + + + 246 + + + YES + + + + + + + + + 300246 + + + + + 200246 + + + + + 100246 + + + + + 247 + + + YES + + + + + + + + 360 + + + YES + + + + + + 249 + + + YES + + + + + + 248 + + + YES + + + + + + 100248 + + + + + 100249 + + + + + 361 + + + + + 440 + + + YES + + + + + + 100440 + + + + + 362 + + + YES + + + + + + 100362 + + + + + 300316 + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + 325 + + + YES + + + + + + 100325 + + + + + 363 + + + YES + + + + + + 100363 + + + + + 367 + + + YES + + + + + + 100367 + + + + + 366 + + + YES + + + + + + 100366 + + + + + 264 + + + YES + + + + + + 100264 + + + + + 402 + + + YES + + + + + + 100402 + + + + + 411 + + + YES + + + + + + 100411 + + + + + 191 + + + YES + + + + + + 100191 + + + + + 194 + + + YES + + + + + + 100194 + + + + + 412 + + + YES + + + + + + 100412 + + + YES + + + + + + 413 + + + YES + + + + + + + + + + 414 + + + + + 415 + + + + + 416 + + + + + 417 + + + + + 418 + + + + + 448 + + + YES + + + + + + 100448 + + + + + 443 + + + YES + + + + + + 100443 + + + + + 271 + + + YES + + + + + + 100271 + + + + + 300302 + + + YES + + + + + + 300303 + + + + + 326 + + + YES + + + + + + 100326 + + + YES + + + + + + 334 + + + + + 389 + + + YES + + + + + + 100389 + + + YES + + + + + + 390 + + + YES + + + + + + + + + + 391 + + + + + 392 + + + + + 393 + + + + + 394 + + + + + 395 + + + + + 300295 + + + YES + + + + + + 300296 + + + + + 336 + + + YES + + + + + + 100336 + + + + + 368 + + + YES + + + + + + 100368 + + + + + 447 + + + YES + + + + + + 100447 + + + + + 449 + + + YES + + + + + + 100449 + + + + + 353 + + + YES + + + + + + 100353 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + -3.ImportedFromIB2 + 100246.IBShouldRemoveOnLegacySave + 100248.IBShouldRemoveOnLegacySave + 100249.IBShouldRemoveOnLegacySave + 12.IBPluginDependency + 12.IBWindowTemplateEditedContentRect + 12.ImportedFromIB2 + 12.editorWindowContentRectSynchronizationRect + 12.windowTemplate.hasMaxSize + 12.windowTemplate.hasMinSize + 12.windowTemplate.maxSize + 12.windowTemplate.minSize + 136.IBAttributePlaceholdersKey + 136.IBPluginDependency + 136.ImportedFromIB2 + 137.IBAttributePlaceholdersKey + 137.IBPluginDependency + 137.ImportedFromIB2 + 152.IBPluginDependency + 152.ImportedFromIB2 + 161.IBPluginDependency + 161.IBWindowTemplateEditedContentRect + 161.ImportedFromIB2 + 161.editorWindowContentRectSynchronizationRect + 161.windowTemplate.hasMaxSize + 161.windowTemplate.hasMinSize + 161.windowTemplate.maxSize + 161.windowTemplate.minSize + 162.IBPluginDependency + 162.ImportedFromIB2 + 164.IBPluginDependency + 164.ImportedFromIB2 + 165.IBPluginDependency + 165.ImportedFromIB2 + 166.CustomClassName + 166.IBPluginDependency + 166.ImportedFromIB2 + 168.IBPluginDependency + 168.ImportedFromIB2 + 172.IBAttributePlaceholdersKey + 172.IBPluginDependency + 172.ImportedFromIB2 + 174.IBAttributePlaceholdersKey + 174.IBPluginDependency + 174.ImportedFromIB2 + 186.IBPluginDependency + 186.ImportedFromIB2 + 187.IBPluginDependency + 187.ImportedFromIB2 + 188.IBPluginDependency + 188.ImportedFromIB2 + 189.IBPluginDependency + 189.ImportedFromIB2 + 190.IBPluginDependency + 190.ImportedFromIB2 + 191.IBPluginDependency + 191.ImportedFromIB2 + 194.IBPluginDependency + 194.ImportedFromIB2 + 200246.IBShouldRemoveOnLegacySave + 201.IBPluginDependency + 201.ImportedFromIB2 + 246.IBPluginDependency + 246.ImportedFromIB2 + 247.CustomClassName + 247.IBPluginDependency + 247.ImportedFromIB2 + 248.IBPluginDependency + 248.ImportedFromIB2 + 249.IBPluginDependency + 249.ImportedFromIB2 + 264.IBPluginDependency + 264.ImportedFromIB2 + 271.IBPluginDependency + 271.ImportedFromIB2 + 291.CustomClassName + 291.IBPluginDependency + 291.ImportedFromIB2 + 292.CustomClassName + 292.IBPluginDependency + 292.ImportedFromIB2 + 293.CustomClassName + 293.IBPluginDependency + 293.ImportedFromIB2 + 297.IBPluginDependency + 297.ImportedFromIB2 + 300246.IBShouldRemoveOnLegacySave + 300279.IBPluginDependency + 300279.ImportedFromIB2 + 300295.IBPluginDependency + 300295.ImportedFromIB2 + 300297.IBPluginDependency + 300297.ImportedFromIB2 + 300302.IBAttributePlaceholdersKey + 300302.IBPluginDependency + 300302.ImportedFromIB2 + 325.IBPluginDependency + 325.ImportedFromIB2 + 326.IBPluginDependency + 326.ImportedFromIB2 + 334.IBPluginDependency + 334.ImportedFromIB2 + 336.IBPluginDependency + 336.ImportedFromIB2 + 353.IBPluginDependency + 353.ImportedFromIB2 + 357.IBPluginDependency + 357.ImportedFromIB2 + 358.IBPluginDependency + 358.ImportedFromIB2 + 360.IBPluginDependency + 360.ImportedFromIB2 + 361.IBPluginDependency + 361.ImportedFromIB2 + 362.IBPluginDependency + 362.ImportedFromIB2 + 363.IBPluginDependency + 363.ImportedFromIB2 + 366.IBPluginDependency + 366.ImportedFromIB2 + 367.IBPluginDependency + 367.ImportedFromIB2 + 368.IBPluginDependency + 368.ImportedFromIB2 + 381.IBPluginDependency + 381.ImportedFromIB2 + 385.IBPluginDependency + 385.ImportedFromIB2 + 389.IBPluginDependency + 389.ImportedFromIB2 + 390.IBPluginDependency + 390.ImportedFromIB2 + 390.editorWindowContentRectSynchronizationRect + 391.IBPluginDependency + 391.ImportedFromIB2 + 392.IBPluginDependency + 392.ImportedFromIB2 + 393.IBPluginDependency + 393.ImportedFromIB2 + 394.IBPluginDependency + 394.ImportedFromIB2 + 395.IBPluginDependency + 395.ImportedFromIB2 + 402.IBPluginDependency + 402.ImportedFromIB2 + 406.IBPluginDependency + 406.ImportedFromIB2 + 411.IBPluginDependency + 411.ImportedFromIB2 + 412.IBPluginDependency + 412.ImportedFromIB2 + 413.IBPluginDependency + 413.ImportedFromIB2 + 413.editorWindowContentRectSynchronizationRect + 414.IBPluginDependency + 414.ImportedFromIB2 + 415.IBPluginDependency + 415.ImportedFromIB2 + 416.IBPluginDependency + 416.ImportedFromIB2 + 417.IBPluginDependency + 417.ImportedFromIB2 + 418.IBPluginDependency + 418.ImportedFromIB2 + 434.IBPluginDependency + 434.ImportedFromIB2 + 437.IBPluginDependency + 437.ImportedFromIB2 + 438.IBPluginDependency + 438.ImportedFromIB2 + 439.IBPluginDependency + 439.ImportedFromIB2 + 440.IBPluginDependency + 440.ImportedFromIB2 + 441.IBPluginDependency + 441.ImportedFromIB2 + 443.IBAttributePlaceholdersKey + 443.IBPluginDependency + 443.ImportedFromIB2 + 447.IBPluginDependency + 447.ImportedFromIB2 + 448.IBPluginDependency + 448.ImportedFromIB2 + 449.IBPluginDependency + 449.ImportedFromIB2 + 6.IBPluginDependency + 6.ImportedFromIB2 + + + YES + + + + + + + + + {{166, 304}, {595, 429}} + + + + + {3.40282e+38, 3.40282e+38} + {224.664, 10} + + ToolTip + + + + Seleziona per permettere ad altri utenti di teleport di controllare il tuo Mac. + + + + + + + + + + UXVlc3RhIMOoIGwnYXJlYSBwZXIgbCdvcmdhbml6emF6aW9uZS4gVHJhc2NpbmEgaSBzaXN0ZW1pIGNv +bnRyb2xsYXRpIGRvdmUgdnVvaS4 + + + + + + + + {{194, 375}, {295, 307}} + + + + + + {213, 107} + + + + + + + TPVersionTextField + + + + + + + + + + QWJvdXQgdGVsZXBvcnTigKY + + + + + + + + + + Seleziona per attivare teleport. + + + + + + + + + + + + + + + + + + + + + + + + TPTableView + + + + + + + + + + + TPLinkTextField + + + + + + + + + + + + + + + + + + + + + + + TGEgZG9wcGlhIHNlbGV6aW9uZSDDqCBxdWFuZG8gcHJlbWkgaWwgdGFzdG8gZGVsIG1vdXNlIHN1bCBi +b3Jkbywgbm9uIHRpIHNwb3N0aSBlIGxvIHByZW1pIGFuY29yYS4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{876, 490}, {157, 103}} + + + + + + + + + + + + + + + + + + + + + {{847, 309}, {157, 103}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + U29sbyBjb24gc2lzdGVtaSBjb2xsZWdhdGkgdmlhIEV0aGVybmV0IGUgY29uIGwnb3B6aW9uZSAiUmlh +dHRpdmEgcGVyIGwnYWNjZXNzbyBkZWxsJ2FtbWluaXN0cmF0b3JlIGRlbCBuZXR3b3JrIEV0aGVybmV0 +IiBhYmlsaXRhdGEgbmVsbGUgcHJlZmVyZW56ZSBSaXNwYXJtaW8gZW5lcmdpYS4 + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 300316 + + + + YES + + + NSTextField + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBUserSource + + + + + TPVersionTextField + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBProjectSource + TPVersionTextField.h + + + + TPLinkTextField + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPLinkTextField.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + TPClosingWindow + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPClosingWindow.h + + + + + + + YES + + YES + + + YES + + + + + id + + + + + + + + TPLayoutView + + + YES + + YES + scroll: + setScaleFactor: + + + YES + id + + + + + + + + + + TPLayoutView.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + NSPreferencePane + NSObject + + YES + + YES + addOther: + changeTabPanel: + + closeAddOther: + closeTrustedCertificate: + confirmAddOther: + deleteTrustedHost: + + showCertificate: + showTrustedHosts: + + + YES + + + + + + + + + + + + + + YES + + YES + + + + + + + addOtherAddressField + addOtherHeightField + addOtherNameField + addOtherSheet + addOtherWidthField + + certificateView + certificateWindow + + + + + + + + + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPPreferencesManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSecureSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPDaemonManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTransfersManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection_Private.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPUDPSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPConnectionsManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTableView.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPHotBorder.h + + + + TPTableView + NSTableView + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTableView.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + : + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + TPPreferencePane + NSPreferencePane + + YES + + YES + addOther: + checkVersion: + closeAddOther: + confirmAddOther: + deleteTrustedHost: + showAboutSheet: + showCertificateChooser: + showCertificateViewer: + + + YES + + + + + + + + + + + + YES + + YES + aboutWindow + activationCheckbox + allowControlCheckbox + chooseCertificateButton + delayedSwitchCheckbox + layoutView + requireKeyCheckbox + sharePasteboardCheckbox + showCertificateButton + statusCheckbox + trustedHostsTableView + versionTextField + + + YES + + + + + + TPLayoutView + + + + + + + + + + + TPPreferencePane.h + + + + + 0 + ../../teleport.xcodeproj + 3 + + YnBsaXN0MDDUAAEAAgADAAQABQAGAAkAClgkdmVyc2lvblQkdG9wWSRhcmNoaXZlclgkb2JqZWN0cxIA +AYag0QAHAAhdSUIub2JqZWN0ZGF0YYABXxAPTlNLZXllZEFyY2hpdmVyrxEEBwALAAwAMQA1ADYAPAA9 +AEEARQCfAKcAtwDCAAsAwwDdAN4A5gDnAOoA7gDvAPIA8wD3AP0A/gECAQcBDwEWAR8BIAEtAS4BMwE0 +ATUBOAE7AT8BRwFIAVABUQFiAWYBbQFyAXsBfAD7AX0BgAGJAYoBlAGVAZoBpAGlAaYBqwGtAbIBswG2 +AbkBvAHAAcEBwwHEAcoB0wHUAdUB2AHeAeMB5wHvAfsCAwIEAgwCDQIVAhYCHwIgAiICKQIqAjECMgI0 +AjsCPAJEAkUCTAJNAlUCVgJwAnECdwKAAoEChAKFAocCkAKRApsCnAKdAPYCngKjAqQCpwKqAr8CxwLV +AtkC9wL4AvkC+gMAAwkDCgMNAxIDEwMWAxwDLQMuAzUDNgM5Az4DPwNCA0sDTANRA1IDVQNeA18DZQNm +A20DdQN2A3wDfQOCA4MDjQOOA5MDlAOXA5oDmwOeA6kDqgOrA64DtgO3A7sDvAO9A8ADxwPIA88D0APX +A9gD3wPgAcAD4QPiA+kD6gPvA/MD+gQTBBoEGwQjBCQEKwQsBDMENAQ7BDwEQwREBEsETARTBFQEWwRc +BGQEZQRsBG0EdQR2BH0EfgSGBIcEjgSPBJcEmASfBKAEqASpBLAEsQTGBMgE2wTgBOEE5QTmBOoE6wTs +BO4E8QT5BQME6wUEBQ4E6wUPBRkE6wUaBSQE6wUlBScFKwUuBTUFNgU+BT8FRgVHBU8FUAVXBVgFYAVh +BWgFaQVxBXIFeQV6BYcFqQXGBccFyAXJBcoFywXMBc0FzgXPBdAF0QXSBd0F4AXhBeYF5wXrBe4F8QX1 +BfYF9wX7AhkF/gDbBgMGBAYHAPIGCgYOBg8GEgYTBhgGGQYeBh8GJgYnBjIGNAY9BOsGQQZDBksE6wZU +BOsGXQTrBmYE6wZvBnYGdwZ/BoAGhwaIBo8GkAaSBpkGmgahBqIGqQaqBr4GwAbEBsUGyAbLBtIG0wba +BtsG4gbjBusD4AHABuwG7QbvBvAG8Qb2BvcG+gb/BwAHBQcGBwsHDAcRBycHKACtBykHLAcwAAsHMQcy +BzMHNAc3BzgHPQc+B0MHSwdkB2UHZgdnB2gHaQdqB2sHbAdtB24HbwdwB3EHcgdzB3QHdQd2B3cHeAd5 +B3oHfAd+B4IHgweIB4kHjgeTB5QHmQejAbsHpAerAAsHrAe0B7UHtge7B7wHwQfCB8cHyAfNB84H0wfU +B9kH3wfjAvgH5AfmB+sH7AfxB/wH/Qf+CAAICggVCB8IIAghCCIIIwgkCCUIJggnCDEINQg2CDkIPAhE +CEUINQhMCFAIUQhZCFoIYghjCGgIcwh0CHUIfwiACIMIhAiOCI8IlwiYCJkIowikCKwIrQiuAAsHMQiv +BzMIsAixCLYItwi8CL0IwgjDCMgIzQjOCNMI1AjZCOMI5AjlCOYI6QjwCPEI8gjzCP4I/wkACQEJDQkO +CQ8JEAkRCRIJGQkaCSEJIgkpCTAJMQkyCTkJOgk7CUQJUAlXCV4JZQlmCWcJbgl1CX4JigmRCZgJnwmm +Ca0JtAm1CbYJvQm+Cb8JyAnUCdsJ5AnwCfcKAAoBCgIKDgoVChwKHQoeCiUKJgonCigKLwowCjEKOAo5 +CjoKQQpCCkMKTApNClkKYgpjCmQKcAp3CngKfwqACoEKiAqPCpAKkQqYCpkKoAqhCqIKqQqyCr4KxwrI +CtQK2wrcCt0K5ArrCuwK8wr0Cv0LCQsQCxELEgsZCxoLIQspCyoLKwsxCzILMws6CzsLPAs/C0QLRQtK +C0sLUAtRC1YLVwtcC10L3wviC+ML5QxnDOoNbQ1uDW8NcA1xDXINcw10DXUNdg13DXgNeQ16DXsHKw18 +DX0Nfg1/DYANgQ2CDYMNhA2FDYYNhw2IDYkNig2LDYwNjQ2ODY8NkA2RDZINkw2UDZUNlg2XDZgNmQ2a +DZsNnA2dDZ4Nnw2gDaENog2jDaQNpQ2mDacNqA2pDaoNqw2sDa0Nrg2vDbANsQ2yDbMNtA21DbYNtw24 +DbkNug27DbwNvQ2+Db8NwA3BDcINww3EDcUNxg3HDcgNyQ3KDcsNzA3NDc4Nzw3QDdEN0g3TDdQN1Q3W +DdcN2A3ZDdoN2w3cDd0N3g3fDeAN4Q3iDeMN5A3lDeYN5w3oDekN6g3rDewN9A38DtYPsA+xD7IPsw+0 +D7UPtg+3D7gPuQ+6D7sCAA+8D70Pvg+/D8APwQ/CD8MPxA/FD8YPxw/ID8kPyg/LD8wPzQ/OD88P0A/R +D9IP0w/UD9UP1g/XCG4P2A/ZD9oP2w/cD90P3g/fD+AP4Q/iD+MP5A/lD+YP5w/oD+kP6g/rD+wP7QDa +D+4P7w/wD/EP8g/zD/QP9Q/2D/cP+A/5D/oP+w/8D/0P/g//EAAQARACEAMQBBAFEAYQBxAIEAkQChAL +EAwQDRAOEA8QEBAREBIQExAUEBUQFhAXEBgQGRAaEBsQHBAdEB4QHxAgECEQIhAjECQQJRAmECcQKBAp +ECoQKxAsEC0QLhAvEDAQMRAyEDMQNBA1EDYQNxA4EDkQOhA7EDwQPRA+ED8QQBBBEEIQQxBEEEUQRhBH +EEgQSRBKEEsQTBBNEE4BoRBPEFAQURBSEFMQVBBVEFYQVxBYEFkQWhBbEFwQXRBeEF8QYBBhEGIQYxBk +EGUQZhBnEGgQaRBqEGsQbBBtApoQbhBvEHAQcRByEHMQdBB1EHYQdxB4EHkQehB7EHwQfRB+EH8QgBCB +EIIQhRCIEItVJG51bGzfEBIADQAOAA8AEAARABIAEwAUABUAFgAXABgAGQAaABsAHAAdAB4AHwAgACEA +IgAjACQAJQAmACcAKAApACoAKwAsAC0ALgAvADBWTlNSb290ViRjbGFzc11OU09iamVjdHNLZXlzXxAP +TlNDbGFzc2VzVmFsdWVzXxAZTlNBY2Nlc3NpYmlsaXR5T2lkc1ZhbHVlc11OU0Nvbm5lY3Rpb25zW05T +TmFtZXNLZXlzW05TRnJhbWV3b3JrXU5TQ2xhc3Nlc0tleXNaTlNPaWRzS2V5c11OU05hbWVzVmFsdWVz +XxAZTlNBY2Nlc3NpYmlsaXR5Q29ubmVjdG9yc11OU0ZvbnRNYW5hZ2VyXxAQTlNWaXNpYmxlV2luZG93 +c18QD05TT2JqZWN0c1ZhbHVlc18QF05TQWNjZXNzaWJpbGl0eU9pZHNLZXlzWU5TTmV4dE9pZFxOU09p +ZHNWYWx1ZXOAAoEEBoECoYEDKYEEBYAIgQKmgAWBAyiBAyqBAqeBBAOAAIAGgQKlgQQEEgAElSSBAyvS +AA4AMgAzADRbTlNDbGFzc05hbWWABIADXxAQVFBQcmVmZXJlbmNlUGFuZdIANwA4ADkAOlgkY2xhc3Nl +c1okY2xhc3NuYW1logA6ADteTlNDdXN0b21PYmplY3RYTlNPYmplY3RfEBBJQkNvY29hRnJhbWV3b3Jr +0gAOAD4APwBAWk5TLm9iamVjdHOAB6DSADcAOABCAEOjAEMARAA7XE5TTXV0YWJsZVNldFVOU1NldNIA +DgA+AEYAR4BErxBXAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0AXgBf +AGAAYQBiAGMAZABlAGYAZwBoAGkAagBrAGwAbQBuAG8AcABxAHIAcwB0AHUAdgB3AHgAeQB6AHsAfAB9 +AH4AfwCAAIEAggCDAIQAhQCGAIcAiACJAIoAiwCMAI0AjgCPAJAAkQCSAJMAlACVAJYAlwCYAJkAmgCb +AJwAnQCegAmAG4EBeYEBfIEBfoEBgIEBgoEBkIEBkoEBr4EBsYEBsoEBtIEBvoEBwIEBwoEBxIEBxoEB +yIEBzoEB0IECB4ECCYECC4ECDYECDoECEIECEoECE4ECGIECHIECJoECKIECKoECK4ECLoECMYECM4EC +NIECNYECOIECOYECOoECPIECPYECPoECP4ECQIECQYECRIECR4ECSYECSoECTIECTYECUYECUoECVYEC +WYECXIECX4ECYoECZYECaYECa4ECboECb4ECcoECdIECd4ECeIECeoECfYECgIECgYECg4EChYECh4EC +ioECjIECjYECk4ECl4ECmYECm4ECnYECn9QADgCgAKEAogCjAKQAHwCmXU5TRGVzdGluYXRpb25YTlNT +b3VyY2VXTlNMYWJlbIAagAqAAoAZ2ACoAA4AqQCqAKsArACtAK4ArwCwALEAsgCzALQAtQCvXxAPTlNO +ZXh0UmVzcG9uZGVyV05TRnJhbWVWTlNDZWxsWE5TdkZsYWdzWU5TRW5hYmxlZFhOU1dpbmRvd1tOU1N1 +cGVydmlld4ALgBiADIAOEQEMCYANgAvYAKgADgC4AKsAuQCtADIArgC6ALsAvAC9AL4AtQDAALpaTlNT +dWJ2aWV3c1tOU0ZyYW1lU2l6ZYAcgDSAHREBEoEBd4ANgDOAHF8QF3t7MTQ0LCAzODd9LCB7MTU4LCAx +OH193QDEAA4AxQDGAMcAyADJAMoAywDMAM0AzgDPANAA0QDSANMA1ADVANIA1wDYAKQA2gDbANxbTlND +ZWxsRmxhZ3NfEBNOU0FsdGVybmF0ZUNvbnRlbnRzXxASTlNQZXJpb2RpY0ludGVydmFsXk5TQnV0dG9u +RmxhZ3MyXxAQTlNBbHRlcm5hdGVJbWFnZV8QD05TS2V5RXF1aXZhbGVudFpOU0NvbnRlbnRzWU5TU3Vw +cG9ydF1OU0NvbnRyb2xWaWV3XxAPTlNQZXJpb2RpY0RlbGF5XE5TQ2VsbEZsYWdzMl1OU0J1dHRvbkZs +YWdzEiQB/gCAF4AWEBkQAoATgBaAD4AQgAoQyBAAEkg8Uf9fEBRDb25kaXZpZGkgcXVlc3RvIE1hY9QA +DgDfAOAA4QDiAOMA5ADlVk5TU2l6ZVZOU05hbWVYTlNmRmxhZ3OAEiNAKgAAAAAAAIAREQQUXEx1Y2lk +YUdyYW5kZdIANwA4AOgA6aIA6QA7Vk5TRm9udNIADgDrAOwA7VtOU0ltYWdlTmFtZYAVgBRYTlNTd2l0 +Y2jSADcAOADwAPGiAPEAO18QE05TQnV0dG9uSW1hZ2VTb3VyY2VQ0gA3ADgA9AD1pAD1APYAqgA7XE5T +QnV0dG9uQ2VsbFxOU0FjdGlvbkNlbGzSADcAOAD4APmlAPkA+gD7APwAO1hOU0J1dHRvbllOU0NvbnRy +b2xWTlNWaWV3W05TUmVzcG9uZGVyXxAUYWxsb3dDb250cm9sQ2hlY2tib3jSADcAOAD/AQCjAQABAQA7 +XxAUTlNOaWJPdXRsZXRDb25uZWN0b3JeTlNOaWJDb25uZWN0b3LUAA4AoAChAKIAowCvAB8BBoAagAuA +AoEBeNcAqAAOALgAqwC5AK0ArgC1AQkBCgELAQwAtQEOgA2ARoEBiBEBAIEBiYANgQGK0gAOAD4ARgER +gESkAKQBEwEUARWACoAegCiALNgAqAAOAKkAqgCrAKwArQCuAK8AsAEZARoBGwC0ALUAr4ALgBiAH4Ag +EQEJCYANgAtfEBZ7ezU0NSwgMzc5fSwgezMyLCAzMn193QDEAA4AxQEhAMYAxwDJAMoAywDMAM0AzgDP +ASIA0QDSASUA0wEmAScBKADYARMA2gErASxdTlNOb3JtYWxJbWFnZRIEAf4AgBeAFoAiEIKAJoAhgBCA +HhIIAAAAEghEQP9mAEEAYgBvAHUAdCAm0wAOADIBLwEwATEBMl5OU1Jlc291cmNlTmFtZYAlgCOAJFdO +U0ltYWdlWmljb24tYWJvdXTSADcAOAE2ATeiATcAO18QEE5TQ3VzdG9tUmVzb3VyY2XSAA4BOQE6APJZ +TlMuc3RyaW5ngCfSADcAOAE8AT2jAT0BPgA7XxAPTlNNdXRhYmxlU3RyaW5nWE5TU3RyaW5n2ACoAA4A +qQCqAKsArACtAK4ArwCwAUIBQwCzALQAtQCvgAuAGIApgCoJgA2AC18QFnt7MTgsIDM4N30sIHsxMTIs +IDE4fX3dAMQADgDFAMYAxwDIAMkAygDLAMwAzQDOAM8BIgDRANIA0wDUANUA0gFNANgBFADaANsA3IAX +gBaAE4AWgCuAEIAoXxAPQXR0aXZhIHRlbGVwb3J03QCoAA4BUgFTAKkA6QC4AKsArQFUAK4BVQFWAK8B +WAFZANsBWgDYAVwAvQC1ALQArwC0AWFeTlNUYWJWaWV3SXRlbXNZTlNUdkZsYWdzXxARTlNEcmF3c0Jh +Y2tncm91bmRfEBZOU0FsbG93VHJ1bmNhdGVkTGFiZWxzXxAVTlNTZWxlY3RlZFRhYlZpZXdJdGVtgAuB +AXaASIBHgBCALYANCYALCYBJ0gAOAD4ARgFkgEShAWWALtcAqAAOAKkAuACrAK0ArgEVAQkBaQFqAQsA +tQEVgCyARoBFgC+ADYAs0gAOAD4ARgFvgESiAXABcYAwgDXYAKgADgCpAKsArQAyAXMArgFlALsBdgC9 +ALUBeADAAWVbTlNFeHRlbnNpb26ALoA0gDGADYAygDOALl8QFnt7MjAsIDE3fSwgezUxMSwgMjk2fX1c +VFBMYXlvdXRWaWV30gA3ADgBfgF/pAF/APsA/AA7XE5TQ3VzdG9tVmlld9gAqAAOAKkAqgCrAKwArQCu +AWUBggGDAYQBhQC0ALUBZYAugEOANoA3EQEiCYANgC5fEBR7ezE3LCAxfSwgezQ5NSwgMTR9fdgAxAAO +AYsAygDLAMwAzgGMASIBjQGOAY8BkAFxAZIBk18QEU5TQmFja2dyb3VuZENvbG9yW05TVGV4dENvbG9y +gEKAOoA4gDmANRIAQgAAgD9fEF9EaXNwb25pIGkgTWFjIGNvbmRpdmlzaSBpbiBhbHRvIGludG9ybm8g +YWwgdHVvIHNjaGVybW8gbmVsbGEgc2V6aW9uZSBjZW50cmFsZSBwZXIgY29udHJvbGxhcmxpLtQADgDf +AOAA4QDiAZcA5AGZgBIjQCYAAAAAAACAEREMHNUADgGbAZwBnQGeAZ8BoAGhAaIBo1dOU0NvbG9yXE5T +Q29sb3JTcGFjZVtOU0NvbG9yTmFtZV1OU0NhdGFsb2dOYW1lgD6APRAGgDyAO1ZTeXN0ZW1cY29udHJv +bENvbG9y0wAOAZwBpwGfAakBqldOU1doaXRlgD4QA0swLjY2NjY2NjY5ANIANwA4AawBm6IBmwA71QAO +AZsBnAGdAZ4BnwGvAaEBsAGjgD6AQYBAgDtfEBBjb250cm9sVGV4dENvbG9y0wAOAZwBpwGfAakBtYA+ +QjAA0gA3ADgBtwG4pAG4APYAqgA7XxAPTlNUZXh0RmllbGRDZWxs0gA3ADgBugG7pQG7APoA+wD8ADtb +TlNUZXh0RmllbGTSADcAOAG9Ab6jAb4BvwA7Xk5TTXV0YWJsZUFycmF5V05TQXJyYXlfEBZ7ezEwLCAz +M30sIHs1NDksIDMxOX190gA3ADgBwgD7owD7APwAO18QFnt7MTMsIDEwfSwgezU2OSwgMzY1fX3SAA4A +PgBGAcaARKMBYQHIAcmASYBNgMDWAA4BywD7AcwBmwCiAc0BzgFlARUBjgHSXE5TSWRlbnRpZmllcllO +U1RhYlZpZXeATIBKgC6ALIA6gEtWbGF5b3V0Xk9yZ2FuaXp6YXppb25l0gA3ADgB1gHXogHXADtdTlNU +YWJWaWV3SXRlbdUADgD7AcwBmwCiAc0B2gEVAY4B3YBMgE6ALIA6gL/VAKgADgCpALgAqwArAQkB4QHi +AQuAAIBGgL6AT9IADgA+AEYB5YBEoQHmgFDXAKgADgC4AKsAuQAyAK4B2gC7AeoB6wHsAMAB2oBOgDSA +UREBLYC9gDOATtIADgA+AEYB8YBEqQHyAfMB9AH1AfYB9wH4AfkB+oBSgFaAW4BggGSAaIB7gLWAudcA +qAAOAKkAqgCrAKwArgHmAYIB/gH/AgAAtAHmgFCAQ4BTgFQRASQJgFBfEBZ7ezE4MiwgODd9LCB7MzQx +LCAyOH192ADEAA4BiwDKAMsAzADOAYwBIgGNAY4CBwGQAfICCgGTgEKAOoBVgDmAUhIQQgAAgD9fEE1Q +ZXIgcmltdW92ZXJlIHVuIHNpc3RlbWEgYXV0b3JpenphdG8sIHNlbGV6aW9uYWxvIGUgcHJlbWkgaWwg +dGFzdG8gYmFja3NwYWNlLtcAqAAOAKkAqgCrAKwArgHmALACEAIRAhIAtAHmgFCAGIBXgFgT/////4AA +AQwJgFBfEBd7ezE3OSwgMjUwfSwgezE2OSwgMzJ9fdwAxAAOAMUAxgDHAMkAygDLAMwAzQDOAM8BIgDR +ANIA0wIZAhoCGwDYAfMA2gErAh6AF4AWEAGAWoBZgBCAVhP/////hoJA/28QFgBTAGMAZQBnAGwAaQAg +AGkAbAAgAGMAZQByAHQAaQBmAGkAYwBhAHQAbyAm0gAOATkBOgDygCfXAKgADgCpAKoAqwCsAK4B5gCw +AiUCJgCzALQB5oBQgBiAXIBdCYBQXxAXe3sxNzksIDI1MH0sIHsxNjIsIDMyfX3cAMQADgDFAMYAxwDJ +AMoAywDMAM0AzgDPASIA0QDSANMCGQItAi4A2AH0ANoBKwIegBeAFoBfgF6AEIBbXxAVTW9zdHJhIGls +IGNlcnRpZmljYXRv0gAOATkBOgDygCfXAKgADgCpAKoAqwCsAK4B5gCwAjcCOACzALQB5oBQgBiAYYBi +CYBQXxAXe3sxODMsIDI4Nn0sIHsxMzcsIDE4fX3dAMQADgDFAMYAxwDIAMkAygDLAMwAzQDOAM8BIgDR +ANIA0wDUANUA0gJBANgB9QDaANsA3IAXgBaAE4AWgGOAEIBgXxAUQWJpbGl0YSBjcml0dG9ncmFmaWHX +AKgADgCpAKoAqwCsAK4B5gGCAkgCSQCzALQB5oBQgEOAZYBmCYBQXxAWe3syMywgMjg3fSwgezE0OCwg +MTd9fdgAxAAOAYsAygDLAMwAzgGMASIBjQGOAlAA2AH2AlMBk4BCgDqAZ4AQgGQSBEAAAIA/XUNyaXR0 +b2dyYWZpYTrfEBIAqAAOAKkCVwJYAlkBiwJaAlsA6QJcAKsArAJdAK4CXgJfAmAB5gJiAmMCZAGpAmUB +jgJnAmgA2AJqAgAAtAIZAeYCbQJuAm9bTlNQcm90b0NlbGxZTlNOdW1Sb3dzXk5TU2VsZWN0ZWRDZWxs +W05TQ2VsbENsYXNzXxAVTlNDZWxsQmFja2dyb3VuZENvbG9yWk5TQ2VsbFNpemVZTlNOdW1Db2xzXxAS +TlNJbnRlcmNlbGxTcGFjaW5nXU5TTWF0cml4RmxhZ3NXTlNDZWxsc4BQgHqAaYB3gGuAOoB2gHmAEIB0 +CYBQgHUSRCggAIBqXxAWe3sxODUsIDIzfSwgezMwOSwgNTh9fdIADgA+AEYCc4BEowJlAnUCdoBrgHCA +ct0AxAAOAMUAxgDHAMgAyQDKAMsAzADNAM4AzwJ4ANECegDTANsCewJ6An0A2AH3ANoA2wDcE/////+E +Af4AgBeAb4BtgG+AbIAQgGhvEC0AQwBoAGkAZQBkAGkAbQBpACAAcwBlACAAaQBsACAAcwBpAHMAdABl +AG0AYQAgAHAAdQDyACAAZQBzAHMAZQByAGUAIABhAHUAdABvAHIAaQB6AHoAYQB0AG/SAA4A6wDsAoOA +FYBuXU5TUmFkaW9CdXR0b27SAA4BOQE6APKAJ94AxAAOAMUAxgDHAMgAyQDKAMsAzADNAM4AzwKIASIA +0QJ6ANMA2wJ7AnoCjQDYAfcA2gDbANwCGVVOU1RhZ4AXgG+AbYBvgHGAEIBobxArAFIAaQBmAGkAdQB0 +AGEAIABzAGUAIABpAGwAIABzAGkAcwB0AGUAbQBhACAAbgBvAG4AIADoACAAZwBpAOAAIABhAHUAdABv +AHIAaQB6AHoAYQB0AG/eAMQADgDFAMYAxwDIAMkAygDLAMwAzQDOAM8CiAEiANEA0gKUANsCewDSApcA +2AH3ApoA2wDcANSAF4AWEEuAbYAWgHOAEIBoEQGQXxAXQWNjZXR0YSBhdXRvbWF0aWNhbWVudGVZezMw +OSwgMTh9Vns0LCAyfdoAxAAOAMYAxwDIAMoAywDNAM4AzwEiANEClADbAnsCoQDYApoA2wDcgBeAbYB4 +gBBVUmFkaW/TAA4BnAGnAZ8BqQKmgD5CMQDSADcAOAKoAqmlAqkA+gD7APwAO1hOU01hdHJpeN4AqAKr +AA4AqQKsAq0CrgC4Aq8AqwCuArACsQKyAeYCtAK1ArYA1AK3ArgCuQK6AL0B5gK8Ar0CvVtOU0hTY3Jv +bGxlclhOU3NGbGFnc1xOU0Nvcm5lclZpZXdfEBBOU0hlYWRlckNsaXBWaWV3XE5TU2Nyb2xsQW10c1tO +U1ZTY3JvbGxlcl1OU05leHRLZXlWaWV3XU5TQ29udGVudFZpZXeAUICvgLSAs4CHgISAfE8QEEEgAABB +IAAAQZgAAEGYAACAUICrgH2AfdIADgA+AEYCwYBEpQK9ArwCtAK4AreAfYCrgK+AhICH2gCoAA4AqQLI +ALgAqwLJAsoArgKxAfgCzALNAs4CzwLQAtEC0gH4AtFZTlNjdkZsYWdzWU5TRG9jVmlld1lOU0JHQ29s +b3KAe4CqgKkQBIB+EQkAgH+AlYB7gH/SAA4APgBGAteARKEC0YB/3xAVAKgC2gAOAVMC2wLcAYsC3QKt +At4C3wLgAKsAuQCsADIC4QLiAK4C4wLkAr0A2wLmAucC6ALpAmgAtAK3Au0CGQLuAQsC7wC0AvEC8gLz +Ar0C9QL2XxAfTlNEcmFnZ2luZ1NvdXJjZU1hc2tGb3JOb25Mb2NhbF8QE05TT3JpZ2luYWxDbGFzc05h +bWVcTlNIZWFkZXJWaWV3XxASTlNBbGxvd3NUeXBlU2VsZWN0XxAXTlNJbnRlcmNlbGxTcGFjaW5nV2lk +dGhfEBlOU0NvbHVtbkF1dG9yZXNpemluZ1N0eWxlXxAYTlNJbnRlcmNlbGxTcGFjaW5nSGVpZ2h0W05T +R3JpZENvbG9yXxAcTlNEcmFnZ2luZ1NvdXJjZU1hc2tGb3JMb2NhbF5OU1RhYmxlQ29sdW1uc1tOU1Jv +d0hlaWdodIB9gKgSGsCAAICBgIOAeQmAhyNACAAAAAAAACNAAAAAAAAAAICCCYCAgKUQD4B9gIojQDEA +AAAAAABbVFBUYWJsZVZpZXdbTlNUYWJsZVZpZXdaezMzMywgMTA0fdYAqAAOAKsAuQCuAvgCuAL8AQsC +/QK4AtGAhICGgIWAhIB/2gCoAA4AqQLIALgAqwLJAsoArgKxAfgCzAMDAs4DBALQAukC0gH4AumAe4Cq +gLKAsYCDgJWAe4CDWXszMzMsIDE3fdIANwA4AwsDDKQDDAD7APwAO18QEU5TVGFibGVIZWFkZXJWaWV3 +1QCoAA4AqQCrAK4B+AMPAxABCwH4gHuAiYCIgHtfEBR7ey0yNiwgMH0sIHsxNiwgMTd9fdIANwA4AxQD +FaQDFQD7APwAO11fTlNDb3JuZXJWaWV30gAOAD4ARgMYgESjAxkDGgMbgIuAmICd2gMdAA4DHgHLAx8D +IAMhAyIDIwL4ALQDJQMmAycDKAMpAhkDKgMrAtFeTlNJc1Jlc2l6ZWFibGVcTlNIZWFkZXJDZWxsV05T +V2lkdGhaTlNEYXRhQ2VsbF5OU1Jlc2l6aW5nTWFza1pOU01pbldpZHRoWk5TTWF4V2lkdGgJgJeAjYCM +I0BioAAAAAAAgJMjQEQAAAAAAAAjQI9AAAAAAACAf1RuYW1l1wDEAA4BiwDKAMsAzgGMAy8DMAMxAzIB +kADbAzQSBIH+AICSgI+AjoA5gJBUTm9tZdMADgGcAacBnwGpAziAPkswLjMzMzMzMjk5ANUADgGbAZwB +nQGeAZ8BrwGhAzwBo4A+gEGAkYA7XxAPaGVhZGVyVGV4dENvbG9y0gA3ADgDQANBpQNBAbgA9gCqADtf +EBFOU1RhYmxlSGVhZGVyQ2VsbNgAxAAOAYsAygDLAMwAzgGMA0MBjQLSA0YBkALRA0kBkxIUIf5AgEKA +lYCUgDmAfxIAAggAgD9ZVGV4dCBDZWxs1QAOAZsBnAGdAZ4BnwGgAaEDTwGjgD6APYCWgDtfEBZjb250 +cm9sQmFja2dyb3VuZENvbG9y0gA3ADgDUwNUogNUADtdTlNUYWJsZUNvbHVtbtoDHQAOAx4BywMfAyAD +IQMiAyMC+AC0AyUDWANZA1oDWwIZA1wDKwLRCYCXgJqAmSNAXQAAAAAAAICcI0AgAAAAAAAAgH9XYWRk +cmVzc9cAxAAOAYsAygDLAM4BjAMvAzADMQNiAZAA2wM0gJKAj4CbgDmAkFlJbmRpcml6em/YAMQADgGL +AMoAywDMAM4BjANDAY0C0gNGAZAC0QNJAZOAQoCVgJSAOYB/gD/YAA4DHgHLAx8DIAMiAyMC+AMlA28D +cANxA3IDcwMrAtGAl4CfgJ4jQE2AAAAAAACAoyNATUmv4AAAAIB/W2NlcnRpZmljYXRl1wDEAA4BiwDK +AMsAzgGMASIDMAN4A3kBkADbAzSAkoChgKCAOYCQW0NlcnRpZmljYXRv1QAOAZsBnAGdAZ4BnwJoAaED +gAGjgD6AeYCigDtbaGVhZGVyQ29sb3LcAMQADgDFAMYAxwDJAMoAywDMAM0AzgDPASIA0QDSApQDhgDS +A4gBkALRApoDiwOMgBeAFhAkgBaApIA5gH8SCAIAABP/////hrZA/1RWZWRp1QAOAZsBnAGdAZ4BnwOQ +AaEDkQGjgD6Ap4CmgDtZZ3JpZENvbG9y0wAOAZwBpwGfAakDloA+RDAuNQDSADcAOAOYA5miA5kAO15O +U0NsYXNzU3dhcHBlcl8QFXt7MSwgMTd9LCB7MzMzLCAxMDR9fdIANwA4A5wDnaQDnQD7APwAO1pOU0Ns +aXBWaWV32ACoA58ADgCpAKsDoACuA6EB+AH4A6QDpQELA6YB+AOoWE5TVGFyZ2V0WE5TQWN0aW9uWU5T +UGVyY2VudIB7gHuAroCsgK2AeyM/7YLYIAAAAF8QGXt7LTEwMCwgLTEwMH0sIHsxNSwgMTgwfX1cX2Rv +U2Nyb2xsZXI60gA3ADgDrAOtpQOtAPoA+wD8ADtaTlNTY3JvbGxlctkAqAOfAA4AqQKsAKsDoACuA6EB ++AH4A6QDsgIZAQsDpgH4A7WAe4B7gK6AsICtgHsjP++x+yAAAABfEBl7ey0xMDAsIC0xMDB9LCB7NDYz +LCAxNX190gAOAD4ARgO5gEShAumAg18QE3t7MSwgMH0sIHszMzMsIDE3fX1fEBh7ezE4NSwgMTE2fSwg +ezMzNSwgMTIyfX3SADcAOAO+A7+kA78A+wD8ADtcTlNTY3JvbGxWaWV31wCoAA4AqQCqAKsArACuAeYB +ggPDA8QCAAC0AeaAUIBDgLaAtwmAUF8QFXt7MTQsIDY0fSwgezE1NywgMTd9fdgAxAAOAYsAygDLAMwA +zgGMASIBjQGOA8sA2AH5AlMBk4BCgDqAuIAQgLWAP18QF1JpY2hpZXN0ZSBkaSBjb250cm9sbG861wCo +AA4AqQCqAKsArACuAeYBggPTA9QAswC0AeaAUIBDgLqAuwmAUF8QFXt7OCwgMjIxfSwgezE2MywgMTd9 +fdgAxAAOAYsAygDLAMwAzgGMASIBjQGOA9sA2AH6AlMBk4BCgDqAvIAQgLmAP18QFFNpc3RlbWkgYXV0 +b3JpenphdGk6Wns1NDksIDMxOX1fEBlJbXBvc3RhemlvbmkgZGkgc2ljdXJlenph1gAOAcsA+wHMAZsA +ogHNA+QD5QEVAY4D6IBMgMGAwoAsgDqBAXVXb3B0aW9uc9UAqAAOAKkAuACrACsBCQPtA+4BC4AAgEaB +AXSAw9IADgA+AEYD8YBEoQPygMTXAKgADgC4AKsAuQAyAK4D5QC7A/YB6wP3AMAD5YDCgDSAxYEBc4Az +gMLSAA4APgBGA/yARK8QFgP9A/4D/wQABAEEAgQDBAQEBQQGBAcECAQJBAoECwQMBA0EDgQPBBAEEQQS +gMaAyoDOgNKA1oDagN6A4oDmgOqBAQiBAQyBARCBARSBARiBAUWBAVaBAVqBAV+BAWOBAWuBAW/XAKgA +DgCpAKoAqwCsAK4D8gCwBBYEFwCzALQD8oDEgBiAx4DICYDEXxAXe3sxNjksIDEzMn0sIHsxNTYsIDE4 +fX3dAMQADgDFAMYAxwDIAMkAygDLAMwAzQDOAM8BIgDRANIA0wDUANUA0gQgANgD/QDaANsA3IAXgBaA +E4AWgMmAEIDGbxAWAFMAbwBsAG8AIABzAGUAIABwAGkA+QAgAHAAaQBjAGMAbwBsAG8AIABkAGnXAKgA +DgCpAKoAqwCsAK4D8gGCBCcEKACzALQD8oDEgEOAy4DMCYDEXxAVe3szNCwgMTY0fSwgezk1LCAzNH19 +2ADEAA4BiwDKAMsAzADOAYwBIgGNAY4ELwDYA/4CUwGTgEKAOoDNgBCAyoA/XlRyYXNmZXJpbWVudGk6 +1wCoAA4AqQCqAKsArACuA/IBggQ3BDgAswC0A/KAxIBDgM+A0AmAxF8QFXt7MjcsIDE3fSwgezEwMiwg +MTd9fdgAxAAOAYsAygDLAMwAzgGMASIBjQGOBD8A2AP/AlMBk4BCgDqA0YAQgM6AP15BZ2dpb3JuYW1l +bnRpOtcAqAAOAKkAqgCrAKwArgPyAYIERwRIALMAtAPygMSAQ4DTgNQJgMRfEBV7ezcyLCAyODd9LCB7 +NTcsIDE3fX3YAMQADgGLAMoAywDMAM4BjAEiAY0BjgRPANgEAAJTAZOAQoA6gNWAEIDSgD9XQ2FtYmlv +OtcAqAAOAKkAqgCrAKwArgPyALAEVwRYALMAtAPygMSAGIDXgNgJgMRfEBZ7ezE0MSwgNzR9LCB7MjUx +LCAxOH193QDEAA4AxQDGAMcAyADJAMoAywDMAM0AzgDPASIA0QDSANMA1ADVANIEYQDYBAEA2gDbANyA +F4AWgBOAFoDZgBCA1l8QJE1vc3RyYSBsbyBzdGF0byBuZWxsYSBiYXJyYSBkZWwgbWVuddcAqAAOAKkA +qgCrAKwArgPyALAEaARpALMAtAPygMSAGIDbgNwJgMRfEBd7ezE0MSwgMTgwfSwgezE2MCwgMTh9fd0A +xAAOAMUAxgDHAMgAyQDKAMsAzADNAM4AzwJ4ANEA0gDTANQA1QDSBHIA2AQCANoA2wDcgBeAFoATgBaA +3YAQgNpfEBVEcmFnICYgRHJvcCB0cmEgaSBNYWPXAKgADgCpAKoAqwCsAK4D8gCwBHkEegCzALQD8oDE +gBiA34DgCYDEXxAXe3sxNjksIDEwOH0sIHsxODgsIDE4fX3dAMQADgDFAMYAxwDIAMkAygDLAMwAzQDO +AM8BIgDRANIA0wDUANUA0gSDANgEAwDaANsA3IAXgBaAE4AWgOGAEIDebxAbAFMAbwBsAG8AIABzAGUA +IABpAGwAIAB0AGEAcwB0AG8AIADoACAAcAByAGUAbQB1AHQAbwA61wCoAA4AqQCqAKsArACuA/IAsASK +BIsAswC0A/KAxIAYgOOA5AmAxF8QF3t7MTQxLCAxNTZ9LCB7MTY3LCAxOH193QDEAA4AxQDGAMcAyADJ +AMoAywDMAM0AzgDPAngA0QDSANMA1ADVANIElADYBAQA2gDbANyAF4AWgBOAFoDlgBCA4l8QF1NpbmNy +b25penphIGdsaSBhcHB1bnRp1wCoAA4AqQCqAKsArACuA/IAsASbBJwAswC0A/KAxIAYgOeA6AmAxF8Q +F3t7MTQxLCAyODZ9LCB7MjM5LCAxOH193QDEAA4AxQDGAMcAyADJAMoAywDMAM0AzgDPASIA0QDSANMA +1ADVANIEpQDYBAUA2gDbANyAF4AWgBOAFoDpgBCA5m8QIgBDAGEAbQBiAGkAYQAgAHMAbwBsAG8AIABz +AGUAIABpAGwAIAB0AGEAcwB0AG8AIADoACAAcAByAGUAbQB1AHQAbwA61wCoAA4AqQCqAKsArACuA/IE +qwSsBK0AswC0A/KAxIEBB4DrgOwJgMRfEBd7ezM2MCwgMTAzfSwgezE0MSwgMjZ9fd8QEgDEBLIEswDF +AMYADgDHAMkAywS0AMwEtQS2BLcAzQDOBLgAzwS5ALQCGQDSApQEvAIZBL0A2AS/BAYBqQC0ALQCmgTD +BMQExV8QGk5TTWVudUl0ZW1SZXNwZWN0QWxpZ25tZW50XxAPTlNBcnJvd1Bvc2l0aW9uWk5TTWVudUl0 +ZW1fEA9OU1ByZWZlcnJlZEVkZ2VfEBJOU1VzZXNJdGVtRnJvbU1lbnVdTlNBbHRlcnNTdGF0ZVZOU01l +bnUT/////4RB/kAJgBaBAQaA7YAQgO6A6gkJEQgAgO8SBoJA/9IADgE5AToA8oAn3AOfAA4EyQTKBMsE +zATNBM4EuAOgAogEzwStBNEE0gTTANIE1QTWBNcExATZBNoCGVdOU1RpdGxlXxARTlNLZXlFcXVpdk1v +ZE1hc2taTlNLZXlFcXVpdl1OU01uZW1vbmljTG9jWU5TT25JbWFnZVxOU01peGVkSW1hZ2VXTlNTdGF0 +ZYDsgPaA8BIAEAAAgBYSf////4DxgPOA74D1EBTTAA4EyQTcBN0E3gTfW05TTWVudUl0ZW1zgQEFgPeA ++GkjGAAgAEMAbwBtAG0AYQBuAGTTAA4AMgEvATABMQTkgCWAI4DyXxAPTlNNZW51Q2hlY2ttYXJr0wAO +ADIBLwEwATEE6YAlgCOA9F8QEE5TTWVudU1peGVkU3RhdGVfEBFfcG9wVXBJdGVtQWN0aW9uOtIANwA4 +BO0EtKIEtAA70gAOATkBOgTwgCdaT3RoZXJWaWV3c9IADgA+AEYE84BEpQS/BPUE9gT3BPiA7oD5gPyA +/4EBAtsDnwAOBMkEygTLBMwEzQTOBLgDoAKIBK0E0QT8BNMA0gTVBNYE1wTEBQEFAoDsgPaA+oAWgPGA +84DvgPsQE2gjJQAgAE8AcAB0AGkAbwBu2wOfAA4EyQTKBMsEzATNBM4EuAOgAogErQTRBQcE0wDSBNUE +1gTXBMQFDAUNgOyA9oD9gBaA8YDzgO+A/hASaSMDACAAQwBvAG4AdAByAG8AbNsDnwAOBMkEygTLBMwE +zQTOBLgDoAKIBK0E0QUSBNMA0gTVBNYE1wTEBRcFGIDsgPaBAQCAFoDxgPOA74EBARARZyHnACAAUwBo +AGkAZgB02wOfAA4EyQTKBMsEzATNBM4EuAOgAogErQTRBR0E0wDSBNUE1gTXBMQFIgUjgOyA9oEBA4AW +gPGA84DvgQEEEBBrIeoAIABDAGEAcABzACAATABvAGMAa9IANwA4BSYEuKIEuAA70gA3ADgFKAUppgUp +BSoA9QD2AKoAO18QEU5TUG9wVXBCdXR0b25DZWxsXk5TTWVudUl0ZW1DZWxs0gA3ADgFLAUtpgUtAPkA ++gD7APwAO11OU1BvcFVwQnV0dG9u1wCoAA4AqQCqAKsArACuA/IBggUxBTIAswC0A/KAxIBDgQEJgQEK +CYDEXxAWe3szMTUsIDI2MH0sIHszNCwgMTd9fdgAxAAOAYsAygDLAMwAzgGMASIBjQGOBTkBkAQHBTwB +k4BCgDqBAQuAOYEBCBIQQAAAgD9VQ29ydG/XAKgADgCpAKoAqwCsAK4D8gCwBUIFQwCzALQD8oDEgBiB +AQ2BAQ4JgMRfEBd7ezE0MSwgMjE0fSwgezIxMywgMTh9fd0AxAAOAMUAxgDHAMgAyQDKAMsAzADNAM4A +zwEiANEA0gDTANQA1QDSBUwA2AQIANoA2wDcgBeAFoATgBaBAQ+AEIEBDF8QH1Byb3ZhIGEgc3ZlZ2xp +YXJlIGkgTWFjIGluIHN0b3DXAKgADgCpAKoAqwCsAK4D8gCwBVMFVACzALQD8oDEgBiBARGBARIJgMRf +EBd7ezE0MSwgMjYyfSwgezE2OCwgMTh9fd0AxAAOAMUAxgDHAMgAyQDKAMsAzADNAM4AzwEiANEA0gDT +ANQA1QDSBV0A2AQJANoA2wDcgBeAFoATgBaBAROAEIEBEF8QFkNhbWJpYSBjb24gdW4gcml0YXJkbzrX +AKgADgCpAKoAqwCsAK4D8gCwBWQFZQCzALQD8oDEgBiBARWBARYJgMRfEBd7ezE0MSwgMjM4fSwgezM0 +MSwgMTh9fd0AxAAOAMUAxgDHAMgAyQDKAMsAzADNAM4AzwEiANEA0gDTANQA1QDSBW4A2AQKANoA2wDc +gBeAFoATgBaBAReAEIEBFF8QMENhbWJpYSBxdWFuZG8gcHJlbWkgZHVlIHZvbHRlIGlsIHRhc3RvIGRl +bCBtb3VzZdcAqAAOAKkAqgCrAKwArgPyAYIFdQV2ALMAtAPygMSAQ4EBGYEBGgmAxF8QFnt7MzMxLCAx +MzF9LCB7NTgsIDIyfX3aAMQADgGLAMsAzAV7AM4BVAV8AYwFfQGNBX8A2AQLBYIFgwC0BYUFhltOU0Zv +cm1hdHRlcl8QE05TUGxhY2Vob2xkZXJTdHJpbmcT/////5Rx/kGAQoEBQYAQgQEYgQEbEgRABAAJgQFA +gQFD3xARAA4FiAWJBYoFiwWMBY0FjgWPBZAFkQWSBZMFlAWVBZYFlwWYBZkFmgWbACsFnQWeBZ8FoAWh +ALQFowArBaUFpgWnBadWTlMubmlsWk5TLmRlY2ltYWxWTlMubmFuW05TLnJvdW5kaW5nV05TLnplcm9f +EBBOUy5uZWdhdGl2ZWF0dHJzVk5TLm1heF1OUy5hdHRyaWJ1dGVzXxARTlMucG9zaXRpdmVmb3JtYXRf +EA9OUy5hbGxvd3NmbG9hdHNfEBFOUy5uZWdhdGl2ZWZvcm1hdF8QEE5TLnBvc2l0aXZlYXR0cnNbTlMu +dGhvdXNhbmRWTlMubWluXE5TLmxvY2FsaXplZF8QD05TLmhhc3Rob3VzYW5kc4EBP4EBO4EBMoEBPYAA +gQEsgQExgQE3gQEcgQErCYEBLYAAgQE5gQEpCAjTAA4FqgA+BasFrAW5V05TLmtleXOBATqsBa0FrgWv +BbAFsQWyBbMFtAW1BbYFtwW4gQEdgQEegQEfgQEggQEhgQEigQEjgQEkgQElgQEmgQEngQEorAWmBaEF +nQWeBZoFvwXABcEFnwXDBaMFpYEBKYEBK4EBLIEBMYEBMoEBM4EBNIEBNoEBN4EBOIEBLYEBOVdtaW5p +bXVtXnBvc2l0aXZlRm9ybWF0XxAXYXR0cmlidXRlZFN0cmluZ0Zvclplcm9fEB90ZXh0QXR0cmlidXRl +c0Zvck5lZ2F0aXZlVmFsdWVzXxAQZGVjaW1hbFNlcGFyYXRvcl8QEWZvcm1hdHRlckJlaGF2aW9yVmxv +Y2FsZVxhbGxvd3NGbG9hdHNXbWF4aW11bV8QFXVzZXNHcm91cGluZ1NlcGFyYXRvcl5uZWdhdGl2ZUZv +cm1hdF8QEWdyb3VwaW5nU2VwYXJhdG9y1wAOBdMF1AXVBdYF1wXYBdkFpwDbANQA2wXbBadaTlMuY29t +cGFjdFtOUy5leHBvbmVudF5OUy5tYW50aXNzYS5ib1lOUy5sZW5ndGhbTlMubWFudGlzc2FbTlMubmVn +YXRpdmWBASoITxAQAAAAAAAAAAAAAAAAAAAAAAjSADcAOAXeBd+iBd8AO18QGk5TRGVjaW1hbE51bWJl +clBsYWNlaG9sZGVyUjBL0wAOBeIBPgXjBeQFo1xOU0F0dHJpYnV0ZXOBATCBAS6BAS1RMNMADgWqAD4F +6AXpBeqBAS+goNIANwA4BewF7aIF7QA7XE5TRGljdGlvbmFyedIANwA4Be8F8KIF8AA7XxASTlNBdHRy +aWJ1dGVkU3RyaW5n0wAOBaoAPgXoBfMF9IEBL6CgYQCgEQPo0gAOBfgF+QDSXU5TLmlkZW50aWZpZXKB +ATWAFtIANwA4BfwF/aIF/QA7WE5TTG9jYWxl1wAOBdMF1AXVBdYF1wXYBdkAtADbANQA1AYBBaeBASoJ +TxAQAABAAAAAAAAAAAAAAAAAAAhRLNIANwA4BgUGBqMGBgXtADtfEBNOU011dGFibGVEaWN0aW9uYXJ5 +0gAOAT4F4wYJgQEwgQE80wAOBeIBPgXjBeQGDYEBMIEBLoEBPlNOYU7SADcAOAYQBhGjBhEFewA7XxAR +TlNOdW1iZXJGb3JtYXR0ZXJaZGltZW5zaW9uadUADgGbAZwBnQGeAZ8CaAGhBhYBo4A+gHmBAUKAO18Q +E3RleHRCYWNrZ3JvdW5kQ29sb3LVAA4BmwGcAZ0BngGfAa8BoQYcAaOAPoBBgQFEgDtZdGV4dENvbG9y +1wCoAA4AqQCqAKsArACuA/IEqwYiBiMAswC0A/KAxIEBB4EBRoEBRwmAxF8QF3t7MzgzLCAyODF9LCB7 +MTQxLCAyNn193xASAMQEsgSzAMUAxgAOAMcAyQDLBLQAzAS1BLYEtwDNAM4EuADPBLkAtAIZANIClAS8 +AhkGKwDYBi0EDAGpALQAtAKaBMMGMQTFCYAWgQEGgQFIgBCBAUmBAUUJCYEBStIADgE5AToA8oAn3AOf +AA4EyQTKBMsEzATNBM4EuAOgAogEzwYjBNEE0gTTANIE1QTWBNcGMQY8BNoCGYEBR4D2gPCAFoDxgPOB +AUqBAUvTAA4EyQTcBN0GPwZAgQEFgQFMgQFN0gAOATkBOgTwgCfSAA4APgBGBkWARKUGLQZHBkgGSQZK +gQFJgQFOgQFQgQFSgQFU2wOfAA4EyQTKBMsEzATNBM4EuAOgAogGIwTRBPwE0wDSBNUE1gTXBjEGUwUC +gQFHgPaA+oAWgPGA84EBSoEBT9sDnwAOBMkEygTLBMwEzQTOBLgDoAKIBiME0QUHBNMA0gTVBNYE1wYx +BlwFDYEBR4D2gP2AFoDxgPOBAUqBAVHbA58ADgTJBMoEywTMBM0EzgS4A6ACiAYjBNEFEgTTANIE1QTW +BNcGMQZlBRiBAUeA9oEBAIAWgPGA84EBSoEBU9sDnwAOBMkEygTLBMwEzQTOBLgDoAKIBiME0QUdBNMA +0gTVBNYE1wYxBm4FI4EBR4D2gQEDgBaA8YDzgQFKgQFV1wCoAA4AqQCqAKsArACuA/IAsAZyBnMAswC0 +A/KAxIAYgQFXgQFYCYDEXxAWe3sxNDEsIDUwfSwgezI2NCwgMTh9fd0AxAAOAMUAxgDHAMgAyQDKAMsA +zADNAM4AzwEiANEA0gDTANQA1QDSBnwA2AQNANoA2wDcgBeAFoATgBaBAVmAEIEBVl8QJ01vc3RyYSBp +bCBiZXplbCBxdWFuZG8gY29udHJvbGxpIGlsIE1hY9cAqAAOAKkAqgCrAKwArgPyALAGgwaEALMAtAPy +gMSAGIEBW4EBXAmAxF8QFXt7Mzg5LCA3fSwgezEyMywgMzJ9fdwAxAAOAMUAxgDHAMkAygDLAMwAzQDO +AM8BIgDRANIA0wIZBosGjADYBA4A2gErAh6AF4AWgQFegQFdgBCBAVpdQ29udHJvbGxhIG9yYdIADgE5 +AToA8oAn1wCoAA4AqQCqAKsArACuA/IBggaVBpYAswC0A/KAxIBDgQFggQFhCYDEXxAUe3s4OCwgNzV9 +LCB7NDEsIDE3fX3YAMQADgGLAMoAywDMAM4BjAEiAY0BjgadANgEDwJTAZOAQoA6gQFigBCBAV+AP1ZT +dGF0bzrXAKgADgCpAKoAqwCsAK4D8gakBqUGpgCzALQD8oDEgQFqgQFkgQFlCYDEXxAWe3szNTMsIDI2 +MX0sIHs4MywgMTV9fd4GqwDEAA4GrAatAMoAywDMBq4GrwDOBrAGsQayBrMBIga0ANsCGQa1BrYEEAa4 +BrkGugWnBrwFp1dOU1ZhbHVlXxATTlNOdW1iZXJPZlRpY2tNYXJrc18QEk5TVGlja01hcmtQb3NpdGlv +blpOU01heFZhbHVlWk5TTWluVmFsdWVaTlNWZXJ0aWNhbF1OU0FsdEluY1ZhbHVlXxAaTlNBbGxvd3NU +aWNrTWFya1ZhbHVlc09ubHkjP+AAAAAAAACBAWmBAWaBAWeBAWMjP/AAAAAAAAAjP7mZmZmZmZoSAAIA +AAgjAAAAAAAAAAAI0gAOATkBOgDygCfUAA4A3wDgAOEA4gbCBsMFI4ASI0AoAAAAAAAAgQFoWUhlbHZl +dGljYdIANwA4BsYGx6QGxwD2AKoAO1xOU1NsaWRlckNlbGzSADcAOAbJBsqlBsoA+gD7APwAO1hOU1Ns +aWRlctcAqAAOAKkAqgCrAKwArgPyAYIGzgbPALMAtAPygMSAQ4EBbIEBbQmAxF8QFnt7NDQxLCAyNjB9 +LCB7MzgsIDE3fX3YAMQADgGLAMoAywDMAM4BjAEiAY0BjgbWAZAEEQU8AZOAQoA6gQFugDmBAWuAP1VM +dW5nb9cAqAAOAKkAqgCrAKwArgPyALAG3gbfALMAtAPygMSAGIEBcIEBcQmAxF8QFnt7MTQxLCAxNn0s +IHsyNDgsIDE4fX3dAMQADgDFAMYAxwDIAMkAygDLAMwAzQDOAM8BIgDRANIA0wDUANUA0gboANgEEgDa +ANsA3IAXgBaAE4AWgQFygBCBAW9fECNDb250cm9sbGEgYXV0b21hdGljYW1lbnRlIGFsbCdhdnZpb1dP +cHppb25p0gA3ADgG7gHMpAHMAPsA/AA7Wns1OTUsIDQzOH1Udmlld9QADgCgAKEAogbyAB8EDgb1gQF7 +gAKBAVqBAXpdY2hlY2tWZXJzaW9uOtIANwA4BvgG+aMG+QEBADtfEBVOU05pYkNvbnRyb2xDb25uZWN0 +b3LUAA4AoAChAKIAowFwAB8G/oAagDCAAoEBfV8QD19pbml0aWFsS2V5Vmlld9QADgCgAKEAogCjAB8B +cAcEgBqAAoAwgQF/WGRlbGVnYXRl1AAOAKAAoQCiAKMEAQAfBwqAGoDWgAKBAYFec3RhdHVzQ2hlY2ti +b3jUAA4AoAChAKIAowcOAB8HEIAagQGDgAKBAY/cBxIADgcTBxQHFQcWBxcHGAcZBxoHGwccALoHHgcf +ByAHIQciByMHJADUAvMHJQcmXE5TV2luZG93Vmlld1xOU1NjcmVlblJlY3RdTlNXaW5kb3dUaXRsZVlO +U1dURmxhZ3NdTlNXaW5kb3dDbGFzc1xOU1dpbmRvd1JlY3RZTlNNYXhTaXplXxAPTlNXaW5kb3dCYWNr +aW5nXxARTlNXaW5kb3dTdHlsZU1hc2tZTlNNaW5TaXplW05TVmlld0NsYXNzgByBAY6BAYuBAYUSQHgA +AIEBhoEBhIEBjYEBjIEBh18QGHt7NTE3LCA0NTB9LCB7NTk1LCA0Mjl9fV8QFTw8IGRvIG5vdCBsb2Nh +bGl6ZSA+PtIADgE5AToHK4AnVFZpZXfSAA4APgBGBy6ARKEAr4ALWns1OTUsIDQyOX1fEBZ7ezAsIDB9 +LCB7MTY4MCwgMTAyOH19XXsyMjQuNjY0LCAzMn1fEBp7My40MDI4MmUrMzgsIDMuNDAyODJlKzM4fdIA +NwA4BzUHNqIHNgA7XxAQTlNXaW5kb3dUZW1wbGF0ZVdfd2luZG931AAOAKAAoQCiBvIAHwETBzyBAXuA +AoAegQGRXxAPc2hvd0Fib3V0U2hlZXQ61AAOAKAAoQCiAKMAHwdBB0KAGoACgQGTgQGu1AAOB0QHRQdG +B0cHSAC0B0pfEA9fTlNNYW5hZ2VkUHJveHlaTlNFZGl0YWJsZV5OU0RlY2xhcmVkS2V5c4EBrYEBqwmB +AZTSAA4APgBGB02ARK8QFgdOB08HUAdRB1IHUwdUB1UHVgdXB1gHWQdaB1sHXAddB14HXwdgB2EHYgdj +gQGVgQGWgQGXgQGYgQGZgQGagQGbgQGcgQGdgQGegQGfgQGggQGhgQGigQGjgQGkgQGlgQGmgQGngQGo +gQGpgQGqXxAScHJlZnMuYWxsb3dDb250cm9sXxAVcHJlZnMuc2hhcmVQYXN0ZWJvYXJkXxAQcHJlZnMu +cmVxdWlyZUtleV8QE3ByZWZzLmRlbGF5ZWRTd2l0Y2hfEBRwcmVmcy5zaG93U3RhdHVzSXRlbV8QGXBy +ZWZzLmxpbWl0UGFzdGVib2FyZFNpemVfEBhwcmVmcy5tYXhQYXN0ZXJib2FyZFNpemVWYWN0aXZlXxAW +cHJlZnMuYXV0b2NoZWNrVmVyc2lvbl8QInByZWZzLnJlamVjdEF1dGhlbnRpY2F0aW9uUmVxdWVzdHNf +EBZwcmVmcy5lbmFibGVFbmNyeXB0aW9uXxAScHJlZnMuc3dpdGNoS2V5VGFnXxAPcHJlZnMuY29weUZp +bGVzXxAacHJlZnMucmVxdWlyZVBhc3RlYm9hcmRLZXlfEBZwcmVmcy5wYXN0ZWJvYXJkS2V5VGFnXxAa +cHJlZnMudHJ1c3RSZXF1ZXN0QmVoYXZpb3JfEA9wcmVmcy53YWtlT25MQU5fEBFwcmVmcy5zd2l0Y2hE +ZWxheV8QGmxvY2FsSG9zdC5zdXBwb3J0RHJhZ05Ecm9wXxAXcHJlZnMubWF4UGFzdGVib2FyZFNpemVf +EBZwcmVmcy5oaWRlQ29udHJvbEJlemVsXxAZcHJlZnMuc3dpdGNoV2l0aERvdWJsZVRhcNEADgd7gQGs +0gA3ADgHfQdEogdEADvSADcAOAd/B4CjB4AHgQA7XxASTlNPYmplY3RDb250cm9sbGVyXE5TQ29udHJv +bGxlcldjb250ZW501AAOAKAAoQCiAKMEBAAfB4eAGoDigAKBAbBfEBdzaGFyZVBhc3RlYm9hcmRDaGVj +a2JveNQADgCgAKEAogCjAB8C0QcEgBqAAoB/gQF/1AAOAKAAoQCiBvIAHwH0B5KBAXuAAoBbgQGzXxAW +c2hvd0NlcnRpZmljYXRlVmlld2VyOtQADgCgAKEAogCjB5YAHweYgBqBAbWAAoEBvdoAqAAOAKkC2wCq +AKsArACtADIArgeaAuYHnAedB54BCwC0B6AHoQeagQG4gKiBAbmBAbeBAbsJgQG6gQG2gQG4XxASVFBW +ZXJzaW9uVGV4dEZpZWxk1wCoAA4AuACrALkArQCuB6ABCQenAQsHqAegB6qBAbqARoEB1YECAYEBuoEC +Al8QFnt7MTcsIDExM30sIHsyNjEsIDE0fX3YAMQADgGLAMoAywDMAM4BjAEiAY0BjgevAZAHlgeyAZOA +QoA6gQG8gDmBAbUSCEAAAIA/WTx2ZXJzaW9uPl8QEHZlcnNpb25UZXh0RmllbGTUAA4AoAChAKIAowAf +AtEHuoAagAKAf4EBv1pkYXRhU291cmNl1AAOAKAAoQCiAKMBcAAfB8CAGoAwgAKBAcFcX2xhc3RLZXlW +aWV31AAOAKAAoQCiAKMB9AAfB8aAGoBbgAKBAcNfEBVzaG93Q2VydGlmaWNhdGVCdXR0b27UAA4AoACh +AKIAowFwAB8HzIAagDCAAoEBxVpsYXlvdXRWaWV31AAOAKAAoQCiAKMC0QAfB9KAGoB/gAKBAcdfEBV0 +cnVzdGVkSG9zdHNUYWJsZVZpZXfUAA4AoAChAKIAowLRB9cHQoAagH+BAcmBAa7UAA4HRAfaB0YHRwfc +B90H3l8QEU5TT2JqZWN0Q2xhc3NOYW1lgQGtgQHNgQHMgQHK0gAOAD4ARgfhgEShB+KBActfEBRudW1i +ZXJPZlNlbGVjdGVkUm93c9EADgd7gQGs1AAOAKAAoQCiAKMBcAAfB+qAGoAwgAKBAc9dX2ZpcnN0S2V5 +Vmlld9QADgCgAKEAogCjB+4AHwfwgBqBAdGAAoECBtwHEgAOBxMHFAcVBxYHFwcYBxkHGgcbBxwHmgce +B/QA0gf2B/cH+Af5ANQCGQf6B/uBAbiBAY6BAgOAFhJweAAAgQHTgQHSgQIFgQIEgQHUXxAYe3sxOTQs +IDM3NX0sIHsyOTUsIDMwN319XxAPVFBDbG9zaW5nV2luZG930gAOATkBOgcrgCfSAA4APgBGCAKARKcI +AwgEB5YIBggHCAgICYEB1oEB5YEBtYEB64EB8IEB94EB/NoAqAAOAKkHRQCqCAsAqwCsAK0ArgeaCA0I +DgC0CBAIEQELALQHoAeaW05TRHJhZ1R5cGVzgQG4gQHkgQHfCYEB4IEB1wmBAbqBAbjSAA4APgA/CBeA +B6cIGAgZCBoIGwgcCB0IHoEB2IEB2YEB2oEB24EB3IEB3YEB3l8QGUFwcGxlIFBERiBwYXN0ZWJvYXJk +IHR5cGVfEBlBcHBsZSBQTkcgcGFzdGVib2FyZCB0eXBlXxAjQ29yZVBhc3RlYm9hcmRGbGF2b3JUeXBl +IDB4NTA0RTQ3NjZfEBVOU0ZpbGVuYW1lc1Bib2FyZFR5cGVfEDFOZVhUIEVuY2Fwc3VsYXRlZCBQb3N0 +U2NyaXB0IHYxLjIgcGFzdGVib2FyZCB0eXBlXxAeTmVYVCBUSUZGIHY0LjAgcGFzdGVib2FyZCB0eXBl +XxAaQXBwbGUgUElDVCBwYXN0ZWJvYXJkIHR5cGVfEBd7ezIwLCAxNTl9LCB7MjU1LCAxMjh9fdgAxAAO +CCgAyggpCCoAzggrCCwILQDbCC4A2wDbCC8Fp1dOU1N0eWxlV05TQWxpZ25XTlNTY2FsZVpOU0FuaW1h +dGVzEiAB/gCBAeOBAeESAgAAAAjTAA4AMgEvATABMQg0gCWAI4EB4lh0ZWxlcG9ydNIANwA4CDcIOKMI +OACqADtbTlNJbWFnZUNlbGzSADcAOAg6CDulCDsA+gD7APwAO1tOU0ltYWdlVmlld9gAqAAOAKkAqgCr +AKwArQCuB5oBggg/CEABCwC0B6AHmoEBuIBDgQHmgQHnCYEBuoEBuF8QFnt7MTcsIDEyOX0sIHsyNjEs +IDIyfX3YAMQADgGLAMoAywDMAM4BjAEiAY0BjghICEkIBAeyAZOAQoA6gQHogQHpgQHlgD/UAA4A3wDg +AOEA4ghOCE8FI4ASI0AyAAAAAAAAgQHqXxARTHVjaWRhR3JhbmRlLUJvbGTYAKgADgCpAKoAqwCsAK0A +rgeaAYIIVAhVAQsAtAegB5qBAbiAQ4EB7IEB7QmBAbqBAbhfEBV7ezEyLCA0M30sIHsyNzQsIDM5fX3Y +AMQADgGLAMoAywDMAM4BjAEiAY0BjghdCF4IBghgAZOAQoA6gQHugQHvgQHrEgBAAACAP28QdQCpACAA +MgAwADAAMwAtADIAMAAwADgAIABhAGIAeQBzAHMAbwBmAHQACgBCAGEAcwBhAHQAbwAgAHMAdQAgAHUA +bgAnAGkAZABlAGEAIABkAGkAIABEAGEAdgBlACAAUwBhAGcAIABlACAASgBlAHIAZQBtAHkAIABRAHUA +aQBuAG4ALgAKAFIAaQBuAGcAcgBhAHoAaQBhAG0AZQBuAHQAaQAgAHMAcABlAGMAaQBhAGwAaQAgAGEA +IABNAGEAbgB1ADAAMgAsACAASgB1AGwAaQBlAG4AIABhAG4AZAAgAFAAYQB1AGwALtQADgDfAOAA4QDi +CGUA5AhngBIjQCQAAAAAAACAERELG9oAqAAOAKkC2wCqAKsArACtADIArgeaAuYIawedCG0IbgC0B6AI +cQeagQG4gKiBAfKBAbeBAfMRAQIJgQG6gQHxgQG4XxAPVFBMaW5rVGV4dEZpZWxkXxATe3sxMiwgOH0s +IHs2OSwgMTd9fdoAxAAOAYsAygDLAMwAzgFUBXwBjAh2AY0Bjgh5CHoIBwU8ALQIfQGTEgQh/gGAQoA6 +gQH0gQH1gQHwCYEB9oA/aiekACAAcwBpAHQAbwAgAHcAZQBi1AAOAN8A4ADhAOIBlwDkBSOAEoARXxAc +aHR0cDovL3RlbGVwb3J0LmFieXNzb2Z0LmNvbdoAqAAOAKkC2wCqAKsArACtADIArgeaAuYIhwedCIkI +bgC0B6AIcQeagQG4gKiBAfiBAbeBAfkJgQG6gQHxgQG4XxAUe3sxMTksIDh9LCB7NTcsIDE3fX3ZAMQA +DgGLAMoAywDMAM4FfAGMASIBjQGOCJIIeggIBTwIlQGTgEKAOoEB+oEB9YEB94EB+4A/ZyekACAAZgBv +AHIAdQBtXxAxaHR0cDovL3d3dy5hYnlzc29mdC5jb20vc29mdHdhcmUvdGVsZXBvcnQvZm9ydW1zL9oA +qAAOAKkC2wCqAKsArACtADIArgeaAuYInAedCJ4IbgC0B6AIcQeagQG4gKiBAf2BAbeBAf4JgQG6gQHx +gQG4XxAUe3syMjgsIDh9LCB7NTgsIDE3fX3ZAMQADgGLAMoAywDMAM4FfAGMASIBjQGOCKcIeggJBTwI +qgGTgEKAOoEB/4EB9YEB/IECAIA/ZiekACAAZABvAG4AYV8QrWh0dHBzOi8vd3d3LnBheXBhbC5jb20v +Y2dpLWJpbi93ZWJzY3I/Y21kPV94Y2xpY2smYnVzaW5lc3M9anVsJTQwbWFjJTJlY29tJml0ZW1fbmFt +ZT10ZWxlcG9ydCZub19zaGlwcGluZz0wJm5vX25vdGU9MCZ0YXg9MCZjdXJyZW5jeV9jb2RlPVVTRCZj +aGFyc2V0PVVURiUyZDgmY2hhcnNldD1VVEYlMmQ4WnsyOTUsIDMwN31aezIxMywgMTI5fVthYm91dFdp +bmRvd9QADgCgAKEAogCjARQAHwi1gBqAKIACgQIIXxASYWN0aXZhdGlvbkNoZWNrYm941AAOAKAAoQCi +AKMB8wAfCLuAGoBWgAKBAgpfEBdjaG9vc2VDZXJ0aWZpY2F0ZUJ1dHRvbtQADgCgAKEAogCjBAUAHwjB +gBqA5oACgQIMXxAScmVxdWlyZUtleUNoZWNrYm941AAOAKAAoQCiAKMAHwEVBwSAGoACgCyBAX/UAA4A +oAChAKIG8gAfAfMIzIEBe4ACgFaBAg9fEBdzaG93Q2VydGlmaWNhdGVDaG9vc2VyOtQADgCgAKEAogCj +BAkAHwjSgBqBARCAAoECEV8QFWRlbGF5ZWRTd2l0Y2hDaGVja2JveNQADgCgAKEAogCjAB8H7gcEgBqA +AoEB0YEBf9cADgCgCNoI2wChAKII3AjdB0EI3wjgBBAI4gDUWU5TS2V5UGF0aFlOU0JpbmRpbmdfEBxO +U05pYkJpbmRpbmdDb25uZWN0b3JWZXJzaW9ugQIXgQGTgQIWgQIVgQFjgQIUXxAidmFsdWU6IHNlbGVj +dGlvbi5wcmVmcy5zd2l0Y2hEZWxheVV2YWx1ZV8QG3NlbGVjdGlvbi5wcmVmcy5zd2l0Y2hEZWxhedIA +NwA4COcI6KMI6AEBADtfEBVOU05pYkJpbmRpbmdDb25uZWN0b3LXAA4AoAjaCNsAoQCiCNwI3QdBCOwI +7QH0CO8A1IECF4EBk4ECG4ECGoBbgQIZXxApZW5hYmxlZDogc2VsZWN0aW9uLnByZWZzLmVuYWJsZUVu +Y3J5cHRpb25XZW5hYmxlZF8QIHNlbGVjdGlvbi5wcmVmcy5lbmFibGVFbmNyeXB0aW9u2QAOAKAI2gjb +CPQAoQCiCPUI3AjdB0EI+Aj5AGUB9Aj8CP0A1F8QE05TUHJldmlvdXNDb25uZWN0b3JZTlNPcHRpb25z +gQIXgQGTgQIfgQIegQIYgFuBAh2BAiBfEBplbmFibGVkMjogc2VsZWN0aW9uLmFjdGl2ZVhlbmFibGVk +Ml8QEHNlbGVjdGlvbi5hY3RpdmXTAA4FqgA+BegJAwkIgQEvpAkECQUJBgkHgQIhgQIigQIjgQIkpAkJ +CQkJCQkJgQIlgQIlgQIlgQIlXxARTlNOdWxsUGxhY2Vob2xkZXJfEBpOU05vdEFwcGxpY2FibGVQbGFj +ZWhvbGRlcl8QGE5TTm9TZWxlY3Rpb25QbGFjZWhvbGRlcl8QG05TTXVsdGlwbGVWYWx1ZXNQbGFjZWhv +bGRlchP//////////9cADgCgCNoI2wChAKII3AjdB0EI+AjtBA0JGADUgQIXgQGTgQIfgQIagQFWgQIn +XxAZZW5hYmxlZDogc2VsZWN0aW9uLmFjdGl2ZdcADgCgCNoI2wChAKII3AjdB0EI+AjgARQJIADUgQIX +gQGTgQIfgQIVgCiBAilfEBd2YWx1ZTogc2VsZWN0aW9uLmFjdGl2ZdcADgCgCNoI2wChAKII3AjdB0EI ++AjtBAQJGADUgQIXgQGTgQIfgQIagOKBAifXAA4AoAjaCNsAoQCiCNwI3QdBCSwI4AQBCS8A1IECF4EB +k4ECLYECFYDWgQIsXxAldmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5zaG93U3RhdHVzSXRlbV8QHnNlbGVj +dGlvbi5wcmVmcy5zaG93U3RhdHVzSXRlbdcADgCgCNoI2wChAKII3AjdB0EJNQjtBAMJOADUgQIXgQGT +gQIwgQIagN6BAi9fEChlbmFibGVkOiBzZWxlY3Rpb24ucHJlZnMuc2hhcmVQYXN0ZWJvYXJkXxAfc2Vs +ZWN0aW9uLnByZWZzLnNoYXJlUGFzdGVib2FyZNkADgCgCNoI2wj0AKEAogj1CNwI3QdBCPgI+QBrBAMI +/AlDANSBAheBAZOBAh+BAh6BAi6A3oECHYECMtMADgWqAD4F6AlGCUuBAS+kCQQJBQkGCQeBAiGBAiKB +AiOBAiSkCQkJCQkJCQmBAiWBAiWBAiWBAiXXAA4AoAjaCNsAoQCiCNwI3QdBCPgI7QQSCRgA1IECF4EB +k4ECH4ECGoEBb4ECJ9cADgCgCNoI2wChAKII3AjdB0EI+AjtAfcJGADUgQIXgQGTgQIfgQIagGiBAifX +AA4AoAjaCNsAoQCiCNwI3QdBCWEI4AQLCWQA1IECF4EBk4ECN4ECFYEBGIECNl8QKHZhbHVlOiBzZWxl +Y3Rpb24ucHJlZnMubWF4UGFzdGVib2FyZFNpemVfECFzZWxlY3Rpb24ucHJlZnMubWF4UGFzdGVib2Fy +ZFNpemXXAA4AoAjaCNsAoQCiCNwI3QdBCPgI7QQBCRgA1IECF4EBk4ECH4ECGoDWgQIn1wAOAKAI2gjb +AKEAogjcCN0HQQk1CO0D/Qk4ANSBAheBAZOBAjCBAhqAxoECL9kADgCgCNoI2wj0AKEAogj1CNwI3QdB +CPgI+QBxA/0I/Al9ANSBAheBAZOBAh+BAh6BAjmAxoECHYECO9MADgWqAD4F6AmACYWBAS+kCQQJBQkG +CQeBAiGBAiKBAiOBAiSkCQkJCQkJCQmBAiWBAiWBAiWBAiXXAA4AoAjaCNsAoQCiCNwI3QdBCPgI7QQF +CRgA1IECF4EBk4ECH4ECGoDmgQIn1wAOAKAI2gjbAKEAogjcCN0HQQj4CO0ECQkYANSBAheBAZOBAh+B +AhqBARCBAifXAA4AoAjaCNsAoQCiCNwI3QdBCPgI7QLRCRgA1IECF4EBk4ECH4ECGoB/gQIn1wAOAKAI +2gjbAKEAogjcCN0HQQj4CO0BcQkYANSBAheBAZOBAh+BAhqANYECJ9cADgCgCNoI2wChAKII3AjdB0EI ++AjtAfUJGADUgQIXgQGTgQIfgQIagGCBAifXAA4AoAjaCNsAoQCiCNwI3QdBCbAI4AQSCbMA1IECF4EB +k4ECQ4ECFYEBb4ECQl8QJ3ZhbHVlOiBzZWxlY3Rpb24ucHJlZnMuYXV0b2NoZWNrVmVyc2lvbl8QIHNl +bGVjdGlvbi5wcmVmcy5hdXRvY2hlY2tWZXJzaW9u1wAOAKAI2gjbAKEAogjcCN0HQQm5CO0EDAm8ANSB +AheBAZOBAkaBAhqBAUWBAkVfECNlbmFibGVkOiBzZWxlY3Rpb24ucHJlZnMucmVxdWlyZUtleV8QGnNl +bGVjdGlvbi5wcmVmcy5yZXF1aXJlS2V52QAOAKAI2gjbCPQAoQCiCPUI3AjdB0EI+Aj5AHkEDAj8CccA +1IECF4EBk4ECH4ECHoECRIEBRYECHYECSNMADgWqAD4F6AnKCc+BAS+kCQQJBQkGCQeBAiGBAiKBAiOB +AiSkCQkJCQkJCQmBAiWBAiWBAiWBAiXXAA4AoAjaCNsAoQCiCNwI3QdBCOwI7QHzCO8A1IECF4EBk4EC +G4ECGoBWgQIZ2QAOAKAI2gjbCPQAoQCiCPUI3AjdB0EI+Aj5AHsB8wj8CeMA1IECF4EBk4ECH4ECHoEC +SYBWgQIdgQJL0wAOBaoAPgXoCeYJ64EBL6QJBAkFCQYJB4ECIYECIoECI4ECJKQJCQkJCQkJCYECJYEC +JYECJYECJdcADgCgCNoI2wChAKII3AjdB0EI+AjtBAIJGADUgQIXgQGTgQIfgQIagNqBAifZAA4AoAja +CNsI9AChAKII9QjcCN0HQQn6CPkAfQQCCf4J/wDUgQIXgQGTgQJPgQIegQJMgNqBAk6BAlBfEC5lbmFi +bGVkMjogc2VsZWN0aW9uLmxvY2FsSG9zdC5zdXBwb3J0RHJhZ05Ecm9wXxAkc2VsZWN0aW9uLmxvY2Fs +SG9zdC5zdXBwb3J0RHJhZ05Ecm9w0wAOBaoAPgXoCgQKCYEBL6QJBAkFCQYJB4ECIYECIoECI4ECJKQJ +CQkJCQkJCYECJYECJYECJYECJdcADgCgCNoI2wChAKII3AjdB0EI+AjtAKQJGADUgQIXgQGTgQIfgQIa +gAqBAifXAA4AoAjaCNsAoQCiCNwI3QdBChgI4ACkChsA1IECF4EBk4ECVIECFYAKgQJTXxAjdmFsdWU6 +IHNlbGVjdGlvbi5wcmVmcy5hbGxvd0NvbnRyb2xfEBxzZWxlY3Rpb24ucHJlZnMuYWxsb3dDb250cm9s +1wAOAKAI2gjbAKEAogjcCN0HQQohCiIEBgokANSBAheBAZOBAliBAleA6oECVl8QLXNlbGVjdGVkVGFn +OiBzZWxlY3Rpb24ucHJlZnMucGFzdGVib2FyZEtleVRhZ1tzZWxlY3RlZFRhZ18QIHNlbGVjdGlvbi5w +cmVmcy5wYXN0ZWJvYXJkS2V5VGFn1wAOAKAI2gjbAKEAogjcCN0HQQorCOAD/QouANSBAheBAZOBAluB +AhWAxoECWl8QKnZhbHVlOiBzZWxlY3Rpb24ucHJlZnMubGltaXRQYXN0ZWJvYXJkU2l6ZV8QI3NlbGVj +dGlvbi5wcmVmcy5saW1pdFBhc3RlYm9hcmRTaXpl1wAOAKAI2gjbAKEAogjcCN0HQQo0COAECQo3ANSB +AheBAZOBAl6BAhWBARCBAl1fECR2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmRlbGF5ZWRTd2l0Y2hfEB1z +ZWxlY3Rpb24ucHJlZnMuZGVsYXllZFN3aXRjaNcADgCgCNoI2wChAKII3AjdB0EKPQjtBAYKQADUgQIX +gQGTgQJhgQIagOqBAmBfEC1lbmFibGVkOiBzZWxlY3Rpb24ucHJlZnMucmVxdWlyZVBhc3RlYm9hcmRL +ZXlfECRzZWxlY3Rpb24ucHJlZnMucmVxdWlyZVBhc3RlYm9hcmRLZXnZAA4AoAjaCNsI9AChAKII9Qjc +CN0HQQk1CPkAhAQGCkoKSwDUgQIXgQGTgQIwgQIegQJfgOqBAmOBAmRfECllbmFibGVkMjogc2VsZWN0 +aW9uLnByZWZzLnNoYXJlUGFzdGVib2FyZNMADgWqAD4F6ApPClSBAS+kCQQJBQkGCQeBAiGBAiKBAiOB +AiSkCQkJCQkJCQmBAiWBAiWBAiWBAiXZAA4AoAjaCNsI9AChAKII9QjcCN0HQQj4Cl0AhQQGCmAKYQDU +gQIXgQGTgQIfgQJngQJigOqBAmaBAmhfEBplbmFibGVkMzogc2VsZWN0aW9uLmFjdGl2ZVhlbmFibGVk +M9MADgWqAD4F6ApmCmuBAS+kCQQJBQkGCQeBAiGBAiKBAiOBAiSkCQkJCQkJCQmBAiWBAiWBAiWBAiXX +AA4AoAjaCNsAoQCiCNwI3QdBCbkI4AQFCnYA1IECF4EBk4ECRoECFYDmgQJqXxAhdmFsdWU6IHNlbGVj +dGlvbi5wcmVmcy5yZXF1aXJlS2V51wAOAKAI2gjbAKEAogjcCN0HQQp7COAEAgp+ANSBAheBAZOBAm2B +AhWA2oECbF8QIHZhbHVlOiBzZWxlY3Rpb24ucHJlZnMuY29weUZpbGVzXxAZc2VsZWN0aW9uLnByZWZz +LmNvcHlGaWxlc9cADgCgCNoI2wChAKII3AjdB0EI+AjtBA4JGADUgQIXgQGTgQIfgQIagQFagQIn1wAO +AKAI2gjbAKEAogjcCN0HQQqLCiIB9wqOANSBAheBAZOBAnGBAleAaIECcF8QMXNlbGVjdGVkVGFnOiBz +ZWxlY3Rpb24ucHJlZnMudHJ1c3RSZXF1ZXN0QmVoYXZpb3JfECRzZWxlY3Rpb24ucHJlZnMudHJ1c3RS +ZXF1ZXN0QmVoYXZpb3LXAA4AoAjaCNsAoQCiCNwI3QdBCTUI4AQECpcA1IECF4EBk4ECMIECFYDigQJz +XxAmdmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5zaGFyZVBhc3RlYm9hcmTXAA4AoAjaCNsAoQCiCNwI3QdB +CpwI4AQKCp8A1IECF4EBk4ECdoECFYEBFIECdV8QKnZhbHVlOiBzZWxlY3Rpb24ucHJlZnMuc3dpdGNo +V2l0aERvdWJsZVRhcF8QI3NlbGVjdGlvbi5wcmVmcy5zd2l0Y2hXaXRoRG91YmxlVGFw1wAOAKAI2gjb +AKEAogjcCN0HQQk1CO0ECwk4ANSBAheBAZOBAjCBAhqBARiBAi/ZAA4AoAjaCNsI9AChAKII9QjcCN0H +QQj4CPkAjQQLCPwKsQDUgQIXgQGTgQIfgQIegQJ3gQEYgQIdgQJ50wAOBaoAPgXoCrQKuYEBL6QJBAkF +CQYJB4ECIYECIoECI4ECJKQJCQkJCQkJCYECJYECJYECJYECJdkADgCgCNoI2wj0AKEAogj1CNwI3QdB +CisKXQCOBAsKxQrGANSBAheBAZOBAluBAmeBAniBARiBAnuBAnxfEC1lbmFibGVkMzogc2VsZWN0aW9u +LnByZWZzLmxpbWl0UGFzdGVib2FyZFNpemXTAA4FqgA+BegKygrPgQEvpAkECQUJBgkHgQIhgQIigQIj +gQIkpAkJCQkJCQkJgQIlgQIlgQIlgQIl1wAOAKAI2gjbAKEAogjcCN0HQQrXCiIEDAraANSBAheBAZOB +An+BAleBAUWBAn5fEClzZWxlY3RlZFRhZzogc2VsZWN0aW9uLnByZWZzLnN3aXRjaEtleVRhZ18QHHNl +bGVjdGlvbi5wcmVmcy5zd2l0Y2hLZXlUYWfXAA4AoAjaCNsAoQCiCNwI3QdBCPgI7QQKCRgA1IECF4EB +k4ECH4ECGoEBFIECJ9cADgCgCNoI2wChAKII3AjdB0EI7AjgAfUK6gDUgQIXgQGTgQIbgQIVgGCBAoJf +ECd2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmVuYWJsZUVuY3J5cHRpb27XAA4AoAjaCNsAoQCiCNwI3QdB +CjQI7QQQCvIA1IECF4EBk4ECXoECGoEBY4EChF8QJmVuYWJsZWQ6IHNlbGVjdGlvbi5wcmVmcy5kZWxh +eWVkU3dpdGNo2QAOAKAI2gjbCPQAoQCiCPUI3AjdB0EI+Aj5AJMEEAj8CvwA1IECF4EBk4ECH4ECHoEC +g4EBY4ECHYEChtMADgWqAD4F6Ar/CwSBAS+kCQQJBQkGCQeBAiGBAiKBAiOBAiSkCQkJCQkJCQmBAiWB +AiWBAiWBAiXXAA4AoAjaCNsAoQCiCNwI3QdBCwwI4AQICw8A1IECF4EBk4ECiYECFYEBDIECiF8QIHZh +bHVlOiBzZWxlY3Rpb24ucHJlZnMud2FrZU9uTEFOXxAZc2VsZWN0aW9uLnByZWZzLndha2VPbkxBTtcA +DgCgCNoI2wChAKII3AjdB0EKPQjgBAMLGADUgQIXgQGTgQJhgQIVgN6BAotfECt2YWx1ZTogc2VsZWN0 +aW9uLnByZWZzLnJlcXVpcmVQYXN0ZWJvYXJkS2V51wAOAKAI2gjbAKEAogjcCN0HQQj4CO0ECAkYANSB +AheBAZOBAh+BAhqBAQyBAifYAA4AoAjaCNsAoQCiCPUI3AjdB0ELJAjgBA0LJwsoANSBAheBAZOBAo+B +AhWBAVaBAo6BApBfECd2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmhpZGVDb250cm9sQmV6ZWxfECBzZWxl +Y3Rpb24ucHJlZnMuaGlkZUNvbnRyb2xCZXplbNMADgWqAD4F6AstCy+BAS+hCy6BApGhCzCBApJfEBZO +U1ZhbHVlVHJhbnNmb3JtZXJOYW1lXxAPTlNOZWdhdGVCb29sZWFu1AAOAKALNAs1CzYApAs4CzlYTlNN +YXJrZXJWTlNGaWxlgQKWgAqBApWBApRfEBBOU1Rvb2xUaXBIZWxwS2V5XxBPU2VsZXppb25hIHBlciBw +ZXJtZXR0ZXJlIGFkIGFsdHJpIHV0ZW50aSBkaSB0ZWxlcG9ydCBkaSBjb250cm9sbGFyZSBpbCB0dW8g +TWFjLtIANwA4Cz0LPqILPgA7XxARTlNJQkhlbHBDb25uZWN0b3LUAA4AoAs0CzULNgETC0ILOYECloAe +gQKYgQKUbxAPAEEAYgBvAHUAdAAgAHQAZQBsAGUAcABvAHIAdCAm1AAOAKALNAs1CzYBFAtICzmBApaA +KIECmoEClF8QIFNlbGV6aW9uYSBwZXIgYXR0aXZhcmUgdGVsZXBvcnQu1AAOAKALNAs1CzYBcAtOCzmB +ApaAMIECnIEClG8QTwBRAHUAZQBzAHQAYQAgAOgAIABsACcAYQByAGUAYQAgAHAAZQByACAAbAAnAG8A +cgBnAGEAbgBpAHoAegBhAHoAaQBvAG4AZQAuACAAVAByAGEAcwBjAGkAbgBhACAAaQAgAHMAaQBzAHQA +ZQBtAGkAIABjAG8AbgB0AHIAbwBsAGwAYQB0AGkAIABkAG8AdgBlACAAdgB1AG8AaQAu1AAOAKALNAs1 +CzYECAtUCzmBApaBAQyBAp6BApRfEKdTb2xvIGNvbiBzaXN0ZW1pIGNvbGxlZ2F0aSB2aWEgRXRoZXJu +ZXQgZSBjb24gbCdvcHppb25lICJSaWF0dGl2YSBwZXIgbCdhY2Nlc3NvIGRlbGwnYW1taW5pc3RyYXRv +cmUgZGVsIG5ldHdvcmsgRXRoZXJuZXQiIGFiaWxpdGF0YSBuZWxsZSBwcmVmZXJlbnplIFJpc3Bhcm1p +byBlbmVyZ2lhLtQADgCgCzQLNQs2BAoLWgs5gQKWgQEUgQKggQKUbxBhAEwAYQAgAGQAbwBwAHAAaQBh +ACAAcwBlAGwAZQB6AGkAbwBuAGUAIADoACAAcQB1AGEAbgBkAG8AIABwAHIAZQBtAGkAIABpAGwAIAB0 +AGEAcwB0AG8AIABkAGUAbAAgAG0AbwB1AHMAZQAgAHMAdQBsACAAYgBvAHIAZABvACwAIABuAG8AbgAg +AHQAaQAgAHMAcABvAHMAdABpACAAZQAgAGwAbwAgAHAAcgBlAG0AaQAgAGEAbgBjAG8AcgBhAC7SAA4A +PgteC1+BAqSvEH8E+AP+BAkGcwT2AmQESAH/Bw4BcQMbBYIEEAfuAjgEBwHaBoQIbQgIAxkICQgQCIkB +GgQIAfUEDgQRBqYDWwQ4AfQC0QZJBBIEAQCkBHoB+QhACAYEBQScB9cIAwFDAhECJgMpALIArwFwAeYD +1AETALoECwbfA8QEBAQCBAACtAdBBIsBFQZHCAQByAVlBs8D8gHyBVQDcgH3BL8D5QQXArwEKAMaBjEF +dgieAfMD/QJJBAYBYQT1BiMCZQP/C78B+AHJB5oGSgUyCAcEaQQKAfYErQTEAWUEDALpAnYEDwaWB54I +VQH6ARQEAwT3AnUBhAZIBi0EDQVDBFgHloEBAoDKgQEQgQFYgPyAd4DUgFSBAYOANYCdgQEbgQFjgQHR +gGKBAQiAToEBXIEB84EB94CLgQH8gQHggQH5gCCBAQyAYIEBWoEBa4EBZYCcgNCAW4B/gQFSgQFvgNaA +CoDggLWBAeeBAeuA5oDogQHJgQHWgCqAWIBdgJOADoALgDCAUIC7gB6AHIEBGIEBcYC3gOKA2oDSgK+B +AZOA5IAsgQFOgQHlgE2BARaBAW2AxIBSgQESgKOAaIDugMKAyICrgMyAmIEBSoEBGoEB/oBWgMaAZoDq +gEmA+YEBR4BrgM6BAqKAe4DAgQG4gQFUgQEKgQHwgNyBARSAZIDsgO+ALoEBRYCDgHKBAV+BAWGBAbuB +Ae2AuYAogN6A/4BwgDeBAVCBAUmBAVaBAQ6A2IEBtdIADgAyADML4YAEgQKjXU5TQXBwbGljYXRpb27S +ADcAOAvkAb+iAb8AO9IADgA+C14L54ECpK8QfwTEA/ID8gQNBMQB9wQAAfIAHwFlAtEFdgPyAB8B9QPy +AcgEDggHB5oC0QeaCAMICAETA/IB5gPyA/IEEAMaA/8B5gH4BjED8gPyAK8EAwHmCAQHmgPyBAUAHwea +ARQB8wH0AxkApAC6AWUB2gH6AK8HDgPyBBIB+QPyA/ID8gH4AB8EBACvBjEHmgEVBAoEEQPlAeYECQMb +AeYExAHJA/0B+AP+AtEGIwQLCAkB5gPyAfYD8gEVBMQEDAH3A/IAHwHmARUH7gYxBAcHmgQCA/IB5gQG +BK0BYQPyAfgB9wPyBA8HlggGAeYArwPyBMQB9wFxBjEGMQPyBAgEAQeagO+AxIDEgQFWgO+AaIDSgFKA +AoAugH+BARqAxIACgGCAxIBNgQFagQHwgQG4gH+BAbiBAdaBAfeAHoDEgFCAxIDEgQFjgJiAzoBQgHuB +AUqAxIDEgAuA3oBQgQHlgQG4gMSA5oACgQG4gCiAVoBbgIuACoAcgC6AToC5gAuBAYOAxIEBb4C1gMSA +xIDEgHuAAoDigAuBAUqBAbiALIEBFIEBa4DCgFCBARCAnYBQgO+AwIDGgHuAyoB/gQFHgQEYgQH8gFCA +xIBkgMSALIDvgQFFgGiAxIACgFCALIEB0YEBSoEBCIEBuIDagMSAUIDqgOyASYDEgHuAaIDEgQFfgQG1 +gQHrgFCAC4DEgO+AaIA1gQFKgQFKgMSBAQyA1oEBuNIADgA+C14MaYECpK8QgAT4A/4ECQZzBPYCZARI +Af8HDgFxAxsH7gQQBYICOAHaBAcGhAhtCAgDGQgJCBAIiQEaBAgB9QQOBBEAHwamA1sB9AQ4AtEGSQQS +BAEApAhAAfkEeggGBAUH1wScCAMBQwImAhEDKQCyAK8BcAHmA9QBEwC6BAsG3wPEBAQEAgQAB0ECtASL +CAQBFQZHAcgFZQbPA/IB8gVUA3IB9wS/A+UCvAQXBCgDGgYxBXYIngHzA/0CSQQGAWECZQYjBPUD/wu/ +B5oByQH4BkoFMggHBAoEaQH2BK0ExAFlBAwC6QeeBA8CdgaWCFUB+gEUBAMCdQT3AYQGSAYtBA0FQweW +BFiBAQKAyoEBEIEBWID8gHeA1IBUgQGDgDWAnYEB0YEBY4EBG4BigE6BAQiBAVyBAfOBAfeAi4EB/IEB +4IEB+YAggQEMgGCBAVqBAWuAAoEBZYCcgFuA0IB/gQFSgQFvgNaACoEB54C1gOCBAeuA5oEByYDogQHW +gCqAXYBYgJOADoALgDCAUIC7gB6AHIEBGIEBcYC3gOKA2oDSgQGTgK+A5IEB5YAsgQFOgE2BARaBAW2A +xIBSgQESgKOAaIDugMKAq4DIgMyAmIEBSoEBGoEB/oBWgMaAZoDqgEmAa4EBR4D5gM6BAqKBAbiAwIB7 +gQFUgQEKgQHwgQEUgNyAZIDsgO+ALoEBRYCDgQG7gQFfgHKBAWGBAe2AuYAogN6AcID/gDeBAVCBAUmB +AVaBAQ6BAbWA2NIADgA+C14M7IECpK8QgAztDO4M7wzwDPEM8gzzDPQM9Qz2DPcM+Az5DPoM+wz8DP0M +/gz/DQANAQ0CDQMNBA0FDQYNBw0IDQkNCg0LDQwNDQ0ODQ8NEA0RDRINEw0UDRUNFg0XDRgNGQ0aDRsN +HA0dDR4NHw0gDSENIg0jDSQNJQ0mDScNKA0pDSoNKw0sDS0NLg0vDTANMQ0yDTMNNA01DTYNNw04DTkN +Og07DTwNPQ0+DT8NQA1BDUINQw1EDUUNRg1HDUgNSQ1KDUsNTA1NDU4NTw1QDVENUg1TDVQNVQ1WDVcN +WA1ZDVoNWw1cDV0NXg1fDWANYQ1iDWMNZA1lDWYNZw1oDWkNag1rDWyBAqiBAqmBAqqBAquBAqyBAq2B +Aq6BAq+BArCBArGBArKBArOBArSBArWBAraBAreBAriBArmBArqBAruBAryBAr2BAr6BAr+BAsCBAsGB +AsKBAsOBAsSBAsWBAsaBAseBAsiBAsmBAsqBAsuBAsyBAs2BAs6BAs+BAtCBAtGBAtKBAtOBAtSBAtWB +AtaBAteBAtiBAtmBAtqBAtuBAtyBAt2BAt6BAt+BAuCBAuGBAuKBAuOBAuSBAuWBAuaBAueBAuiBAumB +AuqBAuuBAuyBAu2BAu6BAu+BAvCBAvGBAvKBAvOBAvSBAvWBAvaBAveBAviBAvmBAvqBAvuBAvyBAv2B +Av6BAv+BAwCBAwGBAwKBAwOBAwSBAwWBAwaBAweBAwiBAwmBAwqBAwuBAwyBAw2BAw6BAw+BAxCBAxGB +AxKBAxOBAxSBAxWBAxaBAxeBAxiBAxmBAxqBAxuBAxyBAx2BAx6BAx+BAyCBAyGBAyKBAyOBAySBAyWB +AyaBAydvEBcATQBlAG4AdQAgAEkAdABlAG0AIAAoIeoAIABDAGEAcABzACAATABvAGMAawApXxAcU3Rh +dGljIFRleHQgKFRyYXNmZXJpbWVudGk6KV8QIkNoZWNrIEJveCAoQ2FtYmlhIGNvbiB1biByaXRhcmRv +OilfEDVCdXR0b24gQ2VsbCAoTW9zdHJhIGlsIGJlemVsIHF1YW5kbyBjb250cm9sbGkgaWwgTWFjKW8Q +FQBNAGUAbgB1ACAASQB0AGUAbQAgACgjAwAgAEMAbwBuAHQAcgBvAGwAKV8QJ1Byb3RvdHlwZSBQcm90 +b3R5cGUgQnV0dG9uIENlbGwgKFJhZGlvKV8QGVRleHQgRmllbGQgQ2VsbCAoQ2FtYmlvOilfEF9UZXh0 +IEZpZWxkIENlbGwgKFBlciByaW11b3ZlcmUgdW4gc2lzdGVtYSBhdXRvcml6emF0bywgc2VsZXppb25h +bG8gZSBwcmVtaSBpbCB0YXN0byBiYWNrc3BhY2UuKVhQcmVmUGFuZV8QbVN0YXRpYyBUZXh0IChEaXNw +b25pIGkgTWFjIGNvbmRpdmlzaSBpbiBhbHRvIGludG9ybm8gYWwgdHVvIHNjaGVybW8gbmVsbGEgc2V6 +aW9uZSBjZW50cmFsZSBwZXIgY29udHJvbGxhcmxpLilfEBpUYWJsZSBDb2x1bW4gKGNlcnRpZmljYXRl +KVVBYm91dF8QEUhvcml6b250YWwgU2xpZGVyXxAQTnVtYmVyIEZvcm1hdHRlcl8QIkJ1dHRvbiBDZWxs +IChBYmlsaXRhIGNyaXR0b2dyYWZpYSlfEBNTdGF0aWMgVGV4dCAoQ29ydG8pXxAbQnV0dG9uIENlbGwg +KENvbnRyb2xsYSBvcmEpbxAcAFQAZQB4AHQAIABGAGkAZQBsAGQAIABDAGUAbABsACAAKCekACAAcwBp +AHQAbwAgAHcAZQBiAClvEBkATABpAG4AawAgAFQAZQB4AHQAIABGAGkAZQBsAGQAIAAoJ6QAIABmAG8A +cgB1AG0AKV8QE1RhYmxlIENvbHVtbiAobmFtZSlvEBgATABpAG4AawAgAFQAZQB4AHQAIABGAGkAZQBs +AGQAIAAoJ6QAIABkAG8AbgBhAClfEBVJbWFnZSBDZWxsICh0ZWxlcG9ydClvEBkAVABlAHgAdAAgAEYA +aQBlAGwAZAAgAEMAZQBsAGwAIAAoJ6QAIABmAG8AcgB1AG0AKW8QFABCAHUAdAB0AG8AbgAgAEMAZQBs +AGwAIAAoAEEAYgBvAHUAdCAmAClfECtDaGVjayBCb3ggKFByb3ZhIGEgc3ZlZ2xpYXJlIGkgTWFjIGlu +IHN0b3ApXxAgQ2hlY2sgQm94IChBYmlsaXRhIGNyaXR0b2dyYWZpYSlfEBtQdXNoIEJ1dHRvbiAoQ29u +dHJvbGxhIG9yYSlfEBNTdGF0aWMgVGV4dCAoTHVuZ28pXEZpbGUncyBPd25lcltTbGlkZXIgQ2VsbF8Q +HVRleHQgRmllbGQgQ2VsbCAoVGV4dCBDZWxsKS0xXxAjUHVzaCBCdXR0b24gKE1vc3RyYSBpbCBjZXJ0 +aWZpY2F0bylfECBUZXh0IEZpZWxkIENlbGwgKEFnZ2lvcm5hbWVudGk6KV8QKVRhYmxlIFZpZXcgKE5v +bWUsIEluZGlyaXp6bywgQ2VydGlmaWNhdG8pbxAVAE0AZQBuAHUAIABJAHQAZQBtACAAKCHnACAAUwBo +AGkAZgB0ACkALQAxXxAvQ2hlY2sgQm94IChDb250cm9sbGEgYXV0b21hdGljYW1lbnRlIGFsbCdhdnZp +bylfEDBDaGVjayBCb3ggKE1vc3RyYSBsbyBzdGF0byBuZWxsYSBiYXJyYSBkZWwgbWVudSlfECBDaGVj +ayBCb3ggKENvbmRpdmlkaSBxdWVzdG8gTWFjKV8QGlRleHQgRmllbGQgQ2VsbCAodGVsZXBvcnQpXxAl +U3RhdGljIFRleHQgKFJpY2hpZXN0ZSBkaSBjb250cm9sbG86KW8QKQBCAHUAdAB0AG8AbgAgAEMAZQBs +AGwAIAAoAFMAbwBsAG8AIABzAGUAIABpAGwAIAB0AGEAcwB0AG8AIADoACAAcAByAGUAbQB1AHQAbwA6 +AClvEIMAUwB0AGEAdABpAGMAIABUAGUAeAB0ACAAKACpACAAMgAwADAAMwAtADIAMAAwADgAIABhAGIA +eQBzAHMAbwBmAHQACgBCAGEAcwBhAHQAbwAgAHMAdQAgAHUAbgAnAGkAZABlAGEAIABkAGkAIABEAGEA +dgBlACAAUwBhAGcAIABlACAASgBlAHIAZQBtAHkAIABRAHUAaQBuAG4ALgAKAFIAaQBuAGcAcgBhAHoA +aQBhAG0AZQBuAHQAaQAgAHMAcABlAGMAaQBhAGwAaQAgAGEAIABNAGEAbgB1ADAAMgAsACAASgB1AGwA +aQBlAG4AIABhAG4AZAAgAFAAYQB1AGwALgApbxAuAEMAaABlAGMAawAgAEIAbwB4ACAAKABDAGEAbQBi +AGkAYQAgAHMAbwBsAG8AIABzAGUAIABpAGwAIAB0AGEAcwB0AG8AIADoACAAcAByAGUAbQB1AHQAbwA6 +AClfEBFUcnVzdGVkSG9zdHNUYWJsZW8QMABCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAEMAYQBtAGIA +aQBhACAAcwBvAGwAbwAgAHMAZQAgAGkAbAAgAHQAYQBzAHQAbwAgAOgAIABwAHIAZQBtAHUAdABvADoA +KV8QFUltYWdlIFZpZXcgKHRlbGVwb3J0KV8QHUJ1dHRvbiBDZWxsIChBdHRpdmEgdGVsZXBvcnQpXxAj +QnV0dG9uIENlbGwgKE1vc3RyYSBpbCBjZXJ0aWZpY2F0bylvECQAQgB1AHQAdABvAG4AIABDAGUAbABs +ACAAKABTAGMAZQBnAGwAaQAgAGkAbAAgAGMAZQByAHQAaQBmAGkAYwBhAHQAbyAmAClfEBtUZXh0IEZp +ZWxkIENlbGwgKFRleHQgQ2VsbClfECJCdXR0b24gQ2VsbCAoQ29uZGl2aWRpIHF1ZXN0byBNYWMpW0N1 +c3RvbSBWaWV3W0xheW91dCBWaWV3XUN1c3RvbSBWaWV3LTFfECZUZXh0IEZpZWxkIENlbGwgKFNpc3Rl +bWkgYXV0b3JpenphdGk6KW8QFQBCAGUAdgBlAGwAIABCAHUAdAB0AG8AbgAgACgAQQBiAG8AdQB0ICYA +KVxDb250ZW50IFZpZXdaVGV4dCBGaWVsZF8QMUJ1dHRvbiBDZWxsIChDb250cm9sbGEgYXV0b21hdGlj +YW1lbnRlIGFsbCdhdnZpbylfEClUZXh0IEZpZWxkIENlbGwgKFJpY2hpZXN0ZSBkaSBjb250cm9sbG86 +KV8QI0NoZWNrIEJveCAoU2luY3Jvbml6emEgZ2xpIGFwcHVudGkpXxAhQ2hlY2sgQm94IChEcmFnICYg +RHJvcCB0cmEgaSBNYWMpXxAVU3RhdGljIFRleHQgKENhbWJpbzopXxASUHJlZlBhbmVDb250cm9sbGVy +XxATSG9yaXpvbnRhbCBTY3JvbGxlcl8QJUJ1dHRvbiBDZWxsIChTaW5jcm9uaXp6YSBnbGkgYXBwdW50 +aSlfEBZTdGF0aWMgVGV4dCAodGVsZXBvcnQpXxBBVG9wIFRhYiBWaWV3IChPcmdhbml6emF6aW9uZSwg +SW1wb3N0YXppb25pIGRpIHNpY3VyZXp6YSwgT3B6aW9uaSlvEBYATQBlAG4AdQAgAEkAdABlAG0AIAAo +IyUAIABPAHAAdABpAG8AbgApAC0AMV8QKVRhYiBWaWV3IEl0ZW0gKEltcG9zdGF6aW9uaSBkaSBzaWN1 +cmV6emEpXxA+QnV0dG9uIENlbGwgKENhbWJpYSBxdWFuZG8gcHJlbWkgZHVlIHZvbHRlIGlsIHRhc3Rv +IGRlbCBtb3VzZSlfEBdUZXh0IEZpZWxkIENlbGwgKEx1bmdvKV1DdXN0b20gVmlldy0yXxBbU3RhdGlj +IFRleHQgKFBlciByaW11b3ZlcmUgdW4gc2lzdGVtYSBhdXRvcml6emF0bywgc2VsZXppb25hbG8gZSBw +cmVtaSBpbCB0YXN0byBiYWNrc3BhY2UuKV8QJEJ1dHRvbiBDZWxsIChDYW1iaWEgY29uIHVuIHJpdGFy +ZG86KV8QEkJ1dHRvbiBDZWxsIChWZWRpKVZNYXRyaXhvEBUATQBlAG4AdQAgAEkAdABlAG0AIAAoIxgA +IABDAG8AbQBtAGEAbgBkAClWVmlldy0yXxARVmVydGljYWwgU2Nyb2xsZXJvECQAQgB1AHQAdABvAG4A +IABDAGUAbABsACAAKABTAG8AbABvACAAcwBlACAAcABpAPkAIABwAGkAYwBjAG8AbABvACAAZABpAClf +ECBUZXh0IEZpZWxkIENlbGwgKFRyYXNmZXJpbWVudGk6KV8QFlRhYmxlIENvbHVtbiAoYWRkcmVzcylf +EBNNZW51IChPdGhlclZpZXdzKS0xXxAPVGV4dCBGaWVsZCBDZWxsbxAYAFQAZQB4AHQAIABGAGkAZQBs +AGQAIABDAGUAbABsACAAKCekACAAZABvAG4AYQApbxAkAFAAdQBzAGgAIABCAHUAdAB0AG8AbgAgACgA +UwBjAGUAZwBsAGkAIABpAGwAIABjAGUAcgB0AGkAZgBpAGMAYQB0AG8gJgApbxAiAEMAaABlAGMAawAg +AEIAbwB4ACAAKABTAG8AbABvACAAcwBlACAAcABpAPkAIABwAGkAYwBjAG8AbABvACAAZABpAClfEB9U +ZXh0IEZpZWxkIENlbGwgKENyaXR0b2dyYWZpYTopbxAYAFAAbwBwAHUAcAAgAEIAdQB0AHQAbwBuACAA +KCMYACAAQwBvAG0AbQBhAG4AZAApXxAeVGFiIFZpZXcgSXRlbSAoT3JnYW5penphemlvbmUpbxA7AEIA +dQB0AHQAbwBuACAAQwBlAGwAbAAgACgAQwBoAGkAZQBkAGkAbQBpACAAcwBlACAAaQBsACAAcwBpAHMA +dABlAG0AYQAgAHAAdQDyACAAZQBzAHMAZQByAGUAIABhAHUAdABvAHIAaQB6AHoAYQB0AG8AKW8QIABQ +AG8AcAAgAFUAcAAgAEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgjGAAgAEMAbwBtAG0AYQBuAGQAKQAt +ADFvEBQATQBlAG4AdQAgAEkAdABlAG0AIAAoIyUAIABPAHAAdABpAG8AbgApXxAcU3RhdGljIFRleHQg +KEFnZ2lvcm5hbWVudGk6KVtBcHBsaWNhdGlvbl5Db250ZW50IFZpZXctMV8QF1RhYiBWaWV3IEl0ZW0g +KE9wemlvbmkpW1Njcm9sbCBWaWV3bxAZAE0AZQBuAHUAIABJAHQAZQBtACAAKCHqACAAQwBhAHAAcwAg +AEwAbwBjAGsAKQAtADFfEBdUZXh0IEZpZWxkIENlbGwgKENvcnRvKW8QHABMAGkAbgBrACAAVABlAHgA +dAAgAEYAaQBlAGwAZAAgACgnpAAgAHMAaQB0AG8AIAB3AGUAYgApXxA8Q2hlY2sgQm94IChDYW1iaWEg +cXVhbmRvIHByZW1pIGR1ZSB2b2x0ZSBpbCB0YXN0byBkZWwgbW91c2UpXxAjQnV0dG9uIENlbGwgKERy +YWcgJiBEcm9wIHRyYSBpIE1hYylfEBtTdGF0aWMgVGV4dCAoQ3JpdHRvZ3JhZmlhOilvEB4AUABvAHAA +IABVAHAAIABCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoIxgAIABDAG8AbQBtAGEAbgBkAClfEBFNZW51 +IChPdGhlclZpZXdzKVZWaWV3LTFvEBoAUABvAHAAdQBwACAAQgB1AHQAdABvAG4AIAAoIxgAIABDAG8A +bQBtAGEAbgBkACkALQAxXxARVGFibGUgSGVhZGVyIFZpZXdfEBtUZXh0IEZpZWxkIENlbGwgKDx2ZXJz +aW9uPilfEBRTdGF0aWMgVGV4dCAoU3RhdG86KV8QJUJ1dHRvbiBDZWxsIChBY2NldHRhIGF1dG9tYXRp +Y2FtZW50ZSlfEBhUZXh0IEZpZWxkIENlbGwgKFN0YXRvOilvEIcAVABlAHgAdAAgAEYAaQBlAGwAZAAg +AEMAZQBsAGwAIAAoAKkAIAAyADAAMAAzAC0AMgAwADAAOAAgAGEAYgB5AHMAcwBvAGYAdAAKAEIAYQBz +AGEAdABvACAAcwB1ACAAdQBuACcAaQBkAGUAYQAgAGQAaQAgAEQAYQB2AGUAIABTAGEAZwAgAGUAIABK +AGUAcgBlAG0AeQAgAFEAdQBpAG4AbgAuAAoAUgBpAG4AZwByAGEAegBpAGEAbQBlAG4AdABpACAAcwBw +AGUAYwBpAGEAbABpACAAYQAgAE0AYQBuAHUAMAAyACwAIABKAHUAbABpAGUAbgAgAGEAbgBkACAAUABh +AHUAbAAuAClfECJTdGF0aWMgVGV4dCAoU2lzdGVtaSBhdXRvcml6emF0aTopXxAbQ2hlY2sgQm94IChB +dHRpdmEgdGVsZXBvcnQpbxAnAEMAaABlAGMAawAgAEIAbwB4ACAAKABTAG8AbABvACAAcwBlACAAaQBs +ACAAdABhAHMAdABvACAA6AAgAHAAcgBlAG0AdQB0AG8AOgApbxA5AEIAdQB0AHQAbwBuACAAQwBlAGwA +bAAgACgAUgBpAGYAaQB1AHQAYQAgAHMAZQAgAGkAbAAgAHMAaQBzAHQAZQBtAGEAIABuAG8AbgAgAOgA +IABnAGkA4AAgAGEAdQB0AG8AcgBpAHoAegBhAHQAbwApbxATAE0AZQBuAHUAIABJAHQAZQBtACAAKCHn +ACAAUwBoAGkAZgB0AClfEHFUZXh0IEZpZWxkIENlbGwgKERpc3BvbmkgaSBNYWMgY29uZGl2aXNpIGlu +IGFsdG8gaW50b3JubyBhbCB0dW8gc2NoZXJtbyBuZWxsYSBzZXppb25lIGNlbnRyYWxlIHBlciBjb250 +cm9sbGFybGkuKW8QFwBNAGUAbgB1ACAASQB0AGUAbQAgACgjAwAgAEMAbwBuAHQAcgBvAGwAKQAtADFv +EBcATQBlAG4AdQAgAEkAdABlAG0AIAAoIxgAIABDAG8AbQBtAGEAbgBkACkALQAxXxAzQ2hlY2sgQm94 +IChNb3N0cmEgaWwgYmV6ZWwgcXVhbmRvIGNvbnRyb2xsaSBpbCBNYWMpXxAtQnV0dG9uIENlbGwgKFBy +b3ZhIGEgc3ZlZ2xpYXJlIGkgTWFjIGluIHN0b3ApXxAeVmVyc2lvbiBUZXh0IEZpZWxkICg8dmVyc2lv +bj4pXxAyQnV0dG9uIENlbGwgKE1vc3RyYSBsbyBzdGF0byBuZWxsYSBiYXJyYSBkZWwgbWVudSnSAA4A +PgteDe6BAqSlAtEHlggJCAgIB4B/gQG1gQH8gQH3gQHw0gAOAD4LXg32gQKkpQLxB6EIcQhxCHGAgIEB +toEB8YEB8YEB8dIADgA+C14N/oECpK8Q1wCUAJIBcQBcBBAAWAI4BAcGhACMAHEAkwgIAGMDGQCGCBAB +GgQIAIQB9QCZBA4GpgBhA1sB9AZJAFQEAQCkAI4AdQBSAHQIBgBuAG8AUwgDAUMAVQBoAXAAZwPUAJcA +XQbfA8QEBACVBAAHQQSLARUIBAHyBVQD5QQXBCgAiwCKAGAGMQBIBXYIngP9AkkEBgFhAmUAgAB/AH4A +TAH4AckHmgBKAHcAWQgHBGkAdgByBAwCdgQPB54AnAhVAfoAUAEUAnUAagBLBA0FQwRYB5YAZgBkBPgA +mABNA/4ECQBeAJoGcwT2AmQESACbAf8HDgMbAFYFggfuAE8B2ghtAIcAawgJCIkAgwBlAIIEEQBXAIEA +HwQ4AFEAWwLRAHgEEgBiBHoB+QhAAIUAcAQFBJwH1wIRAiYDKQBsALIArwHmAF8BEwC6BAsAlgQCArQA +TgZHAcgAjQVlAJEGzwB5AJAD8gNyAfcEvwK8AI8DGgCJAIgB8wT1BiMAewP/AEkAfQu/AHwAegCdBkoF +MgQKAHMAngH2AFoErQTEAWUC6QBtBpYE9wQDAGkBhAZIBi2BAoWBAoGANYEB0IEBY4EBxIBigQEIgQFc +gQJ0gQI5gQKDgQH3gQISgIuBAmWBAeCAIIEBDIECX4BggQKTgQFagQFlgQIOgJyAW4EBUoEBtIDWgAqB +AniBAj6BAbGBAj2BAeuBAjSBAjWBAbKBAdaAKoEBvoECKIAwgQImgLuBAoyBAgeBAXGAt4DigQKHgNKB +AZOA5IAsgQHlgFKBARKAwoDIgMyBAnKBAm+BAg2BAUqACYEBGoEB/oDGgGaA6oBJgGuBAlKBAlGBAk2B +AX6Ae4DAgQG4gQF5gQJAgQHGgQHwgNyBAj+BAjqBAUWAcoEBX4EBu4ECm4EB7YC5gQGSgCiAcIECK4EB +fIEBVoEBDoDYgQG1gQIcgQITgQECgQKNgQGAgMqBARCBAgmBApeBAViA/IB3gNSBApmAVIEBg4CdgQHA +gQEbgQHRgQGQgE6BAfOBAmmBAi6BAfyBAfmBAlyBAhiBAlmBAWuBAcKBAlWAAoDQgQGvgQHOgH+BAkGB +AW+BAhCA4IC1gQHngQJigQI4gOaA6IEByYBYgF2Ak4ECMYAOgAuAUIECC4AegByBARiBAoqA2oCvgQGC +gQFOgE2BAneBARaBAoCBAW2BAkSBAn2AxICjgGiA7oCrgQJ6gJiBAm6BAmuAVoD5gQFHgQJJgM6AG4EC +TIECooECSoECR4ECnYEBVIEBCoEBFIECPIECn4BkgQHIgOyA74AugIOBAjOBAWGA/4DegQIqgDeBAVCB +AUnSAA4APgteDtiBAqSvENcO2Q7aDtsO3A7dDt4O3w7gDuEO4g7jDuQO5Q7mDucO6A7pDuoO6w7sDu0O +7g7vDvAO8Q7yDvMO9A71DvYO9w74DvkO+g77DvwO/Q7+Dv8PAA8BDwIPAw8EDwUPBg8HDwgPCQ8KDwsP +DA8NDw4PDw8QDxEPEg8TDxQPFQ8WDxcPGA8ZDxoPGw8cDx0PHg8fDyAPIQ8iDyMPJA8lDyYPJw8oDykP +Kg8rDywPLQ8uDy8PMA8xDzIPMw80DzUPNg83DzgPOQ86DzsPPA89Dz4PPw9AD0EPQg9DD0QPRQ9GD0cP +SA9JD0oPSw9MD00PTg9PD1APUQ9SD1MPVA9VD1YPVw9YD1kPWg9bD1wPXQ9eD18PYA9hD2IPYw9kD2UP +Zg9nD2gPaQ9qD2sPbA9tD24Pbw9wD3EPcg9zD3QPdQ92D3cPeA95D3oPew98D30Pfg9/D4APgQ+CD4MP +hA+FD4YPhw+ID4kPig+LD4wPjQ+OD48PkA+RD5IPkw+UD5UPlg+XD5gPmQ+aD5sPnA+dD54Pnw+gD6EP +og+jD6QPpQ+mD6cPqA+pD6oPqw+sD60Prg+vgQMsgQMtgQMugQMvgQMwgQMxgQMygQMzgQM0gQM1gQM2 +gQM3gQM4gQM5gQM6gQM7gQM8gQM9gQM+gQM/gQNAgQNBgQNCgQNDgQNEgQNFgQNGgQNHgQNIgQNJgQNK +gQNLgQNMgQNNgQNOgQNPgQNQgQNRgQNSgQNTgQNUgQNVgQNWgQNXgQNYgQNZgQNagQNbgQNcgQNdgQNe +gQNfgQNggQNhgQNigQNjgQNkgQNlgQNmgQNngQNogQNpgQNqgQNrgQNsgQNtgQNugQNvgQNwgQNxgQNy +gQNzgQN0gQN1gQN2gQN3gQN4gQN5gQN6gQN7gQN8gQN9gQN+gQN/gQOAgQOBgQOCgQODgQOEgQOFgQOG +gQOHgQOIgQOJgQOKgQOLgQOMgQONgQOOgQOPgQOQgQORgQOSgQOTgQOUgQOVgQOWgQOXgQOYgQOZgQOa +gQObgQOcgQOdgQOegQOfgQOggQOhgQOigQOjgQOkgQOlgQOmgQOngQOogQOpgQOqgQOrgQOsgQOtgQOu +gQOvgQOwgQOxgQOygQOzgQO0gQO1gQO2gQO3gQO4gQO5gQO6gQO7gQO8gQO9gQO+gQO/gQPAgQPBgQPC +gQPDgQPEgQPFgQPGgQPHgQPIgQPJgQPKgQPLgQPMgQPNgQPOgQPPgQPQgQPRgQPSgQPTgQPUgQPVgQPW +gQPXgQPYgQPZgQPagQPbgQPcgQPdgQPegQPfgQPggQPhgQPigQPjgQPkgQPlgQPmgQPngQPogQPpgQPq +gQPrgQPsgQPtgQPugQPvgQPwgQPxgQPygQPzgQP0gQP1gQP2gQP3gQP4gQP5gQP6gQP7gQP8gQP9gQP+ +gQP/gQQAgQQBgQQCEQHEEQGAEJgQqhEBvxCREgABiB0RAcASAAGH8BIABJUREQFKEQHDEKsQ+BEBqBIA +AYdEEgABh0wRAbsRAaYRAX0SAASVHhEBUBIAAYhfEgAElPwSAAGHmREBgREBixC3EQEIEIgRAVoRAa8R +AQMRAV0QqBEBxhEBxxIABJT7EKQSAAGHThEBVhCJEgAElQ0SAAGIChEBvRCvEgABiAESAAGIWBC/EQG+ +EQFuEQEpEgABh18QuhClEQG5EgABh68QvhIAAYflEgABiAsRAUERAboRAYYQnBIAAYfmEgABh8URAUUS +AASVChEBnBC7EQG1EQFAEQFXEQHFELgQ9hC8EKIRAVERAX8RAQQRASMSAAGIMhEBsBEBWREBhREBtxEB +cBIAAYdGEgAElSESAAGHSBEBahEBKhCuEQG2EQFEEOkSAASVBxIAAYhbEgABh6gQphEBgxEBwhEBnxIA +BJUMEQEKEQFrEQEPEgAElPoSAASVHxIABJUIEQGgEgABiFISAAGIDhIABJUgEgABiFkQDBEBaBDqEQFO +EKEQrREBZhIAAYfDEQFCEQGjEQElEgABh8QRAUMRAYIRAUkRAcESAASU+REBqRIABJUdEgABiA8QzRDo +EPcRAWMRAWERARASAAGIOxEBuBIAAYdFEQGnEQFeEMISAAGHYhEBlhIABJT4EgABiCESAAGHmBEBpBIA +AYcoEMkSAASVGxDOEKwRAUYRAaURAZISAAMONhAaEQGJEQFlEQFLEgAElQ8SAASVEhIAAYhhEQGPEQGR +EgAElRwRAWkRAbIRAaESAAGHlhEBWxD5EQFgEQGVEgAElPcRAZ4SAAGIJRIABJUFEQFvEMoRAZMT//// +//////0SAASVBhIABJUiEQGKEgABiGASAASVDhEBXBIABJUjEgAElQkRAZcSAAGIPBEBnRC9EgAElNYR +AWQSAAGIEBEBohEBmxEBWBIAAYc4EQGHEQGI0gAOAD4ARhCEgESg0gAOAD4LXhCHgQKkoNIADgA+C14Q +ioECpKDSADcAOBCMEI2iEI0AO15OU0lCT2JqZWN0RGF0YQAIABkAIgAnADEAOgA/AEQAUgBUAGYIeAh+ +CMkI0AjXCOUI9wkTCSEJLQk5CUcJUglgCXwJigmdCa8JyQnTCeAJ4gnlCegJ6wnuCfAJ8wn1CfgJ+wn+ +CgEKAwoFCggKCwoQChMKHAooCioKLAo/CkgKUQpcCmEKcAp5CowKlQqgCqIKowqsCrMKwArGCs8K0QuC +C4QLhguJC4wLjwuSC5ULmAubC54LoQukC6cLqgutC7ALswu2C7kLvAu/C8ILxQvIC8sLzgvRC9QL1wva +C90L4AvjC+YL6QvsC+8L8gv1C/gL+wv+DAEMBAwHDAoMDQwQDBMMFgwZDBwMHwwiDCUMKAwrDC4MMQw0 +DDcMOgw9DEAMQwxGDEkMTAxPDFIMVQxYDFsMXgxhDGQMZwxqDG0McAxzDHYMeQx8DH8MggyFDJYMpAyt +DLUMtwy5DLsMvQzeDPAM+Az/DQgNEg0bDScNKQ0rDS0NLw0yDTMNNQ03DVgNYw1vDXENcw11DXgNew19 +DX8NgQ2bDdAN3A3yDgcOFg4pDjsORg5QDl4OcA59DosOkA6SDpQOlg6YDpoOnA6eDqAOog6kDqYOqw7C +DtMO2g7hDuoO7A71DvcO+g8HDxAPFQ8cDyUPMQ8zDzUPPg9HD0wPYg9jD2wPdQ+CD48PmA+jD6wPtg+9 +D8kP4A/pD/AQBxAWECcQKRArEC0QMBBNEE8QURBUEFcQWhBcEF8QaBBqEHMQdRB3EHkQexCcEJ4QoBCi +EKQQpxCoEKoQrBDFEPoRCBENEQ8RERETERURFxEZERsRHREiEScRNBFBEVARUhFUEVYRXhFpEXIRdxGK +EZMRnRGfEagRrxHBEcoR6xHtEe8R8RHzEfQR9hH4EhESRhJIEkoSTBJOElASUhJUEmYSmxKqErQSyBLh +EvkS+xL+EwATAhMEEwYTCBMJEwsTDBMOExcTGRMcEx4TOxM9Ez8TQRNDE0UTRxNQE1ITVxNZE1sTfBOI +E4oTjBOOE5ATkhOUE5YTrxO8E8UTzhPbE/wT/hQAFAIUBBQHFAgUChQMFCMURBRYFGQUZhRoFGoUbBRu +FHMUdRTXFOgU6hTzFPUU+BUNFRUVIhUuFTwVPhVAFUIVRBVGFU0VWhVnFW8VcRVzFX8ViBWNFaIVpBWm +FagVqhW9FcoVzBXPFdgV4RXzFfwWBxYTFhwWIxYyFjoWUxZcFmMWfBaFFocWjhaQFpIWlBatFroWxBbG +FsgWyhbMFs4W0BbXFuYW7xb0FwIXFxcZFxsXHRcfFyEXNhc4FzoXPBc+F0cXSRdMF04XaxdtF28XcRd0 +F3YXeBd6F4MXhReYF5oXnBeeF6AXohekF6YXqBeqF8cXyRfLF80XzxfSF9MX1RfuGA8YERgTGBUYFxgZ +GB4YIBhwGI0YjxiRGJMYlRieGJ8YoRi7GOwY7hjwGPIY9Bj2GPgY+hkDGTIZOxk9GVoZXBleGWAZYhlj +GWUZfxmwGbIZtBm2GbgZuhm8GdQZ3RnfGfwZ/hoAGgIaBBoFGgcaIRpWGlgaWhpcGl4aYBpiGmQaexqY +GpoanBqeGqAaoRqjGrwa3RrfGuEa4xrlGuca7BruGvwbRxtTG10bbBt4G5AbmxulG7obyBvQG9Ib1BvW +G9gb2hvcG94b4BviG+Qb5RvnG+kb7hvwHAkcEhwUHBscHRwfHCEcVhxfHGEcYxxlHGccaRxrHG0cyhzT +HNUc1xzlHO4c8B0pHS8dMR0zHTUdNx05HTsdPR2WHc8d0R3THdUd1x3ZHdsd3R3fHeId/B4GHg0eNh44 +HjoePB4+HkQeUR5THlYeXx5qHnMerB64HsEezh7hHu4e+h8IHxYfGB8aHxwfHh8gHyIfJB83HzkfOx89 +Hz8fSB9KH1UfVx9ZH1sfXR9fH4gfkh+cH6YfqB+qH6wfrh+wH7MftR+3H7kfux/EH8YfyR/LICIgRCBa +IGcgfCCWILIgzSDZIPghByETIRUhFyEcIR4hICEiISMhJSEuITchOSE6ITwhPiFAIUIhRCFNIVkhZSFw +IYkhiyGNIY8hkSGTIbwhviHAIcIhxCHGIcghyiHMIdYh3yHoIfwiESITIhUiFyIZIjAiOSJCIlAiWSJb +ImIiZCJmImgikSKgIq0itSLAIs8i2iLlIuYi6CLqIuwi9SL3IwAjCSMLIxAjLSMyIzQjNiM4IzojPCNB +I04jUCNcI3EjcyN1I3cjeSOLI5QjnyOzI9Qj2SPbI90j3yPhI+Mj6CPqI/QkCSQLJA0kDyQRJCokMyQ4 +JEYkbyRwJHIkdCR2JH8kgSSKJIwklCSxJLMktSS3JLkkuyTFJOYk6CTqJOwk7iTwJPIlEyUVJRclGSUi +JSQlLSUvJTslWCVaJVwlXiVgJWIlbiWDJYUlhyWJJYsllyXIJcolzCXOJdAl0iXUJdYl2yXkJekl/iYA +JgImBCYGJhAmHSYfJiQmLSYyJkEmWSZiJmsmdiaXJqAmqSazJrUmtya5JrsmvSa/Jsgm5CbxJvonBScQ +JzUnNyc5JzsnPSc/J0EnSidmJ28ncSd0J3YnjCenJ7AnuSfGJ+Mn5SfnJ+kn6yfsJ+4oBignKCkoKygt +KC8oMSgzKE0oaihsKG4ocChyKHModSiNKK4osCiyKLQotii4KLoo0SjcKPgpESkTKRUpFykZKRspHikm +KTspPSk/KUIpRClNKU8pUilUKXEpcyl1KXcpeil8KX4phymJKbgpuim8Kb4pwCnCKcQpxinIKcopzCnP +KdIp1SnYKdsp3inhKeQp5ynqKe0p8CoNKg8qESoTKhUqFioYKjIqZyppKmsqbSpvKnEqcyp1KqQqwSrD +KsUqxyrJKsoqzCrkKwUrBysJKwsrDSsPKxErICs9Kz8rQStDK0UrRitIK2ArgSuDK4UrhyuJK4srjSuc +K7kruyu9K78rwSvCK8Qr3Cv9K/8sASwDLAUsBywJLBEsLiwwLDIsNCw2LDcsOSxSLIcsiSyLLI0sjyyR +LJMslSy8LNks2yzdLN8s4SziLOQs/i0zLTUtNy05LTstPS0/LUEtWS12LXgtei18LX4tfy2BLZst0C3S +LdQt1i3YLdot3C3eLhcuNC42LjguOi48Lj0uPy5ZLo4ukC6SLpQuli6YLpounC62LtMu1S7XLtku2y7c +Lt4u+C8tLy8vMS8zLzUvNy85Lzsvgi+fL6EvpC+mL6gvqS+rL8UwEDAtMD8wSjBcMHEwfzCGMI8wkDCS +MJUwlzCZMJswnTCeMJ8wojCkMKkwsjC0MOUw7TEBMQwxGjEkMTExOTE7MT0xPzFEMUYxSzFNMU8xUTFT +MVUxYjFuMXExczF1MYgxlTGXMZkxmzGtMboxvDG+McAx0zHnMfAx9TH+MgAyCzIUMhYyITIjMiUyJzIp +MiwyWTJbMl0yXzJhMmMyZTJnMmkyazJ8MqkyqzKtMq8ysTKzMrUytzK5MrsyzjL7Mv0y/zMCMwQzBjMI +MwozDTMPMx4zSzNNM08zUjNUM1YzWDNaM10zXzN2M38zhDONM5ozrjO9M8Yz0zPhM/40ADQCNAU0CDQJ +NAs0JDRFNEc0STRMNE40UTRWNFg0XjR7NH00fzSCNIU0hjSINKI01zTZNNs03TTfNOI05DTnNQk1JjUo +NSo1LTUwNTE1MzVNNYI1hDWGNYg1ijWNNY81kjWrNcg1yjXMNc810jXTNdU17zYkNiY2KDYqNiw2LzYx +NjQ2ZzaENoY2iDaLNo42jzaRNqo20zbfNvU2/jcANwM3BTcINws3EDcRNxQ3FzdeN2U3cDd3N4M3izee +N6U3szfHN9k37TgAOAw4EzggODI4NTg4ODs4PjhAOEM4RjhJOEw4TzhQOFM4VThYOFs4XDhdOGo4cjh1 +OI44kTiUOJc4mjidOKA4ozimOKk4rDivOLI4yzjOONE41DjXONo43TjgOOM45jjpOOw47zj3OQY5IDlC +OVU5aTlwOX05hTmdOaw5wDndOeg59DoDOg06GTolOig6KTo8Oj06RjpLOmg6azp4OoU6iDqLOo46kDqd +OqA6oTqiOqs6sDq9OsY6yzrgOu068DrxOvI69Tr4OwE7DzsSOxQ7HTsiOys7SDtLO0w7XztgO2I7azty +O4g7kTuUO5c7pDunO6o7rTuxO7o7wTvVO+A79Tv3O/k7/Dv+PBQ8KTwrPC08MDwyPDw8WTxbPF48YTxk +PGU8ZzyBPMw8zTzPPNI81TzXPNo83TzePN884jzrPO09Hj0hPSM9JT0nPSk9Kz0uPTE9Pj1BPUQ9Rz1Q +PVI9Wz1dPWg9az1uPXE9dD13PaQ9pz2pPas9rT2vPbE9tD23PeQ95z3pPes97T3vPfE99D33PiQ+Jz4p +Piw+Lj4wPjI+NT44PmU+aD5qPm0+bz5xPnM+dj55PpY+mD6aPp0+oD6hPqM+vD7xPvM+9T73Pvk+/D7+ +PwE/Kz9IP0o/TD9PP1I/Uz9VP20/nj+gP6I/pT+oP6o/rT+7P8Q/xj/jP+U/5z/qP+0/7j/wQAdAKEAq +QCxAL0AxQDRANkA9QFpAXEBfQGJAZUBmQGhAgUC6QMJA2EDtQPhBA0EOQRxBOUFCQUVBSEFLQU5BV0Fg +QWVBZkFvQXBBeUF7QYxBjkGXQZpBpEGtQbZBw0HMQddB4EH9Qf9CAUIEQgdCCEIKQiNCREJGQkhCS0JN +QlBCUkJYQnVCd0J5QnxCf0KAQoJCm0LQQtJC1ELWQthC20LdQuBDBkMOQxdDIEMrQzBDQUNEQ0ZDSUNM +Q1pDY0NqQ4JDk0OVQ5dDmUOcQ65Dv0PBQ8NDxUPIQ9FD4kPkQ+ZD6EPrQ/pEC0QNRBBEEkQVREZEU0Rg +RG5EeESGRJNEnUSvRMNEzUTZRNtE3kThRORE6UTsRO9E8kT1RPhFE0UrRTRFNkU7RURFRkVJRUtFVkVv +RX1FmkWjRahFu0XDRdRF10XZRdtF3kXwRgFGA0YFRghGC0YcRi5GOUZIRktGTkZPRlJGW0ZdRoxGj0aS +RpVGmEabRp5GoUakRqdGqkatRrBGs0a2RrlGvEa/RsJGxUbIRstGzkbjRvtHDkckRztHV0dyR3lHkke3 +R9BH5Uf3SBRILUhKSFxIcEiNSKdIwEjcSOFI5EjtSPJI+0kCSRdJJEksST1JP0lBSUNJRklgSXFJc0l1 +SXdJekmLSY5JkEmSSZVJrkm/ScFJxEnGSclJ8kn1SfdJ+kn9SgBKAUoESgdKCkofSjxKP0pBSkRKR0pK +Sk1KZkqHSolKi0qOSpBKk0qYSppKpEq3SshKykrMSs5K0UrcSu1K70rxSvNK9ksDSxRLFksYSxpLHUs1 +S0ZLSEtKS0xLT0taS2tLbUtvS3FLdEuMS51Ln0uhS6RLp0u4S8xLz0vSS9VL2EvhS+NL5kvpTABMBUwI +TBlMG0wdTB9MIkwwTEFMQ0xGTEhMS0x8TH9MgkyFTIdMjEyPTJJMlUyYTJtMtkzITNFM00zcTN5M7Uzw +TPNM9kz5TPxM/00CTStNN006TT1NQE1BTURNR01ITUtNTk1XTVlNaE1rTW5NcU10TXdNek19TZlNtU3b +TfNOJ05ITmVOf06gTqhOsE64TsNOyE7LTs5O007UTuFO407lTuhO8U76TwFPDU8WTyFPLU9OT1FPU09W +T1lPWk9dT2BPeU+aT5xPnk+hT6RPp0+pT7pPvE/FT8hP3E/9UABQAlAFUAhQCVAMUA9QJ1BIUEpQTFBP +UFJQVVBaUFxRSVFaUVxRZVFnUWpRk1GWUZhRm1GeUaFRpFGlUahRq1GuUcBR1lH/UgRSBlIIUgtSDlIR +UhJSFVIXUixSPVI/UkFSYFKJUoxSjlKRUpRSl1KYUptSnlKhUrhS3VLfUuFS5FLnUupS7VLvUv5TMlNb +U15TYFNjU2ZTaVNqU21TcFNzU4pTr1OxU7NTtlO5U7xTv1PBU85UflSJVJRUoFSxVLNUtVS3VLpUz1Tg +VOJU5FTmVOlVA1UUVRZVGFUaVR1VMlVDVUVVR1VJVUxVXVVgVWJVZFVnVYFVklWUVZdVmVWcVbRVxVXH +VclVzFXPVexV9lYAVh9WIlYlVihWK1YuVjFWVlZcVnpWg1aKVqJWv1bCVsVWyFbLVs1W0Fb8VwRXJ1dM +V2JXbFdvV3JXdVd4V3tXfVeAV4NXoFepV7xXyVfMV9VX2FfbV95X4VfqV+1X8FfzV/ZYClgnWEJYYFhp +WIZYiViMWI9YkliVWJhYtFjRWNRY11jaWN1Y31jiWPxZGVkcWR9ZIlklWSdZKllHWUpZTVlQWVNZVVlY +WYBZoVm+WcFZxFnHWcpZzFnPWfpaHFpBWkRaR1pKWk1aUFpSWlVaWFplWmhacVp0Wndaelp9WoZaiVqM +Wo9aklqvWrJatVq4WrtavlrBWt5a4VrkWuda6lrsWu9bDFsPWxJbFVsYWxtbHltJW21biluNW5Bbk1uW +W5hbm1u4W7tbvlvBW8RbxlvJW+5b8Vv0W/db+lv9W/9cAlwFXBJcFVweXCFcJFwnXCpcM1w2XDlcPFw/ +XFxcX1xiXGVcaFxqXG1cilyNXJBck1yWXJlcnFy5XLxcv1zCXMVcx1zKXOdc6lztXPBc81z1XPhdFV0Y +XRtdHl0hXSNdJl1DXUZdSV1MXU9dUl1VXX9dol2/XcJdxV3IXctdzl3RXfdeFF45XjxeP15CXkVeSF5L +Xk5eUV5eXmFeal5tXnBec152Xn9egl6FXohei16oXqterl6xXrRetl65Xt5e4V7kXude6l7tXu9e8l71 +XwJfBV8OXxFfFF8XXxpfI18mXylfLF8vX0xfT19SX1VfWF9aX11fgl+FX4hfi1+OX5Ffk1+WX5lfyl/x +X/5gAWAKYA1gEGATYBZgH2AiYCVgKGArYEhgS2BOYFFgVGBWYFlgdmB5YHxgf2CCYIRgh2CtYMxg6WDs +YO9g8mD1YPdg+mEqYTZhWWF2YXlhfGF/YYJhhGGHYbRh2mH3Yfph/WIAYgNiBmIJYjBiUGJtYnBic2J2 +Ynlie2J+Yq5i1WL6Yv1jAGMDYwZjCWMLYw5jEWM9Y0pjTWNWY1ljXGNfY2Jja2NuY3FjdGN3Y5xjn2Oi +Y6VjqGOrY61jsGOzY9Bj2WPmY+lj8mP1Y/hj+2P+ZAdkCmQNZBBkE2QwZDNkNmQ5ZDxkPmRBZGVkgmSF +ZIhki2SOZJBkk2S2ZNJk72TyZPVk+GT7ZP5lAWUeZSFlJGUnZSplLGUvZWNlimWnZaplrWWwZbNltWW4 +ZeFl/mYBZgRmB2YKZg1mEGY9ZmNmgGaDZoZmiWaMZo9mkma3ZrpmvWbAZsNmxmbJZsxmz2bcZt9m6Gbr +Zu5m8Wb0Zv1nAGcDZwZnCWcuZzFnNGc3ZzpnPWdAZ0NnRmd2Z4NnhmePZ5JnlWeYZ5tnpGenZ6pnrWew +Z81n0GfTZ9Zn2WfcZ99oC2gqaEdoSmhNaFBoU2hWaFlodmh5aHxof2iCaIRoh2ixaM5o0WjUaNdo2mjd +aOBpCWkuaTFpNGk3aTppPWlAaUNpRmlTaVZpX2liaWVpaGlraXRpd2l6aX1pgGmdaaBpo2mmaalprGmv +adJp7moLag5qEWoUahdqGWocakpqZ2pqam1qcGpzanZqeWqaap1qoGqjaqZqqWqsaq9q2Wr8awlrDGsP +axJrFWsYazFrQ2tUa11rZGtna2lrbGtva4Jr1Gvda+Jr9mwHbApsDGwPbBJsM2xEbEdsSWxMbE9scmyD +bIZsiGyLbI5tL21AbUNtRm1JbUxt9m4HbgpuDW4QbhNu2G7hbuRv5W/ob+pv7W/wb/Jv9G/2b/hv+2/9 +b/9wAnAFcAhwCnANcA9wEnAVcBhwGnAdcCBwI3AlcChwKnAtcDBwM3A1cDdwOXA7cD5wQXBDcEVwR3BJ +cExwT3BRcFNwVnBZcFtwXXBfcGFwY3BlcGdwaXBrcG1wb3BycHVwd3B5cHtwfXB/cIJwhHCGcIlwjHCO +cJFwlHCWcJhwm3CdcJ9woXCjcKVwp3CpcKtwrnCxcLRwtnC4cLpwvHC+cMBww3DFcMdwynDMcM5w0XDU +cNdw2nDccN9w4XDjcOVw53DqcOxw7nDxcPRw93D6cPxw/nEAcQJxBHEGcQlxDHEPcRJxFHEXcSBxInEl +cTNxPHFBcUpxTXJOclByUnJUcldyWXJbcl1yX3JhcmNyZXJocmpybHJucnBycnJ1cnhye3J9coByg3KG +cohyinKMco5ykHKTcpVyl3KZcptynnKgcqJypHKmcqhyq3KucrBysnK0crdyuXK7cr1yv3LBcsNyxXLH +cslyy3LOctBy03LVctdy2XLbct1y33LhcuNy5nLpcuty7nLxcvNy9XL4cvpy/HL+cwBzAnMEcwZzCHML +cw5zEXMTcxVzF3MZcxtzHXMgcyJzJHMmcyhzKnMtczBzM3M2czhzOnM8cz5zQHNCc0RzRnNIc0pzTXNQ +c1NzVXNXc1lzW3Ndc19zYnNlc2dzanNsc29zeHN7dH50gXSDdIZ0iXSLdI10j3SRdJR0lnSYdJt0nnSh +dKN0pXSodKt0rnSxdLN0tnS5dLx0vnTBdMN0xnTJdMt0znTQdNJ01HTWdNl03HTedOB043TldOd06nTs +dO908XT0dPZ0+HT6dPx0/nUAdQJ1BHUGdQh1CnUNdRB1EnUUdRZ1GHUbdR11H3UidSR1J3UpdSx1L3Ux +dTN1NnU4dTp1PHU+dUB1QnVEdUZ1SXVMdU91UXVTdVV1V3VZdVt1XnVgdWJ1ZXVodWp1bHVvdXJ1dXV4 +dXp1fHV+dYB1gnWFdYd1inWNdY91knWVdZd1mXWbdZ11n3WhdaR1p3Wqda11sHWydbt1vnbBdsR2x3bK +ds120HbTdtZ22Xbcdt924nblduh263budvF29Hb3dvp2/XcAdwN3BncJdwx3D3cSdxV3GHcbdx53IXck +dyd3KnctdzB3M3c2dzl3PHc/d0J3RXdId0t3TndRd1R3V3dad113YHdjd2Z3aXdsd293cnd1d3h3e3d+ +d4F3hHeHd4p3jXeQd5N3lneZd5x3n3eid6V3qHerd653sXe0d7d3une9d8B3w3fGd8l3zHfPd9J31XfY +d9t33nfhd+R353fqd+138Hfzd/Z3+Xf8d/94AngFeAh4C3gOeBF4FHgXeBp4HXggeCN4JngpeCx4L3gy +eDV4OHg7eD54QXhyeJF4tnjueRt5RXlhecN5zHo8ell6X3pzeoZ6q3rBet97GntPe2V7mHuwe+V8EHw+ +fGF8f3yVfKJ8rnzOfPR9F31DfXB9on3Vffh+FX49fpJ/m3/6gA6AcYCJgKmAz4EagTiBXYFpgXWBg4Gs +gdmB5oHxgiWCUYJ3gpuCs4LIgt6DBoMfg2ODkoO+g/+EGYQnhIWErITBhMiE9YT8hRCFW4V+hZeFrYW/ +hfKGPYaEhqaG2Yb6h3OHtofhiACIDIgbiDWIQYh2iJCIy4kKiTCJTomNiaGJqInfifOKEYooilCKa4t8 +i6GLv4wQjIWMro0ijVONhI26jeqOC45AjkmOTI5XjlmOXI5fjmKOZY5ujnGOfI5+joGOhI6HjoqOk46W +kEeQSpBNkE+QUpBVkFiQWpBdkGCQY5BmkGmQbJBvkHGQdJB3kHmQfJB/kIGQhJCHkIqQjZCPkJGQlJCX +kJmQm5CekKGQpJCnkKqQrZCwkLOQtpC4kLuQvpDAkMOQxZDIkMuQzpDQkNKQ1ZDXkNqQ3JDekOGQ45Dm +kOiQ6pDskO+Q8pD1kPiQ+pD9kQCRApEEkQaRCJEKkQ2REJETkRaRGJEakR2RIJEjkSaRKZErkS6RMZE0 +kTaROZE8kT+RQpFEkUeRSZFLkU6RUZFUkVeRWZFckV+RYpFlkWiRa5FtkXCRc5F2kXmRe5F9kX+RgpGE +kYeRiZGMkY+RkpGVkZeRmpGdkaCRo5GmkamRrJGvkbKRtZG4kbqRvJG/kcKRxJHHkcqRzZHPkdGR1JHX +kdqR3JHekeGR45HlkeeR6pHske6R8JHzkfWR95H6kf2R/5IBkgSSB5IJkgySD5ISkhWSGJIbkh2SH5Ih +kiOSJZIokiqSLZIwkjKSNJI3kjqSPJI+kkGSRJJHkkqSTZJQklOSVpJZklySXpJhkmOSZZJnkmmSbJJv +knGSc5J2kniSe5J+koeSipQ7lD6UQZRElEeUSpRNlFCUU5RWlFmUXJRflGKUZZRolGuUbpRxlHSUd5R6 +lH2UgJSDlIaUiZSMlI+UkpSVlJiUm5SelKGUpJSnlKqUrZSwlLOUtpS5lLyUv5TClMWUyJTLlM6U0ZTU +lNeU2pTdlOCU45TmlOmU7JTvlPKU9ZT4lPuU/pUBlQSVB5UKlQ2VEJUTlRaVGZUclR+VIpUllSiVK5Uu +lTGVNJU3lTqVPZVAlUOVRpVJlUyVT5VSlVWVWJVblV6VYZVklWeVapVtlXCVc5V2lXmVfJV/lYKVhZWI +lYuVjpWRlZSVl5WalZ2VoJWjlaaVqZWsla+VspW1lbiVu5W+lcGVxJXHlcqVzZXQldOV1pXZldyV35Xi +leWV6JXrle6V8ZX0lfeV+pX9lgCWA5YGlgmWDJYPlhKWFZYYlhuWHpYhliSWJ5Yqli2WMJYzljaWOZY8 +lj+WQpZFlkiWS5ZOllGWVJZXllqWXZZglmOWZpZplmyWb5ZylnWWeJZ7ln6WgZaEloeWipaNlpCWk5aW +lpmWnJaflqKWpZaolquWrpaxlrSWt5a6lr2WwJbDlsaWyJbKls2Wz5bUlteW3JbhluSW55bpluuW7pbz +lviW+5b+lwGXBpcJlw6XE5cYlxuXHpcglyOXJZcolyuXLpcxlzOXNpc5lz6XQJdFl0iXSpdPl1SXV5dZ +l16XY5dll2iXa5dul3OXdZd3l3qXf5eBl4aXi5eOl5GXlJeWl5uXoJejl6iXq5etl7CXs5e2l7mXu5e9 +l7+XwZfEl8eXypfNl9KX1ZfYl9uX3pfhl+aX65fwl/OX9pf4l/uX/pgAmAWYCpgPmBGYFJgXmBqYH5gi +mCWYKJgtmDKYN5g6mD+YRJhJmE6YUJhTmFWYWJhamFyYX5hkmGeYaphtmHKYdZh4mHuYfpiDmIaYi5iQ +mJKYlJiWmJmYnJifmKSYp5ismK+Yspi0mLmYvJjBmMaYy5jOmNOY1ZjamNyY3pjhmOSY55jsmO6Y8Zj0 +mPeY/JkBmQaZCZkMmRGZFJkXmRqZH5kimSSZJ5kqmS+ZMpk3mTyZP5lBmUSZTZlSmVeZWplfmWSZZ5ls +mXGZdJl5mXyZfpmDmYaZi5mOmZGZlJmZmZyZn5momaqZq5m0mbeZuJnBmcSZxZnOmdMAAAAAAAACAgAA +AAAAABCOAAAAAAAAAAAAAAAAAACZ4g + + + diff --git a/Lock_White.tif b/Lock_White.tif new file mode 100644 index 0000000..959ae25 Binary files /dev/null and b/Lock_White.tif differ diff --git a/MainMenu.nib/designable.nib b/MainMenu.nib/designable.nib new file mode 100644 index 0000000..475a197 --- /dev/null +++ b/MainMenu.nib/designable.nib @@ -0,0 +1,219 @@ + + + + 1080 + 14D109a + 6751 + 1347.49 + 758.60 + + com.apple.InterfaceBuilder.CocoaPlugin + 6751 + + + NSCustomObject + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + TPApplication + + + FirstResponder + + + NSApplication + + + TPMainController + + + SUUpdater + + + + + + + delegate + + + + 306 + + + + delegate + + + + 262 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + 259 + + + TPMainController + + + -3 + + + Application + + + 307 + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 307 + + + + + TPApplication + NSApplication + + IBProjectSource + ../TPApplication.h + + + + TPMainController + NSObject + + IBProjectSource + ../TPMainController.h + + + + + + NSApplication + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSApplication.h + + + + NSMenu + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenu.h + + + + NSObject + + invokeExtensionService: + id + + + invokeExtensionService: + + invokeExtensionService: + id + + + + IBFrameworkSource + AppKit.framework/PrivateHeaders/NSToolbar_Private.h + + + + NSResponder + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSResponder.h + + + + SUUpdater + NSObject + + checkForUpdates: + id + + + checkForUpdates: + + checkForUpdates: + id + + + + delegate + id + + + delegate + + delegate + id + + + + IBFrameworkSource + Sparkle.framework/Headers/SUUpdater.h + + + + + 0 + IBCocoaFramework + NO + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + 3 + + diff --git a/MainMenu.nib/keyedobjects.nib b/MainMenu.nib/keyedobjects.nib new file mode 100644 index 0000000..b3516b3 Binary files /dev/null and b/MainMenu.nib/keyedobjects.nib differ diff --git a/MultipleFiles.icns b/MultipleFiles.icns new file mode 100644 index 0000000..9bc9e45 Binary files /dev/null and b/MultipleFiles.icns differ diff --git a/PTHotKeysLib/res/French.lproj/PTKeyCodes.plist b/PTHotKeysLib/res/French.lproj/PTKeyCodes.plist new file mode 100644 index 0000000..40d44b6 --- /dev/null +++ b/PTHotKeysLib/res/French.lproj/PTKeyCodes.plist @@ -0,0 +1,103 @@ +{ + 0 = "A"; + 1 = "S"; + 2 = "D"; + 3 = "F"; + 4 = "H"; + 5 = "G"; + 6 = "Z"; + 7 = "X"; + 8 = "C"; + 9 = "V"; + 10 = "$"; + 11 = "B"; + 12 = "Q"; + 13 = "W"; + 14 = "E"; + 15 = "R"; + 16 = "Y"; + 17 = "T"; + 18 = "1"; + 19 = "2"; + 20 = "3"; + 21 = "4"; + 22 = "6"; + 23 = "5"; + 24 = "="; + 25 = "9"; + 26 = "7"; + 27 = "-"; + 28 = "8"; + 29 = "0"; + 30 = "\]"; + 31 = "O"; + 32 = "U"; + 33 = "\["; + 34 = "I"; + 35 = "P"; + 36 = "Return"; + 37 = "L"; + 38 = "J"; + 39 = "\'"; + 40 = "K"; + 41 = ";"; + 42 = "\\"; + 43 = ","; + 44 = "\/"; + 45 = "N"; + 46 = "M"; + 47 = "."; + 48 = "Tab"; + 49 = "Space"; + 50 = "\`"; + 51 = "Delete"; + 53 = "ESC"; + 55 = "Command"; + 56 = "Shift"; + 57 = "Caps Lock"; + 58 = "Option"; + 59 = "Control"; + 65 = "Pad ."; + 67 = "Pad *"; + 69 = "Pad +"; + 71 = "Clear"; + 75 = "Pad /"; + 76 = "Pad Enter"; + 78 = "Pad -"; + 81 = "Pad ="; + 82 = "Pad 0"; + 83 = "Pad 1"; + 84 = "Pad 2"; + 85 = "Pad 3"; + 86 = "Pad 4"; + 87 = "Pad 5"; + 88 = "Pad 6"; + 89 = "Pad 7"; + 91 = "Pad 8"; + 92 = "Pad 9"; + 96 = "F5"; + 97 = "F6"; + 98 = "F7"; + 99 = "F3"; + 100 = "F8"; + 101 = "F9"; + 103 = "F11"; + 105 = "F13"; + 107 = "F14"; + 109 = "F10"; + 111 = "F12"; + 113 = "F15"; + 114 = "Ins"; + 115 = "Home"; + 116 = "Page Up"; + 117 = "Del"; + 118 = "F4"; + 119 = "End"; + 120 = "F2"; + 121 = "Page Down"; + 122 = "F1"; + 123 = "Left Arrow"; + 124 = "Right Arrow"; + 125 = "Down Arrow"; + 126 = "Up Arrow"; +} \ No newline at end of file diff --git a/PTHotKeysLib/res/German.lproj/PTKeyCodes.plist b/PTHotKeysLib/res/German.lproj/PTKeyCodes.plist new file mode 100755 index 0000000..40d44b6 --- /dev/null +++ b/PTHotKeysLib/res/German.lproj/PTKeyCodes.plist @@ -0,0 +1,103 @@ +{ + 0 = "A"; + 1 = "S"; + 2 = "D"; + 3 = "F"; + 4 = "H"; + 5 = "G"; + 6 = "Z"; + 7 = "X"; + 8 = "C"; + 9 = "V"; + 10 = "$"; + 11 = "B"; + 12 = "Q"; + 13 = "W"; + 14 = "E"; + 15 = "R"; + 16 = "Y"; + 17 = "T"; + 18 = "1"; + 19 = "2"; + 20 = "3"; + 21 = "4"; + 22 = "6"; + 23 = "5"; + 24 = "="; + 25 = "9"; + 26 = "7"; + 27 = "-"; + 28 = "8"; + 29 = "0"; + 30 = "\]"; + 31 = "O"; + 32 = "U"; + 33 = "\["; + 34 = "I"; + 35 = "P"; + 36 = "Return"; + 37 = "L"; + 38 = "J"; + 39 = "\'"; + 40 = "K"; + 41 = ";"; + 42 = "\\"; + 43 = ","; + 44 = "\/"; + 45 = "N"; + 46 = "M"; + 47 = "."; + 48 = "Tab"; + 49 = "Space"; + 50 = "\`"; + 51 = "Delete"; + 53 = "ESC"; + 55 = "Command"; + 56 = "Shift"; + 57 = "Caps Lock"; + 58 = "Option"; + 59 = "Control"; + 65 = "Pad ."; + 67 = "Pad *"; + 69 = "Pad +"; + 71 = "Clear"; + 75 = "Pad /"; + 76 = "Pad Enter"; + 78 = "Pad -"; + 81 = "Pad ="; + 82 = "Pad 0"; + 83 = "Pad 1"; + 84 = "Pad 2"; + 85 = "Pad 3"; + 86 = "Pad 4"; + 87 = "Pad 5"; + 88 = "Pad 6"; + 89 = "Pad 7"; + 91 = "Pad 8"; + 92 = "Pad 9"; + 96 = "F5"; + 97 = "F6"; + 98 = "F7"; + 99 = "F3"; + 100 = "F8"; + 101 = "F9"; + 103 = "F11"; + 105 = "F13"; + 107 = "F14"; + 109 = "F10"; + 111 = "F12"; + 113 = "F15"; + 114 = "Ins"; + 115 = "Home"; + 116 = "Page Up"; + 117 = "Del"; + 118 = "F4"; + 119 = "End"; + 120 = "F2"; + 121 = "Page Down"; + 122 = "F1"; + 123 = "Left Arrow"; + 124 = "Right Arrow"; + 125 = "Down Arrow"; + 126 = "Up Arrow"; +} \ No newline at end of file diff --git a/PTHotKeysLib/res/Japanese.lproj/PTKeyCodes.plist b/PTHotKeysLib/res/Japanese.lproj/PTKeyCodes.plist new file mode 100644 index 0000000..40d44b6 --- /dev/null +++ b/PTHotKeysLib/res/Japanese.lproj/PTKeyCodes.plist @@ -0,0 +1,103 @@ +{ + 0 = "A"; + 1 = "S"; + 2 = "D"; + 3 = "F"; + 4 = "H"; + 5 = "G"; + 6 = "Z"; + 7 = "X"; + 8 = "C"; + 9 = "V"; + 10 = "$"; + 11 = "B"; + 12 = "Q"; + 13 = "W"; + 14 = "E"; + 15 = "R"; + 16 = "Y"; + 17 = "T"; + 18 = "1"; + 19 = "2"; + 20 = "3"; + 21 = "4"; + 22 = "6"; + 23 = "5"; + 24 = "="; + 25 = "9"; + 26 = "7"; + 27 = "-"; + 28 = "8"; + 29 = "0"; + 30 = "\]"; + 31 = "O"; + 32 = "U"; + 33 = "\["; + 34 = "I"; + 35 = "P"; + 36 = "Return"; + 37 = "L"; + 38 = "J"; + 39 = "\'"; + 40 = "K"; + 41 = ";"; + 42 = "\\"; + 43 = ","; + 44 = "\/"; + 45 = "N"; + 46 = "M"; + 47 = "."; + 48 = "Tab"; + 49 = "Space"; + 50 = "\`"; + 51 = "Delete"; + 53 = "ESC"; + 55 = "Command"; + 56 = "Shift"; + 57 = "Caps Lock"; + 58 = "Option"; + 59 = "Control"; + 65 = "Pad ."; + 67 = "Pad *"; + 69 = "Pad +"; + 71 = "Clear"; + 75 = "Pad /"; + 76 = "Pad Enter"; + 78 = "Pad -"; + 81 = "Pad ="; + 82 = "Pad 0"; + 83 = "Pad 1"; + 84 = "Pad 2"; + 85 = "Pad 3"; + 86 = "Pad 4"; + 87 = "Pad 5"; + 88 = "Pad 6"; + 89 = "Pad 7"; + 91 = "Pad 8"; + 92 = "Pad 9"; + 96 = "F5"; + 97 = "F6"; + 98 = "F7"; + 99 = "F3"; + 100 = "F8"; + 101 = "F9"; + 103 = "F11"; + 105 = "F13"; + 107 = "F14"; + 109 = "F10"; + 111 = "F12"; + 113 = "F15"; + 114 = "Ins"; + 115 = "Home"; + 116 = "Page Up"; + 117 = "Del"; + 118 = "F4"; + 119 = "End"; + 120 = "F2"; + 121 = "Page Down"; + 122 = "F1"; + 123 = "Left Arrow"; + 124 = "Right Arrow"; + 125 = "Down Arrow"; + 126 = "Up Arrow"; +} \ No newline at end of file diff --git a/PTHotKeysLib/res/KeyCodes.plist b/PTHotKeysLib/res/KeyCodes.plist new file mode 100644 index 0000000..8658916 --- /dev/null +++ b/PTHotKeysLib/res/KeyCodes.plist @@ -0,0 +1,96 @@ + + + + + padKeyString + Pad + padKeys + + 65 + 67 + 69 + 75 + 78 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 91 + 92 + + unmappedKeys + + 100 + F8 + 101 + F9 + 103 + F11 + 105 + F13 + 107 + F14 + 109 + F10 + 111 + F12 + 113 + F15 + 114 + Ins + 115 + Home + 116 + Page Up + 117 + Del + 118 + F4 + 119 + End + 120 + F2 + 121 + Page Down + 122 + F1 + 123 + Left Arrow + 124 + Right Arrow + 125 + Down Arrow + 126 + Up Arrow + 36 + Return + 48 + Tab + 49 + Space + 51 + Backspace + 53 + ESC + 71 + Clear + 76 + Pad Enter + 96 + F5 + 97 + F6 + 98 + F7 + 99 + F3 + + version + 1 + + diff --git a/PTHotKeysLib/res/PTKeyboardIcon.tiff b/PTHotKeysLib/res/PTKeyboardIcon.tiff new file mode 100644 index 0000000..b979bda Binary files /dev/null and b/PTHotKeysLib/res/PTKeyboardIcon.tiff differ diff --git a/PTHotKeysLib/res/key.png b/PTHotKeysLib/res/key.png new file mode 100644 index 0000000..b494e90 Binary files /dev/null and b/PTHotKeysLib/res/key.png differ diff --git a/PTHotKeysLib/src/PTHotKey.h b/PTHotKeysLib/src/PTHotKey.h new file mode 100644 index 0000000..7b0bb96 --- /dev/null +++ b/PTHotKeysLib/src/PTHotKey.h @@ -0,0 +1,39 @@ +// +// PTHotKey.h +// Protein +// +// Created by Quentin Carnicelli on Sat Aug 02 2003. +// Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. +// + +#import + +#import "PTKeyCombo.h" + +@interface PTHotKey : NSObject +{ + int mID; + NSString* mIdentifier; + NSString* mName; + PTKeyCombo* mKeyCombo; + id mTarget; + SEL mAction; +} + +- (instancetype)initWithIdentifier: (id)identifier keyCombo: (PTKeyCombo*)combo NS_DESIGNATED_INITIALIZER; +- (instancetype)init; + +@property (nonatomic, strong) id identifier; + +@property (nonatomic, readonly) int ID; + +@property (nonatomic, copy) NSString *name; + +@property (nonatomic, copy) PTKeyCombo *keyCombo; + +@property (nonatomic, unsafe_unretained) id target; +@property (nonatomic) SEL action; + +- (void)invoke; + +@end diff --git a/PTHotKeysLib/src/PTHotKey.m b/PTHotKeysLib/src/PTHotKey.m new file mode 100644 index 0000000..237f809 --- /dev/null +++ b/PTHotKeysLib/src/PTHotKey.m @@ -0,0 +1,109 @@ +// +// PTHotKey.m +// Protein +// +// Created by Quentin Carnicelli on Sat Aug 02 2003. +// Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. +// + +#import "PTHotKey.h" + +#import "PTHotKeyCenter.h" +#import "PTKeyCombo.h" + +static int HotKeyID = 0; + +@implementation PTHotKey + +- (instancetype)init +{ + return [self initWithIdentifier: nil keyCombo: nil]; +} + + +- (instancetype)initWithIdentifier: (id)identifier keyCombo: (PTKeyCombo*)combo +{ + self = [super init]; + + if( self ) + { + [self setIdentifier: identifier]; + [self setKeyCombo: combo]; + mID = ++HotKeyID; + } + + return self; +} + + +- (NSString*)description +{ + return [NSString stringWithFormat: @"<%@: %@, %@>", NSStringFromClass( [self class] ), [self identifier], [self keyCombo]]; +} + +#pragma mark - + +- (void)setIdentifier: (id)ident +{ + mIdentifier = ident; +} + +- (id)identifier +{ + return mIdentifier; +} + +- (int)ID +{ + return mID; +} + +- (void)setKeyCombo: (PTKeyCombo*)combo +{ + if( combo == nil ) + combo = [PTKeyCombo clearKeyCombo]; + + mKeyCombo = combo; +} + +- (PTKeyCombo*)keyCombo +{ + return mKeyCombo; +} + +- (void)setName: (NSString*)name +{ + mName = name; +} + +- (NSString*)name +{ + return mName; +} + +- (void)setTarget: (id)target +{ + mTarget = target; +} + +- (id)target +{ + return mTarget; +} + +- (void)setAction: (SEL)action +{ + mAction = action; +} + +- (SEL)action +{ + return mAction; +} + +- (void)invoke +{ + [mTarget performSelector: mAction withObject: self]; +} + +@end diff --git a/PTHotKeysLib/src/PTHotKeyCenter.h b/PTHotKeysLib/src/PTHotKeyCenter.h new file mode 100644 index 0000000..7d5e957 --- /dev/null +++ b/PTHotKeysLib/src/PTHotKeyCenter.h @@ -0,0 +1,32 @@ +// +// PTHotKeyCenter.h +// Protein +// +// Created by Quentin Carnicelli on Sat Aug 02 2003. +// Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. +// +// Contributers: +// Quentin D. Carnicelli +// Finlay Dobbie +// Vincent Pottier + +#import + +#import "PTHotKey.h" + +@interface PTHotKeyCenter : NSObject +{ + NSMutableDictionary* mHotKeys; //Keys are NSValue of EventHotKeyRef + BOOL mEventHandlerInstalled; +} + ++ (PTHotKeyCenter*)sharedCenter; + +- (BOOL)registerHotKey: (PTHotKey*)hotKey; +- (void)unregisterHotKey: (PTHotKey*)hotKey; + +@property (nonatomic, readonly, copy) NSArray *allHotKeys; +- (PTHotKey*)hotKeyWithIdentifier: (id)ident; +- (PTHotKey*)hotKeyWithID: (int)ID; + +@end diff --git a/PTHotKeysLib/src/PTHotKeyCenter.m b/PTHotKeysLib/src/PTHotKeyCenter.m new file mode 100644 index 0000000..2edcc8c --- /dev/null +++ b/PTHotKeysLib/src/PTHotKeyCenter.m @@ -0,0 +1,262 @@ +// +// PTHotKeyCenter.m +// Protein +// +// Created by Quentin Carnicelli on Sat Aug 02 2003. +// Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. +// + +#import "PTHotKeyCenter.h" +#import "PTHotKey.h" +#import "PTKeyCombo.h" +#import + +#if __PROTEIN__ +#import "PTNSObjectAdditions.h" +#endif + +@interface PTHotKeyCenter (Private) +@property (nonatomic, readonly) BOOL _hasCarbonEventSupport; + +- (PTHotKey*)_hotKeyForCarbonHotKey: (EventHotKeyRef)carbonHotKey; +- (EventHotKeyRef)_carbonHotKeyForHotKey: (PTHotKey*)hotKey; + +- (void)_updateEventHandler; +- (void)_hotKeyDown: (PTHotKey*)hotKey; +- (void)_hotKeyUp: (PTHotKey*)hotKey; +static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* refCon ); +@end + +@implementation PTHotKeyCenter + +static PTHotKeyCenter* _sharedHotKeyCenter = nil; + ++ (PTHotKeyCenter*)sharedCenter +{ + if( _sharedHotKeyCenter == nil ) + { + _sharedHotKeyCenter = [[self alloc] init]; + #if __PROTEIN__ + [_sharedHotKeyCenter releaseOnTerminate]; + #endif + } + + return _sharedHotKeyCenter; +} + +- (instancetype)init +{ + self = [super init]; + + if( self ) + { + mHotKeys = [[NSMutableDictionary alloc] init]; + } + + return self; +} + + +#pragma mark - + +- (BOOL)registerHotKey: (PTHotKey*)hotKey +{ + OSStatus err; + EventHotKeyID hotKeyID; + EventHotKeyRef carbonHotKey; + NSValue* key; + + if( [[self allHotKeys] containsObject: hotKey] == YES ) + [self unregisterHotKey: hotKey]; + + if( [[hotKey keyCombo] isValidHotKeyCombo] == NO ) + return YES; + + hotKeyID.signature = 'PTHk'; + hotKeyID.id = [hotKey ID]; + + err = RegisterEventHotKey( [[hotKey keyCombo] keyCode], + [[hotKey keyCombo] modifiers], + hotKeyID, + GetEventDispatcherTarget(), + 0, + &carbonHotKey ); + + if( err ) + return NO; + + key = [NSValue valueWithPointer: carbonHotKey]; + if( hotKey && key ) + mHotKeys[key] = hotKey; + + [self _updateEventHandler]; + + return YES; +} + +- (void)unregisterHotKey: (PTHotKey*)hotKey +{ + OSStatus err; + EventHotKeyRef carbonHotKey; + NSValue* key; + + if( [[self allHotKeys] containsObject: hotKey] == NO ) + return; + + carbonHotKey = [self _carbonHotKeyForHotKey: hotKey]; + NSAssert( carbonHotKey != nil, @"" ); + + err = UnregisterEventHotKey( carbonHotKey ); + //Watch as we ignore 'err': + + key = [NSValue valueWithPointer: carbonHotKey]; + [mHotKeys removeObjectForKey: key]; + + [self _updateEventHandler]; + + //See that? Completely ignored +} + +- (NSArray*)allHotKeys +{ + return [mHotKeys allValues]; +} + +- (PTHotKey*)hotKeyWithIdentifier: (id)ident +{ + NSEnumerator* hotKeysEnum = [[self allHotKeys] objectEnumerator]; + PTHotKey* hotKey; + + if( !ident ) + return nil; + + while( (hotKey = [hotKeysEnum nextObject]) != nil ) + { + if( [[hotKey identifier] isEqual: ident] ) + return hotKey; + } + + return nil; +} + +- (PTHotKey*)hotKeyWithID: (int)ID +{ + NSEnumerator* hotKeysEnum = [[self allHotKeys] objectEnumerator]; + PTHotKey* hotKey; + + if( ID == 0 ) + return nil; + + while( (hotKey = [hotKeysEnum nextObject]) != nil ) + { + if( [hotKey ID] == ID ) + return hotKey; + } + + return nil; +} + +#pragma mark - + +- (BOOL)_hasCarbonEventSupport +{ + return floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_1; +} + +- (PTHotKey*)_hotKeyForCarbonHotKey: (EventHotKeyRef)carbonHotKey +{ + NSValue* key = [NSValue valueWithPointer: carbonHotKey]; + return mHotKeys[key]; +} + +- (EventHotKeyRef)_carbonHotKeyForHotKey: (PTHotKey*)hotKey +{ + NSArray* values; + NSValue* value; + + values = [mHotKeys allKeysForObject: hotKey]; + NSAssert( [values count] == 1, @"Failed to find Carbon Hotkey for PTHotKey" ); + + value = [values lastObject]; + + return (EventHotKeyRef)[value pointerValue]; +} + +- (void)_updateEventHandler +{ + if( [self _hasCarbonEventSupport] == NO ) //Don't use event handler on these systems + return; + + if( [mHotKeys count] && mEventHandlerInstalled == NO ) + { + EventTypeSpec eventSpec[2] = { + { kEventClassKeyboard, kEventHotKeyPressed }, + { kEventClassKeyboard, kEventHotKeyReleased } + }; + + InstallEventHandler( GetEventDispatcherTarget(), + (EventHandlerProcPtr)hotKeyEventHandler, + 2, eventSpec, nil, nil); + + mEventHandlerInstalled = YES; + } +} + +- (void)_hotKeyDown: (PTHotKey*)hotKey +{ + [hotKey invoke]; +} + +- (void)_hotKeyUp: (PTHotKey*)hotKey +{ +} + +- (OSStatus)sendCarbonEvent: (EventRef)event +{ + OSStatus err; + EventHotKeyID hotKeyID; + PTHotKey* hotKey; + + NSAssert( [self _hasCarbonEventSupport], @"" ); + NSAssert( GetEventClass( event ) == kEventClassKeyboard, @"Unknown event class" ); + + err = GetEventParameter( event, + kEventParamDirectObject, + typeEventHotKeyID, + nil, + sizeof(EventHotKeyID), + nil, + &hotKeyID ); + if( err ) + return err; + + + NSAssert( hotKeyID.signature == 'PTHk', @"Invalid hot key id" ); + NSAssert( hotKeyID.id != 0, @"Invalid hot key id" ); + + hotKey = [self hotKeyWithID:hotKeyID.id]; + + switch( GetEventKind( event ) ) + { + case kEventHotKeyPressed: + [self _hotKeyDown: hotKey]; + break; + + case kEventHotKeyReleased: + [self _hotKeyUp: hotKey]; + break; + + default: + NSAssert( 0, @"Unknown event kind" ); + break; + } + + return noErr; +} + +static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* refCon ) +{ + return [[PTHotKeyCenter sharedCenter] sendCarbonEvent: inEvent]; +} + +@end diff --git a/PTHotKeysLib/src/PTKeyBroadcaster.h b/PTHotKeysLib/src/PTKeyBroadcaster.h new file mode 100644 index 0000000..8dedc28 --- /dev/null +++ b/PTHotKeysLib/src/PTKeyBroadcaster.h @@ -0,0 +1,24 @@ +// +// PTKeyBroadcaster.h +// Protein +// +// Created by Quentin Carnicelli on Sun Aug 03 2003. +// Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. +// + +#import +#if __PROTEIN__ +#import "ProteinDefines.h" +#else +#define PROTEIN_EXPORT __private_extern__ +#endif + +@interface PTKeyBroadcaster : NSButton +{ +} + ++ (long)cocoaModifiersAsCarbonModifiers: (long)cocoaModifiers; + +@end + +NSString* PTKeyBroadcasterKeyEvent; //keys: keyCombo as PTKeyCombo diff --git a/PTHotKeysLib/src/PTKeyBroadcaster.m b/PTHotKeysLib/src/PTKeyBroadcaster.m new file mode 100644 index 0000000..e4643e2 --- /dev/null +++ b/PTHotKeysLib/src/PTKeyBroadcaster.m @@ -0,0 +1,93 @@ +// +// PTKeyBroadcaster.m +// Protein +// +// Created by Quentin Carnicelli on Sun Aug 03 2003. +// Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. +// + +#import "PTKeyBroadcaster.h" +#import "PTKeyCombo.h" +#import + +NSString* PTKeyBroadcasterKeyEvent = @"PTKeyBroadcasterKeyEvent"; + +@implementation PTKeyBroadcaster + +- (void)_bcastKeyCode: (short)keyCode modifiers: (long)modifiers +{ + PTKeyCombo* keyCombo = [PTKeyCombo keyComboWithKeyCode: keyCode modifiers: modifiers]; + NSDictionary* userInfo = @{@"keyCombo": keyCombo}; + + [[NSNotificationCenter defaultCenter] + postNotificationName: PTKeyBroadcasterKeyEvent + object: self + userInfo: userInfo]; +} + +- (BOOL)_bcastEvent: (NSEvent*)event +{ + NSString * string = [event charactersIgnoringModifiers]; + long modifiers = [event modifierFlags]; + + if([string length] > 0) { + unichar c = [string characterAtIndex:0]; + switch(c) { + case '\n': + case '\r': + case 3: // enter + case 27: // escape + if((modifiers & NSDeviceIndependentModifierFlagsMask) == 0) { + return NO; + } + default: + break; + } + } + + short keyCode = [event keyCode]; + + modifiers = [[self class] cocoaModifiersAsCarbonModifiers: modifiers]; + + [self _bcastKeyCode: keyCode modifiers: modifiers]; + + return YES; +} + +- (void)keyDown: (NSEvent*)event +{ + if(![self _bcastEvent: event]) { + [super keyDown:event]; + } +} + +- (BOOL)performKeyEquivalent: (NSEvent*)event +{ + [self _bcastEvent: event]; + return YES; +} + ++ (long)cocoaModifiersAsCarbonModifiers: (long)cocoaModifiers +{ + static long cocoaToCarbon[6][2] = + { + { NSCommandKeyMask, cmdKey}, + { NSAlternateKeyMask, optionKey}, + { NSControlKeyMask, controlKey}, + { NSShiftKeyMask, shiftKey}, + { NSFunctionKeyMask, rightControlKey}, + //{ NSAlphaShiftKeyMask, alphaLock }, //Ignore this? + }; + + long carbonModifiers = 0; + int i; + + for( i = 0 ; i < 6; i++ ) + if( cocoaModifiers & cocoaToCarbon[i][0] ) + carbonModifiers += cocoaToCarbon[i][1]; + + return carbonModifiers; +} + + +@end diff --git a/PTHotKeysLib/src/PTKeyCodeTranslator.h b/PTHotKeysLib/src/PTKeyCodeTranslator.h new file mode 100644 index 0000000..4a07c13 --- /dev/null +++ b/PTHotKeysLib/src/PTKeyCodeTranslator.h @@ -0,0 +1,35 @@ +// +// PTKeyCodeTranslator.h +// Chercher +// +// Created by Finlay Dobbie on Sat Oct 11 2003. +// Copyright (c) 2003 Cliché Software. All rights reserved. +// + +#import +#include +#include + +@interface PTKeyCodeTranslator : NSObject +{ +#if LEGACY_BUILD + KeyboardLayoutRef keyboardLayout; + UCKeyboardLayout *uchrData; + void *KCHRData; + SInt32 keyLayoutKind; + UInt32 keyTranslateState; + UInt32 deadKeyState; +#else + TISInputSourceRef keyboardLayout; + const UCKeyboardLayout *uchrData; + + UInt32 keyTranslateState; + UInt32 deadKeyState; +#endif +} + ++ (id)currentTranslator; + +- (NSString *)translateKeyCode:(short)keyCode; + +@end diff --git a/PTHotKeysLib/src/PTKeyCodeTranslator.m b/PTHotKeysLib/src/PTKeyCodeTranslator.m new file mode 100644 index 0000000..d6b90cb --- /dev/null +++ b/PTHotKeysLib/src/PTKeyCodeTranslator.m @@ -0,0 +1,146 @@ +// +// PTKeyCodeTranslator.m +// Chercher +// +// Created by Finlay Dobbie on Sat Oct 11 2003. +// Copyright (c) 2003 Cliché Software. All rights reserved. +// + +#import "PTKeyCodeTranslator.h" + +@interface PTKeyCodeTranslator (Internal) + +#if LEGACY_BUILD +- (id)initWithKeyboardLayout:(KeyboardLayoutRef)aLayout; +- (KeyboardLayoutRef)keyboardLayout; +#else +- (instancetype)initWithKeyboardLayout:(TISInputSourceRef)inputSourceRef; +@property (nonatomic, readonly) TISInputSourceRef keyboardLayout; +#endif + +@end + +@implementation PTKeyCodeTranslator + +#if LEGACY_BUILD + ++ (id)currentTranslator +{ + + static PTKeyCodeTranslator *current = nil; + KeyboardLayoutRef currentLayout; + OSStatus err = KLGetCurrentKeyboardLayout( ¤tLayout ); + if (err != noErr) return nil; + + if (current == nil) { + current = [[PTKeyCodeTranslator alloc] initWithKeyboardLayout:currentLayout]; + } else if ([current keyboardLayout] != currentLayout) { + [current release]; + current = [[PTKeyCodeTranslator alloc] initWithKeyboardLayout:currentLayout]; + } + return current; +} + +- (id)initWithKeyboardLayout:(KeyboardLayoutRef)aLayout +{ + if ((self = [super init]) != nil) { + OSStatus err; + keyboardLayout = aLayout; + err = KLGetKeyboardLayoutProperty( aLayout, kKLKind, (const void **)&keyLayoutKind ); + if (err != noErr) return nil; + + if (keyLayoutKind == kKLKCHRKind) { + err = KLGetKeyboardLayoutProperty( keyboardLayout, kKLKCHRData, (const void **)&KCHRData ); + if (err != noErr) return nil; + } else { + err = KLGetKeyboardLayoutProperty( keyboardLayout, kKLuchrData, (const void **)&uchrData ); + if (err != noErr) return nil; + } + } + + return self; +} + +- (NSString *)translateKeyCode:(short)keyCode { + if (keyLayoutKind == kKLKCHRKind) { + UInt32 charCode = KeyTranslate( KCHRData, keyCode, &keyTranslateState ); + char theChar = (charCode & 0x00FF); + return [[[NSString alloc] initWithData:[NSData dataWithBytes:&theChar length:1] encoding:NSMacOSRomanStringEncoding] autorelease]; + } else { + UniCharCount maxStringLength = 4, actualStringLength; + UniChar unicodeString[4]; + OSStatus err; + err = UCKeyTranslate( uchrData, keyCode, kUCKeyActionDisplay, 0, LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, maxStringLength, &actualStringLength, unicodeString ); + return [NSString stringWithCharacters:unicodeString length:1]; + } +} + +- (KeyboardLayoutRef)keyboardLayout { + return keyboardLayout; +} + +- (NSString *)description { + NSString *kind; + if (keyLayoutKind == kKLKCHRKind) + kind = @"KCHR"; + else + kind = @"uchr"; + + NSString *layoutName; + KLGetKeyboardLayoutProperty( keyboardLayout, kKLLocalizedName, (const void **)&layoutName ); + return [NSString stringWithFormat:@"PTKeyCodeTranslator layout=%@ (%@)", layoutName, kind]; +} + +#else + ++ (id)currentTranslator +{ + static PTKeyCodeTranslator *current = nil; + TISInputSourceRef currentLayout = TISCopyCurrentKeyboardLayoutInputSource(); + + if (current == nil) { + current = [[PTKeyCodeTranslator alloc] initWithKeyboardLayout:currentLayout]; + } else if ([current keyboardLayout] != currentLayout) { + current = [[PTKeyCodeTranslator alloc] initWithKeyboardLayout:currentLayout]; + } + return current; +} + +- (instancetype)initWithKeyboardLayout:(TISInputSourceRef)aLayout +{ + if ((self = [super init]) != nil) { + + keyboardLayout = aLayout; + CFDataRef uchr = TISGetInputSourceProperty( keyboardLayout , kTISPropertyUnicodeKeyLayoutData ); + uchrData = ( const UCKeyboardLayout* )CFDataGetBytePtr(uchr); + + } + + return self; +} + +- (NSString *)translateKeyCode:(short)keyCode +{ + UniCharCount maxStringLength = 4, actualStringLength; + UniChar unicodeString[4]; + OSStatus err; + err = UCKeyTranslate( uchrData, keyCode, kUCKeyActionDisplay, 0, LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, maxStringLength, &actualStringLength, unicodeString ); + return [NSString stringWithCharacters:unicodeString length:1]; +} + +- (TISInputSourceRef)keyboardLayout +{ + return keyboardLayout; +} + +- (NSString *)description +{ + NSString *kind = @"uchr"; + + NSString *layoutName = (__bridge NSString *)(TISGetInputSourceProperty( keyboardLayout, kTISPropertyLocalizedName )); + return [NSString stringWithFormat:@"PTKeyCodeTranslator layout=%@ (%@)", layoutName, kind]; +} + +#endif + +@end diff --git a/PTHotKeysLib/src/PTKeyCombo.h b/PTHotKeysLib/src/PTKeyCombo.h new file mode 100644 index 0000000..a5fa5eb --- /dev/null +++ b/PTHotKeysLib/src/PTKeyCombo.h @@ -0,0 +1,43 @@ +// +// PTKeyCombo.h +// Protein +// +// Created by Quentin Carnicelli on Sat Aug 02 2003. +// Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. +// + +#import + +#if __PROTEIN__ +#else +#define _PTLocalizedString NSLocalizedString +#endif + +@interface PTKeyCombo : NSObject +{ + int mKeyCode; + int mModifiers; +} + ++ (id)clearKeyCombo; ++ (instancetype)keyComboWithKeyCode: (int)keyCode modifiers: (int)modifiers; +- (instancetype)initWithKeyCode: (int)keyCode modifiers: (int)modifiers NS_DESIGNATED_INITIALIZER; + +- (instancetype)initWithPlistRepresentation: (id)plist; +@property (nonatomic, readonly, strong) id plistRepresentation; + +- (BOOL)isEqual: (PTKeyCombo*)combo; + +@property (nonatomic, readonly) int keyCode; +@property (nonatomic, readonly) int modifiers; + +@property (nonatomic, getter=isClearCombo, readonly) BOOL clearCombo; +@property (nonatomic, getter=isValidHotKeyCombo, readonly) BOOL validHotKeyCombo; + +@end + +@interface PTKeyCombo (UserDisplayAdditions) + +@property (nonatomic, readonly, copy) NSString *description; + +@end diff --git a/PTHotKeysLib/src/PTKeyCombo.m b/PTHotKeysLib/src/PTKeyCombo.m new file mode 100644 index 0000000..8b8f178 --- /dev/null +++ b/PTHotKeysLib/src/PTKeyCombo.m @@ -0,0 +1,235 @@ +// +// PTKeyCombo.m +// Protein +// +// Created by Quentin Carnicelli on Sat Aug 02 2003. +// Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. +// + +#import "PTKeyCombo.h" + +#import "PTKeyCodeTranslator.h" + +#import + +@implementation PTKeyCombo + ++ (id)clearKeyCombo +{ + return [self keyComboWithKeyCode: -1 modifiers: -1]; +} + ++ (instancetype)keyComboWithKeyCode: (int)keyCode modifiers: (int)modifiers +{ + return [[self alloc] initWithKeyCode: keyCode modifiers: modifiers]; +} + +- (instancetype)initWithKeyCode: (int)keyCode modifiers: (int)modifiers +{ + self = [super init]; + + if( self ) + { + mKeyCode = keyCode; + mModifiers = modifiers; + } + + return self; +} + +- (instancetype) initWithCoder:(NSCoder*)coder +{ + return [self initWithPlistRepresentation:[coder decodeObject]]; +} + +- (instancetype)initWithPlistRepresentation: (id)plist +{ + int keyCode, modifiers; + + if( !plist || ![plist count] ) + { + keyCode = -1; + modifiers = -1; + } + else + { + keyCode = [plist[@"keyCode"] intValue]; + if( keyCode <= 0 ) keyCode = -1; + + modifiers = [plist[@"modifiers"] intValue]; + if( modifiers <= 0 ) modifiers = -1; + } + + return [self initWithKeyCode: keyCode modifiers: modifiers]; +} + +- (id)plistRepresentation +{ + return @{@"keyCode": @([self keyCode]), + @"modifiers": @([self modifiers])}; +} + +- (void)encodeWithCoder:(NSCoder*)coder +{ + [coder encodeObject:[self plistRepresentation]]; +} + +- (id)copyWithZone:(NSZone*)zone; +{ + return self; +} + +- (BOOL)isEqual: (PTKeyCombo*)combo +{ + return [self keyCode] == [combo keyCode] && + [self modifiers] == [combo modifiers]; +} + +#pragma mark - + +- (int)keyCode +{ + return mKeyCode; +} + +- (int)modifiers +{ + return mModifiers; +} + +- (BOOL)isValidHotKeyCombo +{ + return mKeyCode >= 0 && mModifiers > 0; +} + +- (BOOL)isClearCombo +{ + return mKeyCode == -1 && mModifiers == -1; +} + +@end + +#pragma mark - + +@implementation PTKeyCombo (UserDisplayAdditions) + ++ (NSString*)_stringForModifiers: (long)modifiers +{ + static unichar modToChar[4][2] = + { + { cmdKey, kCommandUnicode }, + { optionKey, kOptionUnicode }, + { controlKey, kControlUnicode }, + { shiftKey, kShiftUnicode } + }; + + NSString* str = [NSString string]; + long i; + + for( i = 0; i < 4; i++ ) + { + if( modifiers & modToChar[i][0] ) + str = [str stringByAppendingString: [NSString stringWithCharacters: &modToChar[i][1] length: 1]]; + } + + return str; +} + ++ (NSDictionary*)_keyCodesDictionary +{ + static NSDictionary* keyCodes = nil; + + if( keyCodes == nil ) + { + NSString* path; + NSString* contents; + + path = [[NSBundle bundleForClass: self] pathForResource: @"KeyCodes" ofType: @"plist"]; +#if LEGACY_BUILD + contents = [NSString stringWithContentsOfFile: path]; +#else + contents = [NSString stringWithContentsOfFile:path usedEncoding:nil error:NULL]; + +#endif + keyCodes = [contents propertyList]; + } + + return keyCodes; +} + ++ (NSString*)_stringForKeyCode: (short)keyCode legacyKeyCodeMap: (NSDictionary*)dict +{ + id key; + NSString* str; + + key = [NSString stringWithFormat: @"%d", keyCode]; + str = dict[key]; + + if( !str ) + str = [NSString stringWithFormat: @"%X", keyCode]; + + return str; +} + ++ (NSString*)_stringForKeyCode: (short)keyCode newKeyCodeMap: (NSDictionary*)dict +{ + NSString* result; + NSString* keyCodeStr; + NSDictionary* unmappedKeys; + NSArray* padKeys; + + keyCodeStr = [NSString stringWithFormat: @"%d", keyCode]; + + //Handled if its not handled by translator + unmappedKeys = dict[@"unmappedKeys"]; + result = unmappedKeys[keyCodeStr]; + if( result ) + return result; + + //Translate it + result = [[[PTKeyCodeTranslator currentTranslator] translateKeyCode:keyCode] uppercaseString]; + + //Handle if its a key-pad key + padKeys = dict[@"padKeys"]; + if( [padKeys indexOfObject: keyCodeStr] != NSNotFound ) + { + result = [NSString stringWithFormat:@"%@ %@", dict[@"padKeyString"], result]; + } + + return result; +} + ++ (NSString*)_stringForKeyCode: (short)keyCode +{ + NSDictionary* dict; + + dict = [self _keyCodesDictionary]; + if( [dict[@"version"] intValue] <= 0 ) + return [self _stringForKeyCode: keyCode legacyKeyCodeMap: dict]; + + return [self _stringForKeyCode: keyCode newKeyCodeMap: dict]; +} + +- (NSString*)description +{ + NSString* desc; + + if( [self isValidHotKeyCombo] ) //This might have to change + { + desc = [NSString stringWithFormat: @"%@%@", + [[self class] _stringForModifiers: [self modifiers]], + [[self class] _stringForKeyCode: [self keyCode]]]; + } + else + { + #if __PROTEIN__ + desc = _PTLocalizedString( @"(None)", @"Hot Keys: Key Combo text for 'empty' combo" ); + #else + desc = NSLocalizedString( @"(None)", @"Hot Keys: Key Combo text for 'empty' combo" ); + #endif + } + + return desc; +} + +@end diff --git a/PTHotKeysLib/src/PTKeyComboPanel.h b/PTHotKeysLib/src/PTKeyComboPanel.h new file mode 100644 index 0000000..a4f3510 --- /dev/null +++ b/PTHotKeysLib/src/PTKeyComboPanel.h @@ -0,0 +1,42 @@ +// +// PTKeyComboPanel.h +// Protein +// +// Created by Quentin Carnicelli on Sun Aug 03 2003. +// Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. +// + +#import + +@class PTKeyBroadcaster; +@class PTKeyCombo; +@class PTHotKey; + +@interface PTKeyComboPanel : NSWindowController +{ + IBOutlet NSTextField* mTitleField; + IBOutlet NSTextField* mComboField; + IBOutlet PTKeyBroadcaster* mKeyBcaster; + + NSString* mTitleFormat; + NSString* mKeyName; + PTKeyCombo* mKeyCombo; + +} + ++ (PTKeyComboPanel*)sharedPanel; + +@property (nonatomic, readonly) int runModal; +- (void)runModalForHotKey: (PTHotKey*)hotKey; + +- (void)runSheeetForModalWindow: (NSWindow*)wind target: (id)obj; + //Calls hotKeySheetDidEndWithReturnCode: (NSNumber*) on target object + +@property (nonatomic, copy) PTKeyCombo *keyCombo; + +@property (nonatomic, copy) NSString *keyBindingName; + +- (IBAction)ok: (id)sender; +- (IBAction)cancel: (id)sender; +- (IBAction)clear: (id)sender; +@end diff --git a/PTHotKeysLib/src/PTKeyComboPanel.m b/PTHotKeysLib/src/PTKeyComboPanel.m new file mode 100644 index 0000000..cb26fe3 --- /dev/null +++ b/PTHotKeysLib/src/PTKeyComboPanel.m @@ -0,0 +1,193 @@ +// +// PTKeyComboPanel.m +// Protein +// +// Created by Quentin Carnicelli on Sun Aug 03 2003. +// Copyright (c) 2003 Quentin D. Carnicelli. All rights reserved. +// + +#import "PTKeyComboPanel.h" + +#import "PTHotKey.h" +#import "PTKeyCombo.h" +#import "PTKeyBroadcaster.h" +#import "PTHotKeyCenter.h" + +#if __PROTEIN__ +#import "PTNSObjectAdditions.h" +#endif + +@implementation PTKeyComboPanel + +static PTKeyComboPanel* _sharedKeyComboPanel = nil; + ++ (PTKeyComboPanel*)sharedPanel +{ + if( _sharedKeyComboPanel == nil ) + { + _sharedKeyComboPanel = [[self alloc] init]; + + #if __PROTEIN__ + [_sharedKeyComboPanel releaseOnTerminate]; + #endif + } + + return _sharedKeyComboPanel; +} + +- (instancetype)init +{ + return [self initWithWindowNibName: @"PTKeyComboPanel"]; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver: self]; + +} + +- (void)windowDidLoad +{ + [super windowDidLoad]; + + mTitleFormat = [mTitleField stringValue]; + + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector( noteKeyBroadcast: ) + name: PTKeyBroadcasterKeyEvent + object: mKeyBcaster]; +} + +- (void)_refreshContents +{ + if( mComboField ) + [mComboField setStringValue: [mKeyCombo description]]; + + if( mTitleField ) + [mTitleField setStringValue: [NSString stringWithFormat: mTitleFormat, mKeyName]]; +} + +#pragma mark - + +- (int)runModal +{ + int resultCode; + + (void)[self window]; //Force us to load + + [self _refreshContents]; + [[self window] center]; + [self showWindow: self]; + resultCode = [[NSApplication sharedApplication] runModalForWindow: [self window]]; + [self close]; + + return resultCode; +} + +- (void)runModalForHotKey: (PTHotKey*)hotKey +{ + int resultCode; + + [self setKeyBindingName: [hotKey name]]; + [self setKeyCombo: [hotKey keyCombo]]; + + resultCode = [self runModal]; + + if( resultCode == NSOKButton ) + { + [hotKey setKeyCombo: [self keyCombo]]; + [[PTHotKeyCenter sharedCenter] registerHotKey: hotKey]; + } +} + +#pragma mark - + +- (void)_sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + id delegate = (__bridge id)contextInfo; + + [sheet orderOut: nil]; + [self close]; + + if( delegate ) + { + NSNumber* returnObj = @(returnCode); + [delegate performSelector: @selector( hotKeySheetDidEndWithReturnCode: ) withObject: returnObj]; + } +} + +- (void)runSheeetForModalWindow: (NSWindow*)wind target: (id)obj +{ + [[self window] center]; //Force us to load + [self _refreshContents]; + + [[NSApplication sharedApplication] beginSheet: [self window] + modalForWindow: wind + modalDelegate: self + didEndSelector: @selector(_sheetDidEnd:returnCode:contextInfo:) + contextInfo: (void *)CFBridgingRetain(obj)]; +} + +#pragma mark - + +- (void)setKeyCombo: (PTKeyCombo*)combo +{ + mKeyCombo = combo; + [self _refreshContents]; +} + +- (PTKeyCombo*)keyCombo +{ + return mKeyCombo; +} + +- (void)setKeyBindingName: (NSString*)name +{ + mKeyName = name; + [self _refreshContents]; +} + +- (NSString*)keyBindingName +{ + return mKeyName; +} + +#pragma mark - + +- (IBAction)ok: (id)sender +{ + if( [[self window] isSheet] ) + [[NSApplication sharedApplication] endSheet: [self window] returnCode: NSOKButton]; + else + [[NSApplication sharedApplication] stopModalWithCode: NSOKButton]; +} + +- (IBAction)cancel: (id)sender +{ + if( [[self window] isSheet] ) + [[NSApplication sharedApplication] endSheet: [self window] returnCode: NSCancelButton]; + else + [[NSApplication sharedApplication] stopModalWithCode: NSCancelButton]; +} + +- (IBAction)clear: (id)sender +{ + [self setKeyCombo: [PTKeyCombo clearKeyCombo]]; + + if( [[self window] isSheet] ) + [[NSApplication sharedApplication] endSheet: [self window] returnCode: NSOKButton]; + else + [[NSApplication sharedApplication] stopModalWithCode: NSOKButton]; +} + +- (void)noteKeyBroadcast: (NSNotification*)note +{ + PTKeyCombo* keyCombo; + + keyCombo = [note userInfo][@"keyCombo"]; + + [self setKeyCombo: keyCombo]; +} + +@end diff --git a/Protocol.h b/Protocol.h new file mode 100644 index 0000000..322c85e --- /dev/null +++ b/Protocol.h @@ -0,0 +1,38 @@ +/* teleport protocol definitions */ + +#define PROTOCOL_VERSION 18 + +typedef NS_ENUM(NSInteger, TPMsgType) { + TPIdentificationMsgType, + + /* Authentication */ + TPAuthenticationRequestMsgType, + TPAuthenticationInProgressMsgType, + TPAuthenticationAbortMsgType, + TPAuthenticationSuccessMsgType, + TPAuthenticationFailureMsgType, + + /* Control */ + TPControlRequestMsgType, + TPControlSuccessMsgType, + TPControlFailureMsgType, + TPControlStopMsgType, + + /* Events */ + TPEventMsgType, + + /* Transfers */ + TPTransferRequestMsgType, + TPTransferSuccessMsgType, + TPTransferFailureMsgType +/* TPTransferControlMsgType*/ +} ; + +typedef unsigned char TPMouseButton; +typedef int64_t TPScrollWheelDistance; +typedef float TPPixelPos; +typedef struct _TPMouseDelta {int64_t x; int64_t y;} TPMouseDelta; +typedef struct _TPKey {CGKeyCode keyCode; CGCharCode charCode;} TPKey; +/*typedef enum {TPLeftSide=0, TPRightSide, TPTopSide, TPBottomSide} TPScreenSide;*/ +typedef uint64_t TPDataLength; +typedef unsigned short TPProtocolVersion; \ No newline at end of file diff --git a/Readme.pages b/Readme.pages new file mode 100644 index 0000000..555aa20 Binary files /dev/null and b/Readme.pages differ diff --git a/Readme.pdf b/Readme.pdf new file mode 100644 index 0000000..13a4e06 Binary files /dev/null and b/Readme.pdf differ diff --git a/Spanish.lproj/InfoPlist.strings b/Spanish.lproj/InfoPlist.strings new file mode 100644 index 0000000..07a458b Binary files /dev/null and b/Spanish.lproj/InfoPlist.strings differ diff --git a/Spanish.lproj/Localizable.strings b/Spanish.lproj/Localizable.strings new file mode 100644 index 0000000..d4473d8 Binary files /dev/null and b/Spanish.lproj/Localizable.strings differ diff --git a/Spanish.lproj/MainMenuApp.nib/classes.nib b/Spanish.lproj/MainMenuApp.nib/classes.nib new file mode 100644 index 0000000..8eab51a --- /dev/null +++ b/Spanish.lproj/MainMenuApp.nib/classes.nib @@ -0,0 +1,38 @@ + + + + + IBClasses + + + CLASS + TPPrefPaneAppController + LANGUAGE + ObjC + OUTLETS + + window + NSWindow + + SUPERCLASS + NSObject + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + IBVersion + 1 + + diff --git a/Spanish.lproj/MainMenuApp.nib/info.nib b/Spanish.lproj/MainMenuApp.nib/info.nib new file mode 100644 index 0000000..21ddea8 --- /dev/null +++ b/Spanish.lproj/MainMenuApp.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 629 + IBLastKnownRelativeProjectPath + ../teleport.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 265 + + IBSystem Version + 9A569 + targetFramework + IBCocoaFramework + + diff --git a/Spanish.lproj/MainMenuApp.nib/keyedobjects.nib b/Spanish.lproj/MainMenuApp.nib/keyedobjects.nib new file mode 100644 index 0000000..0d5ee09 Binary files /dev/null and b/Spanish.lproj/MainMenuApp.nib/keyedobjects.nib differ diff --git a/Spanish.lproj/teleportPref.xib b/Spanish.lproj/teleportPref.xib new file mode 100644 index 0000000..dd8290b --- /dev/null +++ b/Spanish.lproj/teleportPref.xib @@ -0,0 +1,5934 @@ + + + + 1050 + 9C31 + 629 + 949.26 + 352.00 + + YES + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + TPPreferencePane + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{517, 450}, {595, 429}} + 1081606144 + PDwgZG8gbm90IGxvY2FsaXplID4+A + NSWindow + + View + + + + 256 + + YES + + + 274 + + YES + + + 268 + {{149, 387}, {145, 18}} + + + YES + + 604110336 + 0 + Compartir este Mac + + LucidaGrande + 1.300000e+01 + 1044 + + + 1211912703 + 2 + + NSSwitch + + + + 200 + 25 + + + + + 265 + {{545, 379}, {32, 32}} + + + YES + + 67239424 + 134217728 + QWJvdXTigKY + + + 138690815 + 130 + + NSImage + icon-about + + + + + + 200 + 25 + + + + + 268 + {{18, 387}, {119, 18}} + + + YES + + 67239424 + 0 + Activar teleport + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 274 + + YES + + + 256 + + YES + + + 274 + {{20, 17}, {511, 296}} + + + TPLayoutView + NSView + + + + 290 + {{19, -2}, {501, 17}} + + + YES + + 67239424 + 4325376 + Organiza los Macs compartidos de arriba alrededor de tu pantalla para controlarlos. + + + 1.100000e+01 + 3100 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2OQA + + + + 6 + + controlTextColor + + 3 + MAA + + + + + + {{10, 33}, {549, 319}} + + + + + {{13, 10}, {569, 365}} + + + + YES + + layout + + RGlzcG9zaWNpw7NuA + + + + + + + 256 + + YES + + + 301 + + YES + + + 292 + {{185, 87}, {341, 28}} + + YES + + 67239424 + 272760832 + Para eliminar un host de confianza, seleccionarlo y presionar la tecla borrar + + + + + + + + + -2147483380 + {{176, 250}, {173, 32}} + + YES + + 67239424 + 134217728 + RXNjb2dlciBjZXJ0aWZpY2Fkb+KApg + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{176, 250}, {159, 32}} + + YES + + 67239424 + 134217728 + Mostrar certificado + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{183, 286}, {155, 18}} + + YES + + 67239424 + 0 + SGFiaWxpdGFyIGNvZGlmaWNhY2nDs24 + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{23, 287}, {148, 17}} + + YES + + 67239424 + 71303168 + Q29kaWZpY2FjacOzbjo + + + + + + + + + 292 + {{185, 13}, {299, 58}} + + YES + 3 + 1 + + YES + + -2080244224 + 0 + Preguntarme si confiar en este host + + + 1211912703 + 0 + + NSRadioButton + + + + + + 200 + 25 + + + 67239424 + 0 + UmVjaGF6YXIgc2kgZWwgaG9zdCBubyBlc3TDoSB5YSBlbiBsYSBsaXN0YQ + + 1 + + 1211912703 + 0 + + + + 200 + 25 + + + 67239424 + 0 + QWNlcHRhciBhdXRvbcOhdGljYW1lbnRlA + + 2 + + 1211912703 + 0 + + + + 400 + 75 + + + {299, 18} + {4, 2} + 1143480320 + NSActionCell + + 67239424 + 0 + Radio + + 1211912703 + 0 + + 400 + 75 + + + + + 3 + MQA + + + + + + 274 + + YES + + + 2304 + + YES + + + 256 + {333, 104} + + YES + + + 256 + {333, 17} + + + + + + 256 + {{-26, 0}, {16, 17}} + + + + YES + + name + 1.470000e+02 + 4.000000e+01 + 1.000000e+03 + + 75628032 + 0 + Nombre + + + 3 + MC4zMzMzMzI5OQA + + + 6 + + headerTextColor + + + + + 337772096 + 133120 + Text Cell + + + + 6 + + controlBackgroundColor + + + + + 1 + YES + + + + address + 1.140000e+02 + 8.000000e+00 + 1.000000e+03 + + 75628032 + 0 + RGlyZWNjacOzbg + + + + + + 337772096 + 133120 + + + + + + + 1 + YES + + + + certificate + 6.300000e+01 + 5.857568e+01 + 1.000000e+03 + + 67239424 + 0 + Certificado + + + 6 + + headerColor + + + + + + 67239424 + 134348800 + Ver + + + -2034876161 + 36 + + + 400 + 75 + + + + + 3.000000e+00 + 2.000000e+00 + + + 6 + + gridColor + + 3 + MC41AA + + + 1.700000e+01 + 448823296 + 1 + 15 + 0 + YES + + + {{1, 17}, {333, 104}} + + + + + 4 + + + + 256 + {{-100, -100}, {15, 180}} + + + _doScroller: + 9.222222e-01 + + + + 256 + {{-100, -100}, {463, 15}} + + 1 + + + 9.904762e-01 + + + + 2304 + + YES + + + {{1, 0}, {333, 17}} + + + + + 4 + + + + {{185, 116}, {335, 122}} + + + 2 + + + + + + QSAAAEEgAABBmAAAQZgAAA + + + + 292 + {{14, 54}, {157, 17}} + + YES + + 67239424 + 71303168 + Peticiones de control: + + + + + + + + + 268 + {{8, 221}, {163, 17}} + + YES + + 67239424 + 71303168 + Hosts de confianza: + + + + + + + + {549, 319} + + + + + {{10, 33}, {549, 319}} + + Seguridad + + + + + options + + + 256 + + YES + + + 301 + + YES + + + 268 + {{211, 132}, {146, 18}} + + YES + + 67239424 + 0 + U8OzbG8gc2kgZXMgbWVub3IgZGU + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{67, 177}, {104, 21}} + + YES + + 67239424 + 71303168 + Transferencias: + + + + + + + + + 268 + {{55, 17}, {116, 17}} + + YES + + 67239424 + 71303168 + QWN0dWFsaXphY2nDs246A + + + + + + + + + 268 + {{102, 287}, {69, 17}} + + YES + + 67239424 + 71303168 + Cambio: + + + + + + + + + 268 + {{183, 74}, {255, 18}} + + YES + + 67239424 + 0 + TW9zdHJhciBlc3RhZG8gZW4gbGEgYmFycmEgZGUgbWVuw7pzA + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 180}, {259, 18}} + + YES + + -2080244224 + 0 + Arrastrar y Soltar archivos entre Macs + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{211, 108}, {132, 18}} + + YES + + 67239424 + 0 + U8OzbG8gc2kgcHJlc2lvbm8 + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 156}, {194, 18}} + + YES + + -2080244224 + 0 + Sincronizar el portapapeles + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 286}, {179, 18}} + + YES + + 67239424 + 0 + Q2FtYmlhciBzw7NsbyBzaSBwcmVzaW9ubw + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{364, 103}, {129, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + 4oyYIENvbWFuZG8 + + 1048576 + 2147483647 + 1 + + + NSMenuCheckmark + + + + NSMenuMixedState + + _popUpItemAction: + 20 + + + YES + + + OtherViews + + + YES + + + + 4oylIE9wY2nDs24 + + 1048576 + 2147483647 + + + _popUpItemAction: + 19 + + + + + 4oyDIENvbnRyb2w + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + 4oenIE1hecO6c2N1bGFzA + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + 4oeqIEJsb3F1ZW8gTWF5w7pzYy4 + + 1048576 + 2147483647 + + + _popUpItemAction: + 16 + + + + + 3 + YES + YES + 1 + + + + + 268 + {{367, 260}, {38, 17}} + + YES + + 67239424 + 272629760 + Corto + + + + + + + + + 268 + {{183, 214}, {261, 18}} + + YES + + 67239424 + 0 + Intentar despertar los Macs en reposo + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 262}, {174, 18}} + + YES + + 67239424 + 0 + Cambiar tras un retardo + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{183, 238}, {259, 18}} + + YES + + 67239424 + 0 + Cambiar al tocar el borde dos veces + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{367, 131}, {64, 22}} + + YES + + -1804468671 + 71304192 + + + + YES + + YES + allowsFloats + attributedStringForZero + decimalSeparator + formatterBehavior + groupingSeparator + locale + maximum + minimum + negativeFormat + positiveFormat + textAttributesForNegativeValues + usesGroupingSeparator + + + YES + + + 0 + + YES + + YES + + + YES + + + + wqA + + , + + + + + 0 + 2 + NO + YES + 2 + AABAAAAAAAAAAAAAAAAAAA + + + 0 + 0 + NO + NO + 2 + AAAAAAAAAAAAAAAAAAAAAA + + + 0K + + YES + + YES + + + YES + + + + + + + + + + + + + + + NaN + + + + + + + + NO + NO + YES + + dGFtYcOxbw + + YES + + 6 + + textBackgroundColor + + + + 6 + + textColor + + + + + + + 268 + {{364, 281}, {129, 26}} + + YES + + -2076049856 + 2048 + + + 109199615 + 1 + + + + + 400 + 75 + + + + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + 20 + + + YES + + + OtherViews + + + YES + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 19 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 18 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 17 + + + + + + + 1048576 + 2147483647 + + + _popUpItemAction: + 16 + + + + + 3 + YES + YES + 1 + + + + + 268 + {{183, 50}, {297, 18}} + + YES + + 67239424 + 0 + Mostrar bisel mientras se controla otro Mac + + + 1211912703 + 2 + + + + 200 + 25 + + + + + 268 + {{375, 7}, {153, 32}} + + YES + + 67239424 + 134217728 + Comprobar Ahora + + + -2038284033 + 1 + + + + + 200 + 25 + + + + + 268 + {{102, 75}, {69, 17}} + + YES + + 67239424 + 71303168 + Estado: + + + + + + + + + 268 + {{408, 261}, {83, 15}} + + YES + + 67239424 + 131072 + + + + + Helvetica + 1.200000e+01 + 16 + + + 1.000000e+00 + 1.000000e-01 + 5.000000e-01 + 0.000000e+00 + 0 + 1 + NO + NO + + + + + 268 + {{496, 260}, {37, 17}} + + YES + + 67239424 + 272629760 + Largo + + + + + + + + + 268 + {{183, 16}, {198, 18}} + + YES + + 67239424 + 0 + Q29tcHJvYmFyIHZlcnNpw7NuIGFsIGFicmlyA + + + 1211912703 + 2 + + + + 200 + 25 + + + + {549, 319} + + + + + {{10, 33}, {549, 319}} + + Opciones + + + + + + + 0 + YES + YES + + + {595, 438} + + + + + + {595, 429} + + + + {{0, 0}, {1680, 1028}} + {224.664, 32} + {3.40282e+38, 3.40282e+38} + + + 1 + 2 + {{194, 375}, {295, 307}} + 1886912512 + + TPClosingWindow + + View + + + + 256 + + YES + + + 256 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + CorePasteboardFlavorType 0x504E4766 + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{20, 159}, {255, 128}} + + + YES + + 537001472 + 33554432 + + + teleport + + 0 + 0 + 0 + NO + + YES + + + + 256 + {{17, 129}, {261, 22}} + + + YES + + 67239424 + 138412032 + teleport + + LucidaGrande-Bold + 1.800000e+01 + 16 + + + + + + + + + 256 + {{17, 113}, {261, 14}} + + + YES + + 67239424 + 138412032 + PHZlcnNpb24+A + + + + + + + + + 256 + {{12, 43}, {274, 39}} + + + YES + + 67239424 + 4194304 + wqkgMjAwMy0yMDA4IGFieXNzb2Z0CkJhc2FkbyBlbiB1bmEgaWRlYSBkZSBEYXZlIFNhZyB5IEplcmVt +eSBRdWlubi4KR3JhY2lhcyBhIE1hbnUwMiwgSnVsaWVuIHkgUGF1bC4 + + + 1.000000e+01 + 2843 + + + + + + + + + 258 + {{12, 8}, {73, 17}} + + + YES + + 69336577 + 272629760 + 4p6kIHNpdGlvIHdlYg + + + 1.100000e+01 + 16 + + http://teleport.abyssoft.com + + YES + + + + + + + 258 + {{119, 8}, {56, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIGZvcm9zA + + http://www.abyssoft.com/software/teleport/forums/ + + + + + + + + 258 + {{228, 8}, {55, 17}} + + + YES + + 67239424 + 272629760 + 4p6kIGRvbmFyA + + aHR0cHM6Ly93d3cucGF5cGFsLmNvbS9jZ2ktYmluL3dlYnNjcj9jbWQ9X3hjbGljayZidXNpbmVzcz1q +dWwlNDBtYWMlMmVjb20maXRlbV9uYW1lPXRlbGVwb3J0Jm5vX3NoaXBwaW5nPTAmbm9fbm90ZT0wJnRh +eD0wJmN1cnJlbmN5X2NvZGU9VVNEJmNoYXJzZXQ9VVRGJTJkOCZjaGFyc2V0PVVURiUyZDg + + + + + + + {295, 307} + + + + {{0, 0}, {1680, 1028}} + {213, 129} + {3.40282e+38, 3.40282e+38} + + + + YES + prefs.allowControl + prefs.sharePasteboard + prefs.requireKey + prefs.delayedSwitch + prefs.showStatusItem + prefs.limitPasteboardSize + prefs.maxPasterboardSize + active + prefs.autocheckVersion + prefs.rejectAuthenticationRequests + prefs.enableEncryption + prefs.switchKeyTag + prefs.copyFiles + prefs.requirePasteboardKey + prefs.pasteboardKeyTag + prefs.trustRequestBehavior + prefs.wakeOnLAN + prefs.switchDelay + localHost.supportDragNDrop + prefs.maxPasteboardSize + prefs.hideControlBezel + prefs.switchWithDoubleTap + + YES + + + + + YES + numberOfSelectedRows + + NSTableView + + + + + + YES + + + _window + + + + 26 + + + + layoutView + + + + 145 + + + + allowControlCheckbox + + + + 156 + + + + aboutWindow + + + + 170 + + + + delegate + + + + 171 + + + + showAboutSheet: + + + + 173 + + + + activationCheckbox + + + + 175 + + + + versionTextField + + + + 183 + + + + + + + + 184 + + + + + + + + 200 + + + + view + + + + 202 + + + + sharePasteboardCheckbox + + + + 205 + + + + requireKeyCheckbox + + + + 206 + + + + _firstKeyView + + + + 232 + + + + _initialKeyView + + + + 233 + + + + _lastKeyView + + + + 234 + + + + dataSource + + + + 258 + + + + + + + + 259 + + + + trustedHostsTableView + + + + 260 + + + + statusCheckbox + + + + 266 + + + + delayedSwitchCheckbox + + + + 272 + + + + content + + + + 298 + + + + value: selection.prefs.allowControl + + + + + + + value + selection.prefs.allowControl + 2 + + + 320 + + + + value: selection.prefs.sharePasteboard + + + + + + + + selection.prefs.sharePasteboard + 2 + + + 321 + + + + value: selection.prefs.requireKey + + + + + + + + selection.prefs.requireKey + 2 + + + 322 + + + + value: selection.prefs.delayedSwitch + + + + + + + + selection.prefs.delayedSwitch + 2 + + + 323 + + + + value: selection.prefs.showStatusItem + + + + + + + + selection.prefs.showStatusItem + 2 + + + 324 + + + + value: selection.prefs.limitPasteboardSize + + + + + + + + selection.prefs.limitPasteboardSize + 2 + + + 329 + + + + enabled: selection.prefs.sharePasteboard + + + + + + + enabled + + 2 + + + 330 + + + + + + + + + + + + + 2 + + + 331 + + + + checkVersion: + + + + 337 + + + + value: selection.active + + + + + + + + selection.active + 2 + + + 342 + + + + enabled: selection.active + + + + + + + + + 2 + + + 343 + + + + + + + + + + + + + 2 + + + 344 + + + + enabled2: selection.active + + + + + + + enabled2 + + + YES + + YES + NSMultipleValuesPlaceholder + NSNoSelectionPlaceholder + NSNotApplicablePlaceholder + NSNullPlaceholder + + + YES + + + + + + + + 2 + + + 345 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 346 + + + + enabled3: selection.prefs.limitPasteboardSize + + + + + + + enabled3 + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 347 + + + + + + + + + + + + + 2 + + + 348 + + + + + + + + + + + + + 2 + + + 349 + + + + + + + + + + + + + 2 + + + 350 + + + + + + + + + + + + + 2 + + + 352 + + + + value: selection.prefs.autocheckVersion + + + + + + + + selection.prefs.autocheckVersion + 2 + + + 355 + + + + + + + + + + + + + 2 + + + 356 + + + + + + + + + + + + + 2 + + + 383 + + + + value: selection.prefs.enableEncryption + + + + + + + + selection.prefs.enableEncryption + 2 + + + 384 + + + + enabled: selection.prefs.enableEncryption + + + + + + + + + 2 + + + 386 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 387 + + + + enabled: selection.prefs.requireKey + + + + + + + + + 2 + + + 399 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 400 + + + + selectedTag: selection.prefs.switchKeyTag + + + + + + + selectedTag + selection.prefs.switchKeyTag + 2 + + + 401 + + + + + + + + + + + + + 2 + + + 403 + + + + value: selection.prefs.copyFiles + + + + + + + + selection.prefs.copyFiles + 2 + + + 405 + + + + + + + + 407 + + + + + + + + + + + + + 2 + + + 419 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 420 + + + + value: selection.prefs.requirePasteboardKey + + + + + + + + selection.prefs.requirePasteboardKey + 2 + + + 421 + + + + enabled: selection.prefs.requirePasteboardKey + + + + + + + + + 2 + + + 422 + + + + enabled2: selection.prefs.sharePasteboard + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 423 + + + + enabled3: selection.active + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 424 + + + + selectedTag: selection.prefs.pasteboardKeyTag + + + + + + + + selection.prefs.pasteboardKeyTag + 2 + + + 425 + + + + + + + + + + + + + 2 + + + 431 + + + + + + + + + + + + + 2 + + + 432 + + + + selectedTag: selection.prefs.trustRequestBehavior + + + + + + + + selection.prefs.trustRequestBehavior + 2 + + + 442 + + + + + + + + + + + + + 2 + + + 445 + + + + value: selection.prefs.wakeOnLAN + + + + + + + + selection.prefs.wakeOnLAN + 2 + + + 446 + + + + value: selection.prefs.switchDelay + + + + + + + + selection.prefs.switchDelay + 2 + + + 450 + + + + enabled: selection.prefs.delayedSwitch + + + + + + + + + 2 + + + 451 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 452 + + + + enabled2: selection.localHost.supportDragNDrop + + + + + + + + selection.localHost.supportDragNDrop + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 453 + + + + + + + + + + + + + 2 + + + 454 + + + + value: selection.prefs.maxPasteboardSize + + + + + + + + selection.prefs.maxPasteboardSize + 2 + + + 455 + + + + showCertificateButton + + + + 300281 + + + + chooseCertificateButton + + + + 300282 + + + + showCertificateViewer: + + + + 300283 + + + + showCertificateChooser: + + + + 300284 + + + + + + + + + + + + + 2 + + + 300293 + + + + + + + + + + + + + + YES + + YES + + + + + + + YES + + + + + + + + 2 + + + 300294 + + + + value: selection.prefs.hideControlBezel + + + + + + + + selection.prefs.hideControlBezel + + NSValueTransformerName + NSNegateBoolean + + 2 + + + 300300 + + + + + + + + + + + + + 2 + + + 300301 + + + + value: selection.prefs.switchWithDoubleTap + + + + + + + + selection.prefs.switchWithDoubleTap + 2 + + + 300305 + + + + + + + + + + + + + 2 + + + 300306 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + -3 + + + Application + + + 12 + + + YES + + + + PrefPane + + + 6 + + + YES + + + + + + 201 + + + YES + + + + + + + + + 136 + + + YES + + + + + + 172 + + + YES + + + + + + 174 + + + YES + + + + + + 186 + + + YES + + + + + + + + 187 + + + YES + + + + + + 189 + + + YES + + + + + + + 137 + + + + + 152 + + + YES + + + + + + 188 + + + YES + + + + + + 190 + + + YES + + + + + + 357 + + + YES + + + + + + 358 + + + YES + + + + + + 161 + + + YES + + + + About + + + 162 + + + YES + + + + + + + + + + + + 164 + + + YES + + + + + + 165 + + + YES + + + + + + 166 + + + YES + + + + + + 168 + + + YES + + + + + + 291 + + + YES + + + + + + 292 + + + YES + + + + + + 293 + + + YES + + + + + + 297 + + + PrefPaneController + + + 406 + + + TrustedHostsTable + + + 100136 + + + + + 100172 + + + + + 100174 + + + + + 100152 + + + + + 100164 + + + + + 100165 + + + + + 100166 + + + + + 100168 + + + + + 100291 + + + + + 100292 + + + + + 100293 + + + + + 300315 + + + YES + + + + + + + + + + + + + + 441 + + + YES + + + + + + 100441 + + + + + 300279 + + + YES + + + + + + 300280 + + + + + 385 + + + YES + + + + + + 100385 + + + + + 381 + + + YES + + + + + + 100381 + + + + + 300297 + + + YES + + + + + + 300298 + + + + + 434 + + + YES + + + + + + + + + 100434 + + + + + 439 + + + + + 438 + + + + + 437 + + + + + 246 + + + YES + + + + + + + + + 300246 + + + + + 200246 + + + + + 100246 + + + + + 247 + + + YES + + + + + + + + 360 + + + YES + + + + + + 249 + + + YES + + + + + + 248 + + + YES + + + + + + 100248 + + + + + 100249 + + + + + 361 + + + + + 440 + + + YES + + + + + + 100440 + + + + + 362 + + + YES + + + + + + 100362 + + + + + 300316 + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + 325 + + + YES + + + + + + 100325 + + + + + 363 + + + YES + + + + + + 100363 + + + + + 367 + + + YES + + + + + + 100367 + + + + + 366 + + + YES + + + + + + 100366 + + + + + 264 + + + YES + + + + + + 100264 + + + + + 402 + + + YES + + + + + + 100402 + + + + + 411 + + + YES + + + + + + 100411 + + + + + 191 + + + YES + + + + + + 100191 + + + + + 194 + + + YES + + + + + + 100194 + + + + + 412 + + + YES + + + + + + 100412 + + + YES + + + + + + 413 + + + YES + + + + + + + + + + 414 + + + + + 415 + + + + + 416 + + + + + 417 + + + + + 418 + + + + + 448 + + + YES + + + + + + 100448 + + + + + 443 + + + YES + + + + + + 100443 + + + + + 271 + + + YES + + + + + + 100271 + + + + + 300302 + + + YES + + + + + + 300303 + + + + + 326 + + + YES + + + + + + 100326 + + + YES + + + + + + 334 + + + + + 389 + + + YES + + + + + + 100389 + + + YES + + + + + + 390 + + + YES + + + + + + + + + + 391 + + + + + 392 + + + + + 393 + + + + + 394 + + + + + 395 + + + + + 300295 + + + YES + + + + + + 300296 + + + + + 336 + + + YES + + + + + + 100336 + + + + + 368 + + + YES + + + + + + 100368 + + + + + 447 + + + YES + + + + + + 100447 + + + + + 449 + + + YES + + + + + + 100449 + + + + + 353 + + + YES + + + + + + 100353 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + -3.ImportedFromIB2 + 100246.IBShouldRemoveOnLegacySave + 100248.IBShouldRemoveOnLegacySave + 100249.IBShouldRemoveOnLegacySave + 100271.IBAttributePlaceholdersKey + 12.IBPluginDependency + 12.IBWindowTemplateEditedContentRect + 12.ImportedFromIB2 + 12.editorWindowContentRectSynchronizationRect + 12.windowTemplate.hasMaxSize + 12.windowTemplate.hasMinSize + 12.windowTemplate.maxSize + 12.windowTemplate.minSize + 136.IBAttributePlaceholdersKey + 136.IBPluginDependency + 136.ImportedFromIB2 + 137.IBAttributePlaceholdersKey + 137.IBPluginDependency + 137.ImportedFromIB2 + 152.IBPluginDependency + 152.ImportedFromIB2 + 161.IBPluginDependency + 161.IBWindowTemplateEditedContentRect + 161.ImportedFromIB2 + 161.editorWindowContentRectSynchronizationRect + 161.windowTemplate.hasMaxSize + 161.windowTemplate.hasMinSize + 161.windowTemplate.maxSize + 161.windowTemplate.minSize + 162.IBPluginDependency + 162.ImportedFromIB2 + 164.IBPluginDependency + 164.ImportedFromIB2 + 165.IBPluginDependency + 165.ImportedFromIB2 + 166.CustomClassName + 166.IBPluginDependency + 166.ImportedFromIB2 + 168.IBPluginDependency + 168.ImportedFromIB2 + 172.IBAttributePlaceholdersKey + 172.IBPluginDependency + 172.ImportedFromIB2 + 174.IBAttributePlaceholdersKey + 174.IBPluginDependency + 174.ImportedFromIB2 + 186.IBPluginDependency + 186.ImportedFromIB2 + 187.IBPluginDependency + 187.ImportedFromIB2 + 188.IBPluginDependency + 188.ImportedFromIB2 + 189.IBAttributePlaceholdersKey + 189.IBPluginDependency + 189.ImportedFromIB2 + 190.IBPluginDependency + 190.ImportedFromIB2 + 191.IBPluginDependency + 191.ImportedFromIB2 + 194.IBPluginDependency + 194.ImportedFromIB2 + 200246.IBShouldRemoveOnLegacySave + 201.IBPluginDependency + 201.ImportedFromIB2 + 246.IBPluginDependency + 246.ImportedFromIB2 + 247.CustomClassName + 247.IBPluginDependency + 247.ImportedFromIB2 + 248.IBPluginDependency + 248.ImportedFromIB2 + 249.IBPluginDependency + 249.ImportedFromIB2 + 264.IBPluginDependency + 264.ImportedFromIB2 + 271.IBAttributePlaceholdersKey + 271.IBPluginDependency + 271.ImportedFromIB2 + 291.CustomClassName + 291.IBPluginDependency + 291.ImportedFromIB2 + 292.CustomClassName + 292.IBPluginDependency + 292.ImportedFromIB2 + 293.CustomClassName + 293.IBPluginDependency + 293.ImportedFromIB2 + 297.IBPluginDependency + 297.ImportedFromIB2 + 300246.IBShouldRemoveOnLegacySave + 300279.IBPluginDependency + 300279.ImportedFromIB2 + 300295.IBPluginDependency + 300295.ImportedFromIB2 + 300297.IBPluginDependency + 300297.ImportedFromIB2 + 300302.IBAttributePlaceholdersKey + 300302.IBPluginDependency + 300302.ImportedFromIB2 + 325.IBPluginDependency + 325.ImportedFromIB2 + 326.IBPluginDependency + 326.ImportedFromIB2 + 334.IBPluginDependency + 334.ImportedFromIB2 + 336.IBPluginDependency + 336.ImportedFromIB2 + 353.IBPluginDependency + 353.ImportedFromIB2 + 357.IBPluginDependency + 357.ImportedFromIB2 + 358.IBPluginDependency + 358.ImportedFromIB2 + 360.IBPluginDependency + 360.ImportedFromIB2 + 361.IBPluginDependency + 361.ImportedFromIB2 + 362.IBPluginDependency + 362.ImportedFromIB2 + 363.IBPluginDependency + 363.ImportedFromIB2 + 366.IBPluginDependency + 366.ImportedFromIB2 + 367.IBPluginDependency + 367.ImportedFromIB2 + 368.IBPluginDependency + 368.ImportedFromIB2 + 381.IBPluginDependency + 381.ImportedFromIB2 + 385.IBPluginDependency + 385.ImportedFromIB2 + 389.IBPluginDependency + 389.ImportedFromIB2 + 390.IBPluginDependency + 390.ImportedFromIB2 + 390.editorWindowContentRectSynchronizationRect + 391.IBPluginDependency + 391.ImportedFromIB2 + 392.IBPluginDependency + 392.ImportedFromIB2 + 393.IBPluginDependency + 393.ImportedFromIB2 + 394.IBPluginDependency + 394.ImportedFromIB2 + 395.IBPluginDependency + 395.ImportedFromIB2 + 402.IBPluginDependency + 402.ImportedFromIB2 + 406.IBPluginDependency + 406.ImportedFromIB2 + 411.IBPluginDependency + 411.ImportedFromIB2 + 412.IBPluginDependency + 412.ImportedFromIB2 + 413.IBPluginDependency + 413.ImportedFromIB2 + 413.editorWindowContentRectSynchronizationRect + 414.IBPluginDependency + 414.ImportedFromIB2 + 415.IBPluginDependency + 415.ImportedFromIB2 + 416.IBPluginDependency + 416.ImportedFromIB2 + 417.IBPluginDependency + 417.ImportedFromIB2 + 418.IBPluginDependency + 418.ImportedFromIB2 + 434.IBPluginDependency + 434.ImportedFromIB2 + 437.IBPluginDependency + 437.ImportedFromIB2 + 438.IBPluginDependency + 438.ImportedFromIB2 + 439.IBPluginDependency + 439.ImportedFromIB2 + 440.IBPluginDependency + 440.ImportedFromIB2 + 441.IBPluginDependency + 441.ImportedFromIB2 + 443.IBAttributePlaceholdersKey + 443.IBPluginDependency + 443.ImportedFromIB2 + 447.IBPluginDependency + 447.ImportedFromIB2 + 448.IBPluginDependency + 448.ImportedFromIB2 + 449.IBPluginDependency + 449.ImportedFromIB2 + 6.IBPluginDependency + 6.ImportedFromIB2 + + + YES + + + + + + + + + YES + + YES + + + YES + + + + {{498, 577}, {595, 429}} + + + + + {3.40282e+38, 3.40282e+38} + {224.664, 10} + + ToolTip + + + + Marcar para permitir a otros usuarios de teleport controlar este Mac. + + + + + + + + + + QXJyYXN0cmEgbG9zIGhvc3RzIGNvbnRyb2xhZG9zIHkgcG9ubG9zIGVuIGxhIGRpc3Bvc2ljacOzbiwg +cmVsYXRpdmEgYSBsYSBwYW50YWxsYSBkZWwgTWFjIHByaW5jaXBhbCwgcXVlIGRlc2Vlcy4 + + + + + + + + {{194, 375}, {295, 307}} + + + + + + {213, 107} + + + + + + + TPVersionTextField + + + + + + + + + + QWJvdXQgdGVsZXBvcnTigKY + + + + + + + + + + Marcar para activar teleport. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TPTableView + + + + + + + + + + + + + + Llevar el cursor hasta el borde de la pantalla y mantenerlo brevemente hasta que se activa el cambio. + + + + + TPLinkTextField + + + + + + + + + + + + + + + + + + + + + + + TGxldmFyIGVsIGN1cnNvciBhbCBib3JkZSBkZSBsYSBwYW50YWxsYSwgcmV0aXJhcmxvIHVuIHBhciBk +ZSBjZW50w61tZXRyb3MgeSB2b2x2ZXIgYSBsbGV2YXJsbyBoYXN0YSBlbCBib3JkZSBvdHJhIHZleiBw +YXJhIGFjdGl2YXIgZWwgY2FtYmlvLg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{870, 891}, {201, 103}} + + + + + + + + + + + + + + + + + + + + + {{856, 713}, {201, 103}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + U8OzbG8gZW4gb3JkZW5hZG9yZXMgY29uZWN0YWRvcyBwb3IgRXRoZXJuZXQgeSBjb24gbGEgb3BjacOz +biDigJxBY3RpdmFyIGVsIG9yZGVuYWRvciBwYXJhIHBlcm1pdGlyIGFjY2VkZXIgYWwgYWRtaW5pc3Ry +YWRvciBkZSByZWRlcyBldGhlcm5ldOKAnSBoYWJpbGl0YWRhIGVuIGVsIHBhbmVsIGRlIHByZWZlcmVu +Y2lhcyBFY29ub21pemFkb3IuA + + + + + + + + + + + + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 300316 + + + + YES + + + NSPreferencePane + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + IBUserSource + + + + + TPPreferencePane + NSPreferencePane + + YES + + YES + addOther: + checkVersion: + closeAddOther: + confirmAddOther: + deleteTrustedHost: + showAboutSheet: + showCertificateChooser: + showCertificateViewer: + + + YES + id + + + + + + + + + + + YES + + YES + aboutWindow + activationCheckbox + allowControlCheckbox + chooseCertificateButton + delayedSwitchCheckbox + layoutView + requireKeyCheckbox + sharePasteboardCheckbox + showCertificateButton + statusCheckbox + trustedHostsTableView + versionTextField + + + YES + + + + + + TPLayoutView + + + + + + + + + + IBProjectSource + TPPreferencePane.h + + + + + NSObject + + YES + + YES + addOther: + changeTabPanel: + + closeAddOther: + closeTrustedCertificate: + confirmAddOther: + deleteTrustedHost: + + showCertificate: + showTrustedHosts: + + + YES + id + + + + + + + + + + + + + YES + + YES + + + + + + + addOtherAddressField + addOtherHeightField + addOtherNameField + addOtherSheet + addOtherWidthField + + certificateView + certificateWindow + + + + + + + + + + + YES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TPVersionTextField + NSTextField + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPVersionTextField.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + + + + + + + + + + TPClosingWindow + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPClosingWindow.h + + + + TPLinkTextField + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPLinkTextField.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPDaemonManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTableView.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection_Private.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPUDPSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPNetworkConnection.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTransfersManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSecureSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPPreferencesManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTCPSocket.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPConnectionsManager.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPHotBorder.h + + + + TPTableView + NSTableView + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + TPTableView.h + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + + + + YES + + YES + + + YES + + + + YES + + YES + + + YES + + + + + + + + + TPLayoutView + + + YES + + YES + scroll: + setScaleFactor: + + + YES + + + + + + + + + + + TPLayoutView.h + + + + + + + : + + + + YES + + YES + + + YES + + + + + + + + + + 0 + ../../teleport.xcodeproj + 3 + + YnBsaXN0MDDUAAEAAgADAAQABQAGAAkAClgkdmVyc2lvblQkdG9wWSRhcmNoaXZlclgkb2JqZWN0cxIA +AYag0QAHAAhdSUIub2JqZWN0ZGF0YYABXxAPTlNLZXllZEFyY2hpdmVyrxEEDAALAAwAMQA1ADYAPAA9 +AEEARQChAKkAuADBAAsAwgDDAMQAyADJAM0A0gDcAOUA5gD+AP8BBwEIAQsBDAEPARMBFwEbARwBHwEk +ASUBKgErATABSAFJAK0BSgFNAVQBWAFgAWcBbwFwAXwBfQGBAYIBhQGOAY8BmgGbAaABoQGiAaUBpwGv +AbABuAG5AcoBzgHTAdwB3QHnAegB7QH3AfgB+QH+AgACBQIGAgkCDAIPAhMCFAIWAhcCHQImAicCKAIr +AjECNgI6AkYCTgJPAlcCWAJgAmECaAJpAmsCcgJzAnsCfAKDAoQCjAKNAqcCqAKuArcCuAK7ArwCvgLH +AsgC0gLTAtQBFgLVAtoC2wLeAuEC9gL+AwwDEAMtAy4DLwMwAzYDPwNAA0MDSANJA0wDUgNjA2QDawNs +A28DdAN1A3gDgQOCA4cDiAOLA5QDlQObA5wDowOrA6wDsgOzA7gDuQPDA8QDyQPKA80D0APRA9QD3wPg +A+ED5APsA+0D8QPyA/MD9gP9A/4EBQQGBA0EDgQVBBYCEwQXBBgEHwQgBCUEKQQwBEkEUARRBFkEWgRh +BGIEaQRqBHEEcgR5BHoEgQSCBIkEigSRBJIEmgSbBKIEowSrBKwEswS0BLwEvQTEBMUEzQTOBNUE1gTe +BN8E5gTnBPwE/gURBRYFFwUbBRwFIAUhBSIFJAUnBS8FOQUhBToFRAUhBUUFTwUhBVAFWgUhBVsFXQVh +BWQFawVsBXQFdQV8BX0FhQWGBY0FjgWWBZcFngWfBacFqAWvBbAFvQXfBfwF/QX+Bf8GAAYBBgIGAwYE +BgUGBgYHBggGEwYWBhcGHAYdBiEGJAYnBisGLAYtBjEA9gY0AXoGOQY6Bj0BCwZABkQGRQZIBkkGTgZP +BlQGVQZcBl0GaAZqBnMFIQZ3BnkGgQUhBooFIQaTBSEGnAUhBqUGrAatBrUGtga9Br4GxQbGBsgGzwbQ +BtcG2AbfBuAG9Ab2BvoG+wb+BwEHCAcJBxAHEQcYBxkHIQQWAhMHIgcjByUHJgALBycHKAcpByoHLQcu +BzMHNAc5BzoHPwdKB0sHTAdOAAsHVQdfB2sHdQd2B3cHeAd5B3oHewd8B30HhweLB4wHjweSB5oHmweL +B6MHpweoB7ICDgezB7QHuwe8B8QHxQfNB84H0wfeB98H4AfqB+sH7gfvB/kH+ggCCAMIBAgOCA8IFwgY +CBkACwcnCBoHKQgbCBwIIQgiCCcIKAgtCDQITQhOCE8IUAhRCFIIUwhUCFUIVghXCFgIWQhaCFsIXAhd +CF4IXwhgCGEIYghjCGUIZwhrCGwIcQhyCHcIeAh9CH4IgwiJCI0DLgiOCJAIlQiWCJsInAihCKIIpwio +CK0IrgizCLgIuQi+CL8IxAjJCMoIzwjQCNUI1gjbCOUI5gjnCOgI6wj0CPUI9gj3CP0I/gj/CQYJDQkO +CQ8JFgkXCRgJHwkgCSEJKwksCS0JOQk6CTsJPAk9CT4JRQlGCUcJSAlPCVAJUQlYCVkJWglhCWIJYwlq +CWsJbAlzCXQJdQl8CX0JfgmFCYYJhwmQCZwJowmkCasJtAnACckJygnLCcwJ2AnfCeAJ6Qn1CfwJ/QoE +Cg0KGQogCicKLgo1CjwKPQpGClIKWQpiCm4KdQp2CncKfgp/CoAKhwqOCpUKlgqXCp4KnwqoCqkKtQq+ +Cr8KywrSCtkK4ArhCugK8QryCvMK/wsGCwcLCAsPCxYLHQseCyULLAszCzQLNQs8Cz0LPgtBC0YLRwtM +C00LUgtTC1gLXQteC2MLZAtpC2oL7AvvC/AL8gx0DPcNeg17DXwNfQ1+DX8NgA2BDYINgw2EDYUNhg2H +DYgNiQ2KDYsNjA2NDY4Njw2QDZENkg2TDZQNlQ2WDZcNmA2ZDZoNmw2cDZ0Nng2fDaANoQ2iDaMNpA2l +DaYNpw2oDakNqg2rDawNrQ2uDa8NsA2xDbINsw20DbUNtg23DbgNuQ26DbsNvA29Db4Nvw3ADcENwg3D +DcQNxQ3GDccNyA3JAUwNyg3LDcwNzQ3ODc8N0A3RDdIN0w3UDdUN1g3XDdgN2Q3aDdsN3A3dDd4N3w3g +DeEN4g3jDeQN5Q3mDecN6A3pDeoN6w3sDe0N7g3vDfAN8Q3yDfMN9A31DfYN9w34DfkOAQ4JDuUPwQ/C +D8MPxA/FD8YPxw/ID8kPyg/LD8wPzQ/OD88P0A/RD9IP0w/UD9UP1g/XD9gP2Q/aD9sP3A/dD94P3w/g +D+EP4g/jD+QP5Q/mD+cP6A/pD+oP6w/sD+0P7g/vD/AP8Q/yD/MP9A/1D/YP9w/4D/kP+g/7D/wP/Q/+ +D/8QABABEAIQAxAEEAUQBhAHEAgQCRAKEAsQDBANEA4QDxAQEBEQEhATEBQQFRAWEBcQGBAZEBoQGxAc +EB0QHhAfECAQIRAiECMQJAD7ECUQJhAnECgQKRAqECsQLBAtEC4QLxAwEDEQMhAzEDQQNRA2EDcQOBA5 +EDoQOxA8ED0QPhA/EEAQQRBCEEMH2RBEEEUQRhBHEEgQSRBKEEsQTBBNEE4QTxBQEFEQUhBTEFQQVRBW +EFcQWBBZEFoQWxBcEF0QXhBfEGAQYRBiEGMQZAH0EGUQZhBnEGgQaRBqEGsQbBBtEG4QbxBwEHEQchBz +EHQQdRB2AksQdxB4EHkQehB7EHwQfRB+AtEQfxCAEIEQghCDEIQQhRCGEIcQiBCJEIoQixCMEI0QjhCP +EJAQkRCSEJMQlBCVEJgQmxCeVSRudWxs3xASAA0ADgAPABAAEQASABMAFAAVABYAFwAYABkAGgAbABwA +HQAeAB8AIAAhACIAIwAkACUAJgAnACgAKQAqACsALAAtAC4ALwAwVk5TUm9vdFYkY2xhc3NdTlNPYmpl +Y3RzS2V5c18QD05TQ2xhc3Nlc1ZhbHVlc18QGU5TQWNjZXNzaWJpbGl0eU9pZHNWYWx1ZXNdTlNDb25u +ZWN0aW9uc1tOU05hbWVzS2V5c1tOU0ZyYW1ld29ya11OU0NsYXNzZXNLZXlzWk5TT2lkc0tleXNdTlNO +YW1lc1ZhbHVlc18QGU5TQWNjZXNzaWJpbGl0eUNvbm5lY3RvcnNdTlNGb250TWFuYWdlcl8QEE5TVmlz +aWJsZVdpbmRvd3NfEA9OU09iamVjdHNWYWx1ZXNfEBdOU0FjY2Vzc2liaWxpdHlPaWRzS2V5c1lOU05l +eHRPaWRcTlNPaWRzVmFsdWVzgAKBBAuBAqSBAyyBBAqACIECqYAFgQMrgQMtgQKqgQQIgACABoECqIEE +CRIABJUmgQMu0gAOADIAMwA0W05TQ2xhc3NOYW1lgASAA18QEFRQUHJlZmVyZW5jZVBhbmXSADcAOAA5 +ADpYJGNsYXNzZXNaJGNsYXNzbmFtZaIAOgA7Xk5TQ3VzdG9tT2JqZWN0WE5TT2JqZWN0XxAQSUJDb2Nv +YUZyYW1ld29ya9IADgA+AD8AQFpOUy5vYmplY3RzgAeg0gA3ADgAQgBDowBDAEQAO1xOU011dGFibGVT +ZXRVTlNTZXTSAA4APgBGAEeAV68QWQBIAEkASgBLAEwATQBOAE8AUABRAFIAUwBUAFUAVgBXAFgAWQBa +AFsAXABdAF4AXwBgAGEAYgBjAGQAZQBmAGcAaABpAGoAawBsAG0AbgBvAHAAcQByAHMAdAB1AHYAdwB4 +AHkAegB7AHwAfQB+AH8AgACBAIIAgwCEAIUAhgCHAIgAiQCKAIsAjACNAI4AjwCQAJEAkgCTAJQAlQCW +AJcAmACZAJoAmwCcAJ0AngCfAKCACYATgCOAJYAngQGMgQGOgQGQgQHPgQHRgQHTgQHwgQHygQH0gQH2 +gQH8gQH+gQIAgQICgQIEgQIGgQIHgQIJgQILgQIMgQIOgQIQgQISgQITgQIYgQIfgQIggQIjgQImgQIp +gQIygQI2gQI5gQI8gQI/gQJCgQJFgQJIgQJLgQJNgQJPgQJQgQJSgQJXgQJZgQJbgQJdgQJegQJggQJh +gQJigQJjgQJkgQJmgQJogQJpgQJrgQJugQJxgQJygQJzgQJ2gQJ4gQJ7gQJ+gQJ/gQKAgQKCgQKDgQKH +gQKKgQKLgQKMgQKOgQKPgQKQgQKTgQKXgQKZgQKbgQKdgQKegQKggQKi1AAOAKIAowCkAKUApgAfAKhd +TlNEZXN0aW5hdGlvblhOU1NvdXJjZVdOU0xhYmVsgBKACoACgBHYAKoADgCrAKwArQAyAK4ArwCwALEA +sgCzALQAtQC2ALBfEA9OU05leHRSZXNwb25kZXJXTlNGcmFtZVhOU3ZGbGFnc1hOU1dpbmRvd1tOU0V4 +dGVuc2lvbltOU1N1cGVydmlld4ALgBCADBEBEoANgA6AD4AL1wCqAA4AqwC5AKwArQCvALoAuwC8AL0A +vgC0ALpaTlNTdWJ2aWV3c4BFgFmAWIBHEQEAgA2ARV8QFnt7MjAsIDE3fSwgezUxMSwgMjk2fX1cVFBM +YXlvdXRWaWV3Vk5TVmlld9IANwA4AMUAxqQAxgDDAMcAO1xOU0N1c3RvbVZpZXdbTlNSZXNwb25kZXJa +bGF5b3V0Vmlld9IANwA4AMoAy6MAywDMADtfEBROU05pYk91dGxldENvbm5lY3Rvcl5OU05pYkNvbm5l +Y3RvctQADgCiAKMApADOAB8A0ADRgCKAAoAUgCHXAKoADgCrANMArADUAK8A1QDWANcA2ADZANoA1VZO +U0NlbGxZTlNFbmFibGVkgBWAIIAWgBcRAQwJgBXXAKoADgC5AKwA3QAyAK8A3gCxAOAA4QDiALYA3ltO +U0ZyYW1lU2l6ZYBhgBCAYxEBLYDKgA+AYV8QF3t7MTc2LCAyNTB9LCB7MTU5LCAzMn193ADnAA4A6ADp +AOoA6wDsAO0A7gDvAPAA8QDyAPMA9AD1APYA9wD4APkA0AD7APwA/VtOU0NlbGxGbGFnc18QE05TQWx0 +ZXJuYXRlQ29udGVudHNfEBJOU1BlcmlvZGljSW50ZXJ2YWxeTlNCdXR0b25GbGFnczJfEA9OU0tleUVx +dWl2YWxlbnRaTlNDb250ZW50c1lOU1N1cHBvcnRdTlNDb250cm9sVmlld18QD05TUGVyaW9kaWNEZWxh +eVxOU0NlbGxGbGFnczJdTlNCdXR0b25GbGFncxIEAf4AgB+AHBAZEAGAHYAYgBmAFBDIEggAAAAT//// +/4aCQP9fEBNNb3N0cmFyIGNlcnRpZmljYWRv1AAOAQABAQECAQMBBAEFAQZWTlNTaXplVk5TTmFtZVhO +U2ZGbGFnc4AbI0AqAAAAAAAAgBoRBBRcTHVjaWRhR3JhbmRl0gA3ADgBCQEKogEKADtWTlNGb250UNIA +DgENAQ4BC1lOUy5zdHJpbmeAHtIANwA4ARABEaMBEQESADtfEA9OU011dGFibGVTdHJpbmdYTlNTdHJp +bmfSADcAOAEUARWkARUBFgDTADtcTlNCdXR0b25DZWxsXE5TQWN0aW9uQ2VsbNIANwA4ARgBGaUBGQEa +AMMAxwA7WE5TQnV0dG9uWU5TQ29udHJvbF8QFnNob3dDZXJ0aWZpY2F0ZVZpZXdlcjrSADcAOAEdAR6j +AR4AzAA7XxAVTlNOaWJDb250cm9sQ29ubmVjdG9y1AAOAKIAowCkAKUApgAfASOAEoAKgAKAJF1fZmly +c3RLZXlWaWV31AAOAKIAowCkAKUApgAfASmAEoAKgAKAJlxfbGFzdEtleVZpZXfUAA4AogCjAKQApQEt +AB8BL4ASgCiAAoEBi9wBMQAOATIBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFG +AUdcTlNXaW5kb3dWaWV3XE5TU2NyZWVuUmVjdF1OU1dpbmRvd1RpdGxlWU5TV1RGbGFnc11OU1dpbmRv +d0NsYXNzXE5TV2luZG93UmVjdFlOU01heFNpemVfEA9OU1dpbmRvd0JhY2tpbmdfEBFOU1dpbmRvd1N0 +eWxlTWFza1lOU01pblNpemVbTlNWaWV3Q2xhc3OALYEBioEBh4AqEkB4AACAK4ApgQGJEAIQD4EBiIAs +XxAYe3s1MTcsIDQ1MH0sIHs1OTUsIDQyOX19XxAVPDwgZG8gbm90IGxvY2FsaXplID4+0gAOAQ0BDgFM +gB5UVmlld9cAqgAOALkArADdAK0ArwC0ALsBUAC+AVEAtAFTgA2AWYAugQGFgA2BAYbSAA4APgBGAVaA +V6EBV4Av2ACqAA4AuQCsAN0ArQAyAK8BPACxAVsAswFcALQAtgE8gC2AEIAwgQGEgA2AD4At0gAOAD4A +RgFigFekAWMBZAFlALqAMYA4gEGARdgAqgAOAKsA0wCsANQArQCvAVcA1gFqAWsA2QDaALQBV4AvgCCA +MoAzCYANgC9fEBd7ezE0OSwgMzg3fSwgezE0NSwgMTh9fd0A5wAOAOgA6QDqAXEA6wDsAO0A7gDvAPAA +8QFyAPMA9AD1AUQBdQD0AXcA+QFjAPsBegF7XxAQTlNBbHRlcm5hdGVJbWFnZRIkAf4AgB+AHIA1gByA +NIAZgDEQABJIPFH/XxASQ29tcGFydGlyIGVzdGUgTWFj0gAOAX4BfwGAW05TSW1hZ2VOYW1lgDeANlhO +U1N3aXRjaNIANwA4AYMBhKIBhAA7XxATTlNCdXR0b25JbWFnZVNvdXJjZdgAqgAOAKsA0wCsANQArQCv +AVcA1gGIAYkBigDaALQBV4AvgCCAOYA6EQEJCYANgC9fEBZ7ezU0NSwgMzc5fSwgezMyLCAzMn193QDn +AA4A6AGQAOkA6gDrAOwA7QDuAO8A8ADxAPIA8wD0AZMA9QGUAZUBlgD5AWQA+wD8AZldTlNOb3JtYWxJ +bWFnZYAfgByAPBCCgECAO4AZgDgSCERA/2YAQQBiAG8AdQB0ICbTAA4AMgGcAZ0BngGfXk5TUmVzb3Vy +Y2VOYW1lgD+APYA+V05TSW1hZ2VaaWNvbi1hYm91dNIANwA4AaMBpKIBpAA7XxAQTlNDdXN0b21SZXNv +dXJjZdIADgENAQ4BC4Ae2ACqAA4AqwDTAKwA1ACtAK8BVwDWAaoBqwDZANoAtAFXgC+AIIBCgEMJgA2A +L18QFnt7MTgsIDM4N30sIHsxMTksIDE4fX3dAOcADgDoAOkA6gFxAOsA7ADtAO4A7wDwAPEA8gDzAPQA +9QFEAXUA9AG1APkBZQD7AXoBe4AfgByANYAcgESAGYBBXxAQQWN0aXZhciB0ZWxlcG9ydN0AqgAOAboB +uwCrAQoAuQCsAK0BvACvAb0BvgFXAcABwQF6AcIA+QHEALMAtADaAVcA2gHJXk5TVGFiVmlld0l0ZW1z +WU5TVHZGbGFnc18QEU5TRHJhd3NCYWNrZ3JvdW5kXxAWTlNBbGxvd1RydW5jYXRlZExhYmVsc18QFU5T +U2VsZWN0ZWRUYWJWaWV3SXRlbYAvgQGDgFuAWoAZgEaADQmALwmAXNIADgA+AEYBzIBXoQCwgAvSAA4A +PgBGAdCAV6IApgHSgAqASNgAqgAOAKsA0wCsANQArQCvALAB1QHWAdcB2ADaALQAsIALgFaASYBKEQEi +CYANgAtfEBV7ezE5LCAtMn0sIHs1MDEsIDE3fX3YAOcADgHeAOwA7QDuAPAB3wDyAeAB4QHiAeMB0gHl +AeZfEBFOU0JhY2tncm91bmRDb2xvcltOU1RleHRDb2xvcoBVgE2AS4BMgEgSAEIAAIBSXxBTT3JnYW5p +emEgbG9zIE1hY3MgY29tcGFydGlkb3MgZGUgYXJyaWJhIGFscmVkZWRvciBkZSB0dSBwYW50YWxsYSBw +YXJhIGNvbnRyb2xhcmxvcy7UAA4BAAEBAQIBAwHqAQUB7IAbI0AmAAAAAAAAgBoRDBzVAA4B7gHvAfAB +8QHyAfMB9AH1AfZXTlNDb2xvclxOU0NvbG9yU3BhY2VbTlNDb2xvck5hbWVdTlNDYXRhbG9nTmFtZYBR +gFAQBoBPgE5WU3lzdGVtXGNvbnRyb2xDb2xvctMADgHvAfoB8gH8Af1XTlNXaGl0ZYBREANLMC42NjY2 +NjY2OQDSADcAOAH/Ae6iAe4AO9UADgHuAe8B8AHxAfICAgH0AgMB9oBRgFSAU4BOXxAQY29udHJvbFRl +eHRDb2xvctMADgHvAfoB8gH8AgiAUUIwANIANwA4AgoCC6QCCwEWANMAO18QD05TVGV4dEZpZWxkQ2Vs +bNIANwA4Ag0CDqUCDgEaAMMAxwA7W05TVGV4dEZpZWxk0gA3ADgCEAIRowIRAhIAO15OU011dGFibGVB +cnJheVdOU0FycmF5XxAWe3sxMCwgMzN9LCB7NTQ5LCAzMTl9fdIANwA4AhUAw6MAwwDHADtfEBZ7ezEz +LCAxMH0sIHs1NjksIDM2NX190gAOAD4ARgIZgFejAckCGwIcgFyAYIDN1gAOAh4AwwIfAe4ApAIgAiEA +sAC6AeECJVxOU0lkZW50aWZpZXJZTlNUYWJWaWV3gF+AXYALgEWATYBeVmxheW91dGsARABpAHMAcABv +AHMAaQBjAGkA8wBu0gA3ADgCKQIqogIqADtdTlNUYWJWaWV3SXRlbdUADgDDAh8B7gCkAiAA3gC6AeEC +MIBfgGGARYBNgMzVAKoADgCrALkArAArALsCNAI1AL6AAIBZgMuAYtIADgA+AEYCOIBXoQDVgBXSAA4A +PgBGAjyAV6kCPQI+ANACQAJBAkICQwJEAkWAZIBogBSAbYBxgHWAiIDCgMbXAKoADgCrANMArADUAK8A +1QHVAkkCSgJLANoA1YAVgFaAZYBmEQEkCYAVXxAWe3sxODUsIDg3fSwgezM0MSwgMjh9fdgA5wAOAd4A +7ADtAO4A8AHfAPIB4AHhAlIB4wI9AlUB5oBVgE2AZ4BMgGQSEEIAAIBSXxBNUGFyYSBlbGltaW5hciB1 +biBob3N0IGRlIGNvbmZpYW56YSwgc2VsZWNjaW9uYXJsbyB5IHByZXNpb25hciBsYSB0ZWNsYSBib3Jy +YXLXAKoADgCrANMArADUAK8A1QDWAlsCXAJdANoA1YAVgCCAaYBqE/////+AAAEMCYAVXxAXe3sxNzYs +IDI1MH0sIHsxNzMsIDMyfX3cAOcADgDoAOkA6gDrAOwA7QDuAO8A8ADxAPIA8wD0APUA9gJkAmUA+QI+ +APsA/AD9gB+AHIBsgGuAGYBobxAUAEUAcwBjAG8AZwBlAHIAIABjAGUAcgB0AGkAZgBpAGMAYQBkAG8g +JtIADgENAQ4BC4Ae1wCqAA4AqwDTAKwA1ACvANUA1gJuAm8A2QDaANWAFYAggG6AbwmAFV8QF3t7MTgz +LCAyODZ9LCB7MTU1LCAxOH193QDnAA4A6ADpAOoBcQDrAOwA7QDuAO8A8ADxAPIA8wD0APUBRAF1APQC +eAD5AkAA+wF6AXuAH4AcgDWAHIBwgBmAbW8QFgBIAGEAYgBpAGwAaQB0AGEAcgAgAGMAbwBkAGkAZgBp +AGMAYQBjAGkA8wBu1wCqAA4AqwDTAKwA1ACvANUB1QJ/AoAA2QDaANWAFYBWgHKAcwmAFV8QFnt7MjMs +IDI4N30sIHsxNDgsIDE3fX3YAOcADgHeAOwA7QDuAPAB3wDyAeAB4QKHAPkCQQKKAeaAVYBNgHSAGYBx +EgRAAACAUm0AQwBvAGQAaQBmAGkAYwBhAGMAaQDzAG4AOt8QEgCqAA4AqwKOAo8CkAHeApECkgEKApMA +rADUApQArwKVApYClwDVApkCmgKbAfwCnAHhAp4CnwD5AqECSwDaAPYA1QKkAqUCpltOU1Byb3RvQ2Vs +bFlOU051bVJvd3NeTlNTZWxlY3RlZENlbGxbTlNDZWxsQ2xhc3NfEBVOU0NlbGxCYWNrZ3JvdW5kQ29s +b3JaTlNDZWxsU2l6ZVlOU051bUNvbHNfEBJOU0ludGVyY2VsbFNwYWNpbmddTlNNYXRyaXhGbGFnc1dO +U0NlbGxzgBWAh4B2gISAeIBNgIOAhoAZgIEJgBWAghJEKCAAgHdfEBZ7ezE4NSwgMTN9LCB7Mjk5LCA1 +OH190gAOAD4ARgKqgFejApwCrAKtgHiAfYB/3QDnAA4A6ADpAOoBcQDrAOwA7QDuAO8A8ADxAq8A8wKx +APUBegKyArECtAD5AkIA+wF6AXsT/////4QB/gCAH4B8gHqAfIB5gBmAdV8QI1ByZWd1bnRhcm1lIHNp +IGNvbmZpYXIgZW4gZXN0ZSBob3N00gAOAX4BfwK6gDeAe11OU1JhZGlvQnV0dG9u0gAOAQ0BDgELgB7e +AOcADgDoAOkA6gFxAOsA7ADtAO4A7wDwAPECvwDyAPMCsQD1AXoCsgKxAsQA+QJCAPsBegF7APZVTlNU +YWeAH4B8gHqAfIB+gBmAdW8QKgBSAGUAYwBoAGEAegBhAHIAIABzAGkAIABlAGwAIABoAG8AcwB0ACAA +bgBvACAAZQBzAHQA4QAgAHkAYQAgAGUAbgAgAGwAYQAgAGwAaQBzAHQAYd4A5wAOAOgA6QDqAXEA6wDs +AO0A7gDvAPAA8QK/APIA8wD0AssBegKyAPQCzgD5AkIC0QF6AXsBRIAfgBwQS4B6gByAgIAZgHURAZBv +EBcAQQBjAGUAcAB0AGEAcgAgAGEAdQB0AG8AbQDhAHQAaQBjAGEAbQBlAG4AdABlWXsyOTksIDE4fVZ7 +NCwgMn3aAOcADgDpAOoBcQDsAO0A7wDwAPEA8gDzAssBegKyAtgA+QLRAXoBe4AfgHqAhYAZVVJhZGlv +0wAOAe8B+gHyAfwC3YBRQjEA0gA3ADgC3wLgpQLgARoAwwDHADtYTlNNYXRyaXjeAKoC4gAOAKsC4wLk +AuUAuQLmAKwArwLnAugC6QDVAusC7ALtAUQC7gLvAvAC8QCzANUC8wL0AvRbTlNIU2Nyb2xsZXJYTlNz +RmxhZ3NcTlNDb3JuZXJWaWV3XxAQTlNIZWFkZXJDbGlwVmlld1xOU1Njcm9sbEFtdHNbTlNWU2Nyb2xs +ZXJdTlNOZXh0S2V5Vmlld11OU0NvbnRlbnRWaWV3gBWAvIDBgMCAlICRgIlPEBBBIAAAQSAAAEGYAABB +mAAAgBWAuICKgIrSAA4APgBGAviAV6UC9ALzAusC7wLugIqAuIC8gJGAlNoAqgAOAKsC/wC5AKwDAAMB +AK8C6AJDAwMDBAMFAwYDBwMIAwkCQwMIWU5TY3ZGbGFnc1lOU0RvY1ZpZXdZTlNCR0NvbG9ygIiAt4C2 +EASAixEJAICMgKKAiICM0gAOAD4ARgMOgFehAwiAjN8QFQCqAxEADgG7AxIDEwHeAxQC5AMVAxYDFwCs +AN0A1AAyAxgDGQCvAxoDGwL0AXoDHQMeAx8DIAKfANoC7gMkAPYDJQC+AyYA2gMoAykBRQL0AysDLF8Q +H05TRHJhZ2dpbmdTb3VyY2VNYXNrRm9yTm9uTG9jYWxfEBNOU09yaWdpbmFsQ2xhc3NOYW1lXE5TSGVh +ZGVyVmlld18QEk5TQWxsb3dzVHlwZVNlbGVjdF8QF05TSW50ZXJjZWxsU3BhY2luZ1dpZHRoXxAZTlND +b2x1bW5BdXRvcmVzaXppbmdTdHlsZV8QGE5TSW50ZXJjZWxsU3BhY2luZ0hlaWdodFtOU0dyaWRDb2xv +cl8QHE5TRHJhZ2dpbmdTb3VyY2VNYXNrRm9yTG9jYWxeTlNUYWJsZUNvbHVtbnNbTlNSb3dIZWlnaHSA +ioC1EhrAgACAjoCQgIYJgJQjQAgAAAAAAAAjQAAAAAAAAACAjwmAjYCygIqAlyNAMQAAAAAAAFtUUFRh +YmxlVmlld1tOU1RhYmxlVmlld1p7MzMzLCAxMDR91gCqAA4ArADdAK8DLgLvAzIAvgMzAu8DCICRgJOA +koCRgIzaAKoADgCrAv8AuQCsAwADAQCvAugCQwMDAzkDBQM6AwcDIAMJAkMDIICIgLeAv4C+gJCAooCI +gJBZezMzMywgMTd90gA3ADgDQQNCpANCAMMAxwA7XxARTlNUYWJsZUhlYWRlclZpZXfVAKoADgCrAKwA +rwJDA0UDRgC+AkOAiICWgJWAiF8QFHt7LTI2LCAwfSwgezE2LCAxN3190gA3ADgDSgNLpANLAMMAxwA7 +XV9OU0Nvcm5lclZpZXfSAA4APgBGA06AV6MDTwNQA1GAmIClgKraA1MADgNUAh4DVQNWA1cDWANZAy4A +2gNbA1wDXQNeA18A9gNgA2EDCF5OU0lzUmVzaXplYWJsZVxOU0hlYWRlckNlbGxXTlNXaWR0aFpOU0Rh +dGFDZWxsXk5TUmVzaXppbmdNYXNrWk5TTWluV2lkdGhaTlNNYXhXaWR0aAmApICagJkjQGJgAAAAAACA +oCNARAAAAAAAACNAj0AAAAAAAICMVG5hbWXXAOcADgHeAOwA7QDwAd8DZQNmA2cDaAHjAXoDahIEgf4A +gJ+AnICbgEyAnVZOb21icmXTAA4B7wH6AfIB/ANugFFLMC4zMzMzMzI5OQDVAA4B7gHvAfAB8QHyAgIB +9ANyAfaAUYBUgJ6ATl8QD2hlYWRlclRleHRDb2xvctIANwA4A3YDd6UDdwILARYA0wA7XxARTlNUYWJs +ZUhlYWRlckNlbGzYAOcADgHeAOwA7QDuAPAB3wN5AeADCQN8AeMDCAN/AeYSFCH+QIBVgKKAoYBMgIwS +AAIIAIBSWVRleHQgQ2VsbNUADgHuAe8B8AHxAfIB8wH0A4UB9oBRgFCAo4BOXxAWY29udHJvbEJhY2tn +cm91bmRDb2xvctIANwA4A4kDiqIDigA7XU5TVGFibGVDb2x1bW7aA1MADgNUAh4DVQNWA1cDWANZAy4A +2gNbA44DjwOQA5EA9gOSA2EDCAmApICngKYjQFyAAAAAAACAqSNAIAAAAAAAAICMV2FkZHJlc3PXAOcA +DgHeAOwA7QDwAd8DZQNmA2cDmAHjAXoDaoCfgJyAqIBMgJ1pAEQAaQByAGUAYwBjAGkA8wBu2ADnAA4B +3gDsAO0A7gDwAd8DeQHgAwkDfAHjAwgDfwHmgFWAooChgEyAjIBS2AAOA1QCHgNVA1YDWANZAy4DWwOl +A6YDpwOoA6kDYQMIgKSArICrI0BPgAAAAAAAgLAjQE1Jr+AAAACAjFtjZXJ0aWZpY2F0ZdcA5wAOAd4A +7ADtAPAB3wDyA2YDrgOvAeMBegNqgJ+AroCtgEyAnVtDZXJ0aWZpY2Fkb9UADgHuAe8B8AHxAfICnwH0 +A7YB9oBRgIaAr4BOW2hlYWRlckNvbG9y3ADnAA4A6ADpAOoA6wDsAO0A7gDvAPAA8QDyAPMA9ALLA7wA +9AO+AeMDCALRA8EDwoAfgBwQJIAcgLGATICMEggCAAAT/////4a2QP9TVmVy1QAOAe4B7wHwAfEB8gPG +AfQDxwH2gFGAtICzgE5ZZ3JpZENvbG9y0wAOAe8B+gHyAfwDzIBRRDAuNQDSADcAOAPOA8+iA88AO15O +U0NsYXNzU3dhcHBlcl8QFXt7MSwgMTd9LCB7MzMzLCAxMDR9fdIANwA4A9ID06QD0wDDAMcAO1pOU0Ns +aXBWaWV32ACqA9UADgCrAKwD1gCvA9cCQwJDA9oD2wC+A9wCQwPeWE5TVGFyZ2V0WE5TQWN0aW9uWU5T +UGVyY2VudICIgIiAu4C5gLqAiCM/7YLYIAAAAF8QGXt7LTEwMCwgLTEwMH0sIHsxNSwgMTgwfX1cX2Rv +U2Nyb2xsZXI60gA3ADgD4gPjpQPjARoAwwDHADtaTlNTY3JvbGxlctkAqgPVAA4AqwLjAKwD1gCvA9cC +QwJDA9oD6AD2AL4D3AJDA+uAiICIgLuAvYC6gIgjP++x+yAAAABfEBl7ey0xMDAsIC0xMDB9LCB7NDYz +LCAxNX190gAOAD4ARgPvgFehAyCAkF8QE3t7MSwgMH0sIHszMzMsIDE3fX1fEBh7ezE4NSwgMTE2fSwg +ezMzNSwgMTIyfX3SADcAOAP0A/WkA/UAwwDHADtcTlNTY3JvbGxWaWV31wCqAA4AqwDTAKwA1ACvANUB +1QP5A/oCSwDaANWAFYBWgMOAxAmAFV8QFXt7MTQsIDU0fSwgezE1NywgMTd9fdgA5wAOAd4A7ADtAO4A +8AHfAPIB4AHhBAEA+QJEAooB5oBVgE2AxYAZgMKAUl8QFlBldGljaW9uZXMgZGUgY29udHJvbDrXAKoA +DgCrANMArADUAK8A1QHVBAkECgDZANoA1YAVgFaAx4DICYAVXxAVe3s4LCAyMjF9LCB7MTYzLCAxN319 +2ADnAA4B3gDsAO0A7gDwAd8A8gHgAeEEEQD5AkUCigHmgFWATYDJgBmAxoBSXxATSG9zdHMgZGUgY29u +ZmlhbnphOlp7NTQ5LCAzMTl9WVNlZ3VyaWRhZNYADgIeAMMCHwHuAKQCIAQaBBsAugHhBB6AX4DOgM+A +RYBNgQGCV29wdGlvbnPVAKoADgCrALkArAArALsEIwQkAL6AAIBZgQGBgNDSAA4APgBGBCeAV6EEKIDR +1wCqAA4AuQCsAN0AMgCvBBsAsQQsAOEELQC2BBuAz4AQgNKBAYCAD4DP0gAOAD4ARgQygFevEBYEMwQ0 +BDUENgQ3BDgEOQQ6BDsEPAQ9BD4EPwRABEEEQgRDBEQERQRGBEcESIDTgNeA24DfgOOA54DrgO+A84D3 +gQEVgQEZgQEdgQEhgQElgQFSgQFjgQFngQFsgQFwgQF4gQF81wCqAA4AqwDTAKwA1ACvBCgA1gRMBE0A +2QDaBCiA0YAggNSA1QmA0V8QF3t7MjExLCAxMzJ9LCB7MTQ2LCAxOH193QDnAA4A6ADpAOoBcQDrAOwA +7QDuAO8A8ADxAPIA8wD0APUBRAF1APQEVgD5BDMA+wF6AXuAH4AcgDWAHIDWgBmA028QEwBTAPMAbABv +ACAAcwBpACAAZQBzACAAbQBlAG4AbwByACAAZABl1wCqAA4AqwDTAKwA1ACvBCgB1QRdBF4A2QDaBCiA +0YBWgNiA2QmA0V8QFnt7NjcsIDE3N30sIHsxMDQsIDIxfX3YAOcADgHeAOwA7QDuAPAB3wDyAeAB4QRl +APkENAKKAeaAVYBNgNqAGYDXgFJfEA9UcmFuc2ZlcmVuY2lhczrXAKoADgCrANMArADUAK8EKAHVBG0E +bgDZANoEKIDRgFaA3IDdCYDRXxAVe3s1NSwgMTd9LCB7MTE2LCAxN3192ADnAA4B3gDsAO0A7gDwAd8A +8gHgAeEEdQD5BDUCigHmgFWATYDegBmA24BSbgBBAGMAdAB1AGEAbABpAHoAYQBjAGkA8wBuADrXAKoA +DgCrANMArADUAK8EKAHVBH0EfgDZANoEKIDRgFaA4IDhCYDRXxAWe3sxMDIsIDI4N30sIHs2OSwgMTd9 +fdgA5wAOAd4A7ADtAO4A8AHfAPIB4AHhBIUA+QQ2AooB5oBVgE2A4oAZgN+AUldDYW1iaW861wCqAA4A +qwDTAKwA1ACvBCgA1gSNBI4A2QDaBCiA0YAggOSA5QmA0V8QFnt7MTgzLCA3NH0sIHsyNTUsIDE4fX3d +AOcADgDoAOkA6gFxAOsA7ADtAO4A7wDwAPEA8gDzAPQA9QFEAXUA9ASXAPkENwD7AXoBe4AfgByANYAc +gOaAGYDjbxAjAE0AbwBzAHQAcgBhAHIAIABlAHMAdABhAGQAbwAgAGUAbgAgAGwAYQAgAGIAYQByAHIA +YQAgAGQAZQAgAG0AZQBuAPoAc9cAqgAOAKsA0wCsANQArwQoANYEngSfANkA2gQogNGAIIDogOkJgNFf +EBd7ezE4MywgMTgwfSwgezI1OSwgMTh9fd0A5wAOAOgA6QDqAXEA6wDsAO0A7gDvAPAA8QKvAPMA9AD1 +AUQBdQD0BKgA+QQ4APsBegF7gB+AHIA1gByA6oAZgOdfECZBcnJhc3RyYXIgeSBTb2x0YXIgYXJjaGl2 +b3MgZW50cmUgTWFjc9cAqgAOAKsA0wCsANQArwQoANYErwSwANkA2gQogNGAIIDsgO0JgNFfEBd7ezIx +MSwgMTA4fSwgezEzMiwgMTh9fd0A5wAOAOgA6QDqAXEA6wDsAO0A7gDvAPAA8QDyAPMA9AD1AUQBdQD0 +BLkA+QQ5APsBegF7gB+AHIA1gByA7oAZgOtvEBAAUwDzAGwAbwAgAHMAaQAgAHAAcgBlAHMAaQBvAG4A +b9cAqgAOAKsA0wCsANQArwQoANYEwATBANkA2gQogNGAIIDwgPEJgNFfEBd7ezE4MywgMTU2fSwgezE5 +NCwgMTh9fd0A5wAOAOgA6QDqAXEA6wDsAO0A7gDvAPAA8QKvAPMA9AD1AUQBdQD0BMoA+QQ6APsBegF7 +gB+AHIA1gByA8oAZgO9fEBtTaW5jcm9uaXphciBlbCBwb3J0YXBhcGVsZXPXAKoADgCrANMArADUAK8E +KADWBNEE0gDZANoEKIDRgCCA9ID1CYDRXxAXe3sxODMsIDI4Nn0sIHsxNzksIDE4fX3dAOcADgDoAOkA +6gFxAOsA7ADtAO4A7wDwAPEA8gDzAPQA9QFEAXUA9ATbAPkEOwD7AXoBe4AfgByANYAcgPaAGYDzbxAY +AEMAYQBtAGIAaQBhAHIAIABzAPMAbABvACAAcwBpACAAcAByAGUAcwBpAG8AbgBv1wCqAA4AqwDTAKwA +1ACvBCgE4QTiBOMA2QDaBCiA0YEBFID4gPkJgNFfEBd7ezM2NCwgMTAzfSwgezEyOSwgMjZ9fd8QEgDn +BOgE6QDoAOkADgDqAOsA7QTqAO4E6wTsBO0A7wDwBO4A8QTvANoA9gD0AssE8gD2BPMA+QT1BDwB/ADa +ANoC0QT5BPoE+18QGk5TTWVudUl0ZW1SZXNwZWN0QWxpZ25tZW50XxAPTlNBcnJvd1Bvc2l0aW9uWk5T +TWVudUl0ZW1fEA9OU1ByZWZlcnJlZEVkZ2VfEBJOU1VzZXNJdGVtRnJvbU1lbnVdTlNBbHRlcnNTdGF0 +ZVZOU01lbnUT/////4RB/kAJgByBAROA+oAZgPuA9wkJEQgAgPwSBoJA/9IADgENAQ4BC4Ae3APVAA4E +/wUABQEFAgUDBQQE7gPWAr8FBQTjBQcFCAUJAPQFCwUMBQ0E+gUPBRAA9ldOU1RpdGxlXxARTlNLZXlF +cXVpdk1vZE1hc2taTlNLZXlFcXVpdl1OU01uZW1vbmljTG9jWU5TT25JbWFnZVxOU01peGVkSW1hZ2VX +TlNTdGF0ZYD5gQEDgP0SABAAAIAcEn////+A/oEBAID8gQECEBTTAA4E/wUSBRMFFAUVW05TTWVudUl0 +ZW1zgQESgQEEgQEFaSMYACAAQwBvAG0AYQBuAGQAb9MADgAyAZwBnQGeBRqAP4A9gP9fEA9OU01lbnVD +aGVja21hcmvTAA4AMgGcAZ0BngUfgD+APYEBAV8QEE5TTWVudU1peGVkU3RhdGVfEBFfcG9wVXBJdGVt +QWN0aW9uOtIANwA4BSME6qIE6gA70gAOAQ0BDgUmgB5aT3RoZXJWaWV3c9IADgA+AEYFKYBXpQT1BSsF +LAUtBS6A+4EBBoEBCYEBDIEBD9sD1QAOBP8FAAUBBQIFAwUEBO4D1gK/BOMFBwUyBQkA9AULBQwFDQT6 +BTcFOID5gQEDgQEHgByA/oEBAID8gQEIEBNoIyUAIABPAHAAYwBpAPMAbtsD1QAOBP8FAAUBBQIFAwUE +BO4D1gK/BOMFBwU9BQkA9AULBQwFDQT6BUIFQ4D5gQEDgQEKgByA/oEBAID8gQELEBJpIwMAIABDAG8A +bgB0AHIAbwBs2wPVAA4E/wUABQEFAgUDBQQE7gPWAr8E4wUHBUgFCQD0BQsFDAUNBPoFTQVOgPmBAQOB +AQ2AHID+gQEAgPyBAQ4QEWwh5wAgAE0AYQB5APoAcwBjAHUAbABhAHPbA9UADgT/BQAFAQUCBQMFBATu +A9YCvwTjBQcFUwUJAPQFCwUMBQ0E+gVYBVmA+YEBA4EBEIAcgP6BAQCA/IEBERAQbxARIeoAIABCAGwA +bwBxAHUAZQBvACAATQBhAHkA+gBzAGMALtIANwA4BVwE7qIE7gA70gA3ADgFXgVfpgVfBWABFQEWANMA +O18QEU5TUG9wVXBCdXR0b25DZWxsXk5TTWVudUl0ZW1DZWxs0gA3ADgFYgVjpgVjARkBGgDDAMcAO11O +U1BvcFVwQnV0dG9u1wCqAA4AqwDTAKwA1ACvBCgB1QVnBWgA2QDaBCiA0YBWgQEWgQEXCYDRXxAWe3sz +NjcsIDI2MH0sIHszOCwgMTd9fdgA5wAOAd4A7ADtAO4A8AHfAPIB4AHhBW8B4wQ9BXIB5oBVgE2BARiA +TIEBFRIQQAAAgFJVQ29ydG/XAKoADgCrANMArADUAK8EKADWBXgFeQDZANoEKIDRgCCBARqBARsJgNFf +EBd7ezE4MywgMjE0fSwgezI2MSwgMTh9fd0A5wAOAOgA6QDqAXEA6wDsAO0A7gDvAPAA8QDyAPMA9AD1 +AUQBdQD0BYIA+QQ+APsBegF7gB+AHIA1gByBARyAGYEBGV8QJUludGVudGFyIGRlc3BlcnRhciBsb3Mg +TWFjcyBlbiByZXBvc2/XAKoADgCrANMArADUAK8EKADWBYkFigDZANoEKIDRgCCBAR6BAR8JgNFfEBd7 +ezE4MywgMjYyfSwgezE3NCwgMTh9fd0A5wAOAOgA6QDqAXEA6wDsAO0A7gDvAPAA8QDyAPMA9AD1AUQB +dQD0BZMA+QQ/APsBegF7gB+AHIA1gByBASCAGYEBHV8QF0NhbWJpYXIgdHJhcyB1biByZXRhcmRv1wCq +AA4AqwDTAKwA1ACvBCgA1gWaBZsA2QDaBCiA0YAggQEigQEjCYDRXxAXe3sxODMsIDIzOH0sIHsyNTks +IDE4fX3dAOcADgDoAOkA6gFxAOsA7ADtAO4A7wDwAPEA8gDzAPQA9QFEAXUA9AWkAPkEQAD7AXoBe4Af +gByANYAcgQEkgBmBASFfECNDYW1iaWFyIGFsIHRvY2FyIGVsIGJvcmRlIGRvcyB2ZWNlc9cAqgAOAKsA +0wCsANQArwQoAdUFqwWsANkA2gQogNGAVoEBJoEBJwmA0V8QFnt7MzY3LCAxMzF9LCB7NjQsIDIyfX3a +AOcADgHeAO0A7gWxAPABvAWyAd8FswHgBbUA+QRBBbgFuQDaBbsFvFtOU0Zvcm1hdHRlcl8QE05TUGxh +Y2Vob2xkZXJTdHJpbmcT/////5Rx/kGAVYEBToAZgQElgQEoEgRABAAJgQFNgQFQ3xARAA4FvgW/BcAF +wQXCBcMFxAXFBcYFxwXIBckFygXLBcwFzQXOBc8F0AXRACsF0wXUBdUF1gXXANoF2QArBdsF3AXdBd1W +TlMubmlsWk5TLmRlY2ltYWxWTlMubmFuW05TLnJvdW5kaW5nV05TLnplcm9fEBBOUy5uZWdhdGl2ZWF0 +dHJzVk5TLm1heF1OUy5hdHRyaWJ1dGVzXxARTlMucG9zaXRpdmVmb3JtYXRfEA9OUy5hbGxvd3NmbG9h +dHNfEBFOUy5uZWdhdGl2ZWZvcm1hdF8QEE5TLnBvc2l0aXZlYXR0cnNbTlMudGhvdXNhbmRWTlMubWlu +XE5TLmxvY2FsaXplZF8QD05TLmhhc3Rob3VzYW5kc4EBTIEBSIEBP4EBSoAAgQE5gQE+gQFEgQEpgQE4 +CYEBOoAAgQFGgQE2CAjTAA4F4AA+BeEF4gXvV05TLmtleXOBAUesBeMF5AXlBeYF5wXoBekF6gXrBewF +7QXugQEqgQErgQEsgQEtgQEugQEvgQEwgQExgQEygQEzgQE0gQE1rAXcBdcF0wXUBdAF9QX2BfcF1QX5 +BdkF24EBNoEBOIEBOYEBPoEBP4EBQIEBQYEBQ4EBRIEBRYEBOoEBRldtaW5pbXVtXnBvc2l0aXZlRm9y +bWF0XxAXYXR0cmlidXRlZFN0cmluZ0Zvclplcm9fEB90ZXh0QXR0cmlidXRlc0Zvck5lZ2F0aXZlVmFs +dWVzXxAQZGVjaW1hbFNlcGFyYXRvcl8QEWZvcm1hdHRlckJlaGF2aW9yVmxvY2FsZVxhbGxvd3NGbG9h +dHNXbWF4aW11bV8QFXVzZXNHcm91cGluZ1NlcGFyYXRvcl5uZWdhdGl2ZUZvcm1hdF8QEWdyb3VwaW5n +U2VwYXJhdG9y1wAOBgkGCgYLBgwGDQYOBg8F3QF6AUQBegYRBd1aTlMuY29tcGFjdFtOUy5leHBvbmVu +dF5OUy5tYW50aXNzYS5ib1lOUy5sZW5ndGhbTlMubWFudGlzc2FbTlMubmVnYXRpdmWBATcITxAQAAAA +AAAAAAAAAAAAAAAAAAjSADcAOAYUBhWiBhUAO18QGk5TRGVjaW1hbE51bWJlclBsYWNlaG9sZGVyUjBL +0wAOBhgBEgYZBhoF2VxOU0F0dHJpYnV0ZXOBAT2BATuBATpRMNMADgXgAD4GHgYfBiCBATygoNIANwA4 +BiIGI6IGIwA7XE5TRGljdGlvbmFyedIANwA4BiUGJqIGJgA7XxASTlNBdHRyaWJ1dGVkU3RyaW5n0wAO +BeAAPgYeBikGKoEBPKCgYQCgEQPo0gAOBi4GLwD0XU5TLmlkZW50aWZpZXKBAUKAHNIANwA4BjIGM6IG +MwA7WE5TTG9jYWxl1wAOBgkGCgYLBgwGDQYOBg8A2gF6AUQBRAY3Bd2BATcJTxAQAABAAAAAAAAAAAAA +AAAAAAhRLNIANwA4BjsGPKMGPAYjADtfEBNOU011dGFibGVEaWN0aW9uYXJ50gAOARIGGQY/gQE9gQFJ +0wAOBhgBEgYZBhoGQ4EBPYEBO4EBS1NOYU7SADcAOAZGBkejBkcFsQA7XxARTlNOdW1iZXJGb3JtYXR0 +ZXJmAHQAYQBtAGEA8QBv1QAOAe4B7wHwAfEB8gKfAfQGTAH2gFGAhoEBT4BOXxATdGV4dEJhY2tncm91 +bmRDb2xvctUADgHuAe8B8AHxAfICAgH0BlIB9oBRgFSBAVGATll0ZXh0Q29sb3LXAKoADgCrANMArADU +AK8EKAThBlgGWQDZANoEKIDRgQEUgQFTgQFUCYDRXxAXe3szNjQsIDI4MX0sIHsxMjksIDI2fX3fEBIA +5wToBOkA6ADpAA4A6gDrAO0E6gDuBOsE7ATtAO8A8ATuAPEE7wDaAPYA9ALLBPIA9gZhAPkGYwRCAfwA +2gDaAtEE+QZnBPsJgByBAROBAVWAGYEBVoEBUgkJgQFX0gAOAQ0BDgELgB7cA9UADgT/BQAFAQUCBQMF +BATuA9YCvwUFBlkFBwUIBQkA9AULBQwFDQZnBnIFEAD2gQFUgQEDgP2AHID+gQEAgQFXgQFY0wAOBP8F +EgUTBnUGdoEBEoEBWYEBWtIADgENAQ4FJoAe0gAOAD4ARgZ7gFelBmMGfQZ+Bn8GgIEBVoEBW4EBXYEB +X4EBYdsD1QAOBP8FAAUBBQIFAwUEBO4D1gK/BlkFBwUyBQkA9AULBQwFDQZnBokFOIEBVIEBA4EBB4Ac +gP6BAQCBAVeBAVzbA9UADgT/BQAFAQUCBQMFBATuA9YCvwZZBQcFPQUJAPQFCwUMBQ0GZwaSBUOBAVSB +AQOBAQqAHID+gQEAgQFXgQFe2wPVAA4E/wUABQEFAgUDBQQE7gPWAr8GWQUHBUgFCQD0BQsFDAUNBmcG +mwVOgQFUgQEDgQENgByA/oEBAIEBV4EBYNsD1QAOBP8FAAUBBQIFAwUEBO4D1gK/BlkFBwVTBQkA9AUL +BQwFDQZnBqQFWYEBVIEBA4EBEIAcgP6BAQCBAVeBAWLXAKoADgCrANMArADUAK8EKADWBqgGqQDZANoE +KIDRgCCBAWSBAWUJgNFfEBZ7ezE4MywgNTB9LCB7Mjk3LCAxOH193QDnAA4A6ADpAOoBcQDrAOwA7QDu +AO8A8ADxAPIA8wD0APUBRAF1APQGsgD5BEMA+wF6AXuAH4AcgDWAHIEBZoAZgQFjXxArTW9zdHJhciBi +aXNlbCBtaWVudHJhcyBzZSBjb250cm9sYSBvdHJvIE1hY9cAqgAOAKsA0wCsANQArwQoANYGuQa6ANkA +2gQogNGAIIEBaIEBaQmA0V8QFXt7Mzc1LCA3fSwgezE1MywgMzJ9fdwA5wAOAOgA6QDqAOsA7ADtAO4A +7wDwAPEA8gDzAPQA9QD2BsEGwgD5BEQA+wD8AP2AH4AcgQFrgQFqgBmBAWdfEA9Db21wcm9iYXIgQWhv +cmHSAA4BDQEOAQuAHtcAqgAOAKsA0wCsANQArwQoAdUGywbMANkA2gQogNGAVoEBbYEBbgmA0V8QFXt7 +MTAyLCA3NX0sIHs2OSwgMTd9fdgA5wAOAd4A7ADtAO4A8AHfAPIB4AHhBtMA+QRFAooB5oBVgE2BAW+A +GYEBbIBSV0VzdGFkbzrXAKoADgCrANMArADUAK8EKAbaBtsG3ADZANoEKIDRgQF3gQFxgQFyCYDRXxAW +e3s0MDgsIDI2MX0sIHs4MywgMTV9fd4G4QDnAA4G4gbjAOwA7QDuBuQG5QDwBuYG5wboBukA8gbqAXoA +9gbrBuwERgbuBu8G8AXdBvIF3VdOU1ZhbHVlXxATTlNOdW1iZXJPZlRpY2tNYXJrc18QEk5TVGlja01h +cmtQb3NpdGlvblpOU01heFZhbHVlWk5TTWluVmFsdWVaTlNWZXJ0aWNhbF1OU0FsdEluY1ZhbHVlXxAa +TlNBbGxvd3NUaWNrTWFya1ZhbHVlc09ubHkjP+AAAAAAAACBAXaBAXOBAXSBAXAjP/AAAAAAAAAjP7mZ +mZmZmZoSAAIAAAgjAAAAAAAAAAAI0gAOAQ0BDgELgB7UAA4BAAEBAQIBAwb4BvkFWYAbI0AoAAAAAAAA +gQF1WUhlbHZldGljYdIANwA4BvwG/aQG/QEWANMAO1xOU1NsaWRlckNlbGzSADcAOAb/BwClBwABGgDD +AMcAO1hOU1NsaWRlctcAqgAOAKsA0wCsANQArwQoAdUHBAcFANkA2gQogNGAVoEBeYEBegmA0V8QFnt7 +NDk2LCAyNjB9LCB7MzcsIDE3fX3YAOcADgHeAOwA7QDuAPAB3wDyAeAB4QcMAeMERwVyAeaAVYBNgQF7 +gEyBAXiAUlVMYXJnb9cAqgAOAKsA0wCsANQArwQoANYHFAcVANkA2gQogNGAIIEBfYEBfgmA0V8QFnt7 +MTgzLCAxNn0sIHsxOTgsIDE4fX3dAOcADgDoAOkA6gFxAOsA7ADtAO4A7wDwAPEA8gDzAPQA9QFEAXUA +9AceAPkESAD7AXoBe4AfgByANYAcgQF/gBmBAXxvEBoAQwBvAG0AcAByAG8AYgBhAHIAIAB2AGUAcgBz +AGkA8wBuACAAYQBsACAAYQBiAHIAaQByWE9wY2lvbmVz0gA3ADgHJAIfpAIfAMMAxwA7Wns1OTUsIDQz +OH1aezU5NSwgNDI5fV8QFnt7MCwgMH0sIHsxNjgwLCAxMDI4fX1dezIyNC42NjQsIDMyfV8QGnszLjQw +MjgyZSszOCwgMy40MDI4MmUrMzh90gA3ADgHKwcsogcsADtfEBBOU1dpbmRvd1RlbXBsYXRlV193aW5k +b3fUAA4AogCjAKQApQFlAB8HMoASgEGAAoEBjV8QEmFjdGl2YXRpb25DaGVja2JveNQADgCiAKMApADO +AB8ERAc4gCKAAoEBZ4EBj11jaGVja1ZlcnNpb2461AAOAKIAowCkAKUAHwc9Bz6AEoACgQGRgQHO3AEx +AA4BMgEzATQBNQE2ATcBOAE5AToBOwdAAT0HQgD0B0QHRQdGB0cBRAD2B0gHSYEBlYEBioEBy4AcEnB4 +AACBAZOBAZKBAc2BAcyBAZRfEBh7ezE5NCwgMzc1fSwgezI5NSwgMzA3fX1fEA9UUENsb3NpbmdXaW5k +b3fSAA4BDQEOAUyAHtcAqgAOALkArADdAK0ArwdPALsHUQC+B1IHTwdUgQGWgFmBAZeBAcmBAZaBAcrS +AA4APgBGB1eAV6cHWAdZB1oHWwdcB10HXoEBmIEBp4EBrYEBs4EBuIEBv4EBxNoAqgAOAKsHYADTB2EA +rADUAK0ArwdAB2MHZADaB2YHZwC+ANoHTwdAWk5TRWRpdGFibGVbTlNEcmFnVHlwZXOBAZWBAaaBAaEJ +gQGigQGZCYEBloEBldIADgA+AD8HbYAHpwduB28HcAdxB3IHcwd0gQGagQGbgQGcgQGdgQGegQGfgQGg +XxAZQXBwbGUgUERGIHBhc3RlYm9hcmQgdHlwZV8QGUFwcGxlIFBORyBwYXN0ZWJvYXJkIHR5cGVfECND +b3JlUGFzdGVib2FyZEZsYXZvclR5cGUgMHg1MDRFNDc2Nl8QFU5TRmlsZW5hbWVzUGJvYXJkVHlwZV8Q +MU5lWFQgRW5jYXBzdWxhdGVkIFBvc3RTY3JpcHQgdjEuMiBwYXN0ZWJvYXJkIHR5cGVfEB5OZVhUIFRJ +RkYgdjQuMCBwYXN0ZWJvYXJkIHR5cGVfEBpBcHBsZSBQSUNUIHBhc3RlYm9hcmQgdHlwZV8QF3t7MjAs +IDE1OX0sIHsyNTUsIDEyOH192ADnAA4HfgDsB38HgADwB4EHggeDAXoHhAF6AXoHhQXdV05TU3R5bGVX +TlNBbGlnbldOU1NjYWxlWk5TQW5pbWF0ZXMSIAH+AIEBpYEBoxICAAAACNMADgAyAZwBnQGeB4qAP4A9 +gQGkWHRlbGVwb3J00gA3ADgHjQeOoweOANMAO1tOU0ltYWdlQ2VsbNIANwA4B5AHkaUHkQEaAMMAxwA7 +W05TSW1hZ2VWaWV32ACqAA4AqwDTAKwA1ACtAK8HQAHVB5UHlgC+ANoHTwdAgQGVgFaBAaiBAakJgQGW +gQGVXxAWe3sxNywgMTI5fSwgezI2MSwgMjJ9fdgA5wAOAd4A7ADtAO4A8AHfAPIB4AHhB54HnwdZB6EB +5oBVgE2BAaqBAauBAacSCEAAAIBS1AAOAQABAQECAQMHpQemBVmAGyNAMgAAAAAAAIEBrF8QEUx1Y2lk +YUdyYW5kZS1Cb2xk2gCqAA4AqwMSANMArADUAK0AMgCvB0ADHQerB6wHrQC+ANoHTwewB0CBAZWAtYEB +sIEBr4EBsQmBAZaBAa6BAZVfEBJUUFZlcnNpb25UZXh0RmllbGRfEBZ7ezE3LCAxMTN9LCB7MjYxLCAx +NH192ADnAA4B3gDsAO0A7gDwAd8A8gHgAeEHtwHjB1oHoQHmgFWATYEBsoBMgQGtgFJZPHZlcnNpb24+ +2ACqAA4AqwDTAKwA1ACtAK8HQAHVB78HwAC+ANoHTwdAgQGVgFaBAbSBAbUJgQGWgQGVXxAVe3sxMiwg +NDN9LCB7Mjc0LCAzOX192ADnAA4B3gDsAO0A7gDwAd8A8gHgAeEHyAfJB1sHywHmgFWATYEBtoEBt4EB +sxIAQAAAgFJvEGQAqQAgADIAMAAwADMALQAyADAAMAA4ACAAYQBiAHkAcwBzAG8AZgB0AAoAQgBhAHMA +YQBkAG8AIABlAG4AIAB1AG4AYQAgAGkAZABlAGEAIABkAGUAIABEAGEAdgBlACAAUwBhAGcAIAB5ACAA +SgBlAHIAZQBtAHkAIABRAHUAaQBuAG4ALgAKAEcAcgBhAGMAaQBhAHMAIABhACAATQBhAG4AdQAwADIA +LAAgAEoAdQBsAGkAZQBuACAAeQAgAFAAYQB1AGwALtQADgEAAQEBAgEDB9ABBQfSgBsjQCQAAAAAAACA +GhELG9oAqgAOAKsDEgDTAKwA1ACtADIArwdAAx0H1gesB9gH2QDaB08H3AdAgQGVgLWBAbqBAa+BAbsR +AQIJgQGWgQG5gQGVXxAPVFBMaW5rVGV4dEZpZWxkXxATe3sxMiwgOH0sIHs3MywgMTd9fdoA5wAOAd4A +7ADtAO4A8AG8BbIB3wfhAeAB4QfkB+UHXAVyANoH6AHmEgQh/gGAVYBNgQG8gQG9gQG4CYEBvoBSayek +ACAAcwBpAHQAaQBvACAAdwBlAGLUAA4BAAEBAQIBAwHqAQUFWYAbgBpfEBxodHRwOi8vdGVsZXBvcnQu +YWJ5c3NvZnQuY29t2gCqAA4AqwMSANMArADUAK0AMgCvB0ADHQfyB6wH9AfZANoHTwfcB0CBAZWAtYEB +wIEBr4EBwQmBAZaBAbmBAZVfEBR7ezExOSwgOH0sIHs1NiwgMTd9fdkA5wAOAd4A7ADtAO4A8AWyAd8A +8gHgAeEH/QflB10FcggAAeaAVYBNgQHCgQG9gQG/gQHDgFJnJ6QAIABmAG8AcgBvAHNfEDFodHRwOi8v +d3d3LmFieXNzb2Z0LmNvbS9zb2Z0d2FyZS90ZWxlcG9ydC9mb3J1bXMv2gCqAA4AqwMSANMArADUAK0A +MgCvB0ADHQgHB6wICQfZANoHTwfcB0CBAZWAtYEBxYEBr4EBxgmBAZaBAbmBAZVfEBR7ezIyOCwgOH0s +IHs1NSwgMTd9fdkA5wAOAd4A7ADtAO4A8AWyAd8A8gHgAeEIEgflB14FcggVAeaAVYBNgQHHgQG9gQHE +gQHIgFJnJ6QAIABkAG8AbgBhAHJfEK1odHRwczovL3d3dy5wYXlwYWwuY29tL2NnaS1iaW4vd2Vic2Ny +P2NtZD1feGNsaWNrJmJ1c2luZXNzPWp1bCU0MG1hYyUyZWNvbSZpdGVtX25hbWU9dGVsZXBvcnQmbm9f +c2hpcHBpbmc9MCZub19ub3RlPTAmdGF4PTAmY3VycmVuY3lfY29kZT1VU0QmY2hhcnNldD1VVEYlMmQ4 +JmNoYXJzZXQ9VVRGJTJkOFp7Mjk1LCAzMDd9WnsyMTMsIDEyOX1YZGVsZWdhdGXUAA4AogCjAKQApQAf +AwgIIIASgAKAjIEB0FpkYXRhU291cmNl1AAOAKIAowCkAKUBYwAfCCaAEoAxgAKBAdJfEBRhbGxvd0Nv +bnRyb2xDaGVja2JveNQADgCiAKMApAClAB8IKwgsgBKAAoEB1IEB79QADgguB2AILwgwCDEA2ggzXxAP +X05TTWFuYWdlZFByb3h5Xk5TRGVjbGFyZWRLZXlzgQHugQHsCYEB1dIADgA+AEYINoBXrxAWCDcIOAg5 +CDoIOwg8CD0IPgg/CEAIQQhCCEMIRAhFCEYIRwhICEkISghLCEyBAdaBAdeBAdiBAdmBAdqBAduBAdyB +Ad2BAd6BAd+BAeCBAeGBAeKBAeOBAeSBAeWBAeaBAeeBAeiBAemBAeqBAetfEBJwcmVmcy5hbGxvd0Nv +bnRyb2xfEBVwcmVmcy5zaGFyZVBhc3RlYm9hcmRfEBBwcmVmcy5yZXF1aXJlS2V5XxATcHJlZnMuZGVs +YXllZFN3aXRjaF8QFHByZWZzLnNob3dTdGF0dXNJdGVtXxAZcHJlZnMubGltaXRQYXN0ZWJvYXJkU2l6 +ZV8QGHByZWZzLm1heFBhc3RlcmJvYXJkU2l6ZVZhY3RpdmVfEBZwcmVmcy5hdXRvY2hlY2tWZXJzaW9u +XxAicHJlZnMucmVqZWN0QXV0aGVudGljYXRpb25SZXF1ZXN0c18QFnByZWZzLmVuYWJsZUVuY3J5cHRp +b25fEBJwcmVmcy5zd2l0Y2hLZXlUYWdfEA9wcmVmcy5jb3B5RmlsZXNfEBpwcmVmcy5yZXF1aXJlUGFz +dGVib2FyZEtleV8QFnByZWZzLnBhc3RlYm9hcmRLZXlUYWdfEBpwcmVmcy50cnVzdFJlcXVlc3RCZWhh +dmlvcl8QD3ByZWZzLndha2VPbkxBTl8QEXByZWZzLnN3aXRjaERlbGF5XxAabG9jYWxIb3N0LnN1cHBv +cnREcmFnTkRyb3BfEBdwcmVmcy5tYXhQYXN0ZWJvYXJkU2l6ZV8QFnByZWZzLmhpZGVDb250cm9sQmV6 +ZWxfEBlwcmVmcy5zd2l0Y2hXaXRoRG91YmxlVGFw0QAOCGSBAe3SADcAOAhmCC6iCC4AO9IANwA4CGgI +aaMIaQhqADtfEBJOU09iamVjdENvbnRyb2xsZXJcTlNDb250cm9sbGVyV2NvbnRlbnTUAA4AogCjAKQA +pQQ6AB8IcIASgO+AAoEB8V8QF3NoYXJlUGFzdGVib2FyZENoZWNrYm941AAOAKIAowCkAKUENwAfCHaA +EoDjgAKBAfNec3RhdHVzQ2hlY2tib3jUAA4AogCjAKQApQQ/AB8IfIASgQEdgAKBAfVfEBVkZWxheWVk +U3dpdGNoQ2hlY2tib3jUAA4AogCjAKQApQMICIEILIASgIyBAfeBAe/UAA4ILgiECC8IMAiGCIcIiF8Q +EU5TT2JqZWN0Q2xhc3NOYW1lgQHugQH7gQH6gQH40gAOAD4ARgiLgFehCIyBAflfEBRudW1iZXJPZlNl +bGVjdGVkUm93c9EADghkgQHt1AAOAKIAowCkAKUEOwAfCJSAEoDzgAKBAf1fEBJyZXF1aXJlS2V5Q2hl +Y2tib3jUAA4AogCjAKQApQFXAB8ImoASgC+AAoEB/1R2aWV31AAOAKIAowCkAKUCPgAfCKCAEoBogAKB +AgFfEBdjaG9vc2VDZXJ0aWZpY2F0ZUJ1dHRvbtQADgCiAKMApAClAwgAHwimgBKAjIACgQIDXxAVdHJ1 +c3RlZEhvc3RzVGFibGVWaWV31AAOAKIAowCkAM4AHwI+CKyAIoACgGiBAgVfEBdzaG93Q2VydGlmaWNh +dGVDaG9vc2VyOtQADgCiAKMApAClAB8Augc+gBKAAoBFgQHO1AAOAKIAowCkAKUHWgAfCLeAEoEBrYAC +gQIIXxAQdmVyc2lvblRleHRGaWVsZNQADgCiAKMApAClANAAHwi9gBKAFIACgQIKXxAVc2hvd0NlcnRp +ZmljYXRlQnV0dG9u1AAOAKIAowCkAKUAHwCmBz6AEoACgAqBAc7UAA4AogCjAKQApQc9AB8IyIASgQGR +gAKBAg1bYWJvdXRXaW5kb3fUAA4AogCjAKQAzgAfAWQIzoAigAKAOIECD18QD3Nob3dBYm91dFNoZWV0 +OtQADgCiAKMApAClAKYAHwjUgBKACoACgQIRXxAPX2luaXRpYWxLZXlWaWV31AAOAKIAowCkAKUAHwMI +Bz6AEoACgIyBAc7XAA4AogjcCN0AowCkCN4I3wgrCOEI4gFjCOQBRFlOU0tleVBhdGhZTlNCaW5kaW5n +XxAcTlNOaWJCaW5kaW5nQ29ubmVjdG9yVmVyc2lvboECF4EB1IECFoECFYAxgQIUXxAZZW5hYmxlZDog +c2VsZWN0aW9uLmFjdGl2ZVdlbmFibGVkXxAQc2VsZWN0aW9uLmFjdGl2ZdIANwA4COkI6qMI6gDMADtf +EBVOU05pYkJpbmRpbmdDb25uZWN0b3LYAA4AogjcCN0AowCkCOwI3gjfCCsI7wjwBEMI8gjzAURZTlNP +cHRpb25zgQIXgQHUgQIbgQIagQFjgQIZgQIcXxAndmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5oaWRlQ29u +dHJvbEJlemVsVXZhbHVlXxAgc2VsZWN0aW9uLnByZWZzLmhpZGVDb250cm9sQmV6ZWzTAA4F4AA+Bh4I ++Qj7gQE8oQj6gQIdoQj8gQIeXxAWTlNWYWx1ZVRyYW5zZm9ybWVyTmFtZV8QD05TTmVnYXRlQm9vbGVh +btcADgCiCNwI3QCjAKQI3gjfCCsI4QjiBEQI5AFEgQIXgQHUgQIWgQIVgQFngQIU1wAOAKII3AjdAKMA +pAjeCN8IKwkJCPAEOQkMAUSBAheBAdSBAiKBAhqA64ECIV8QK3ZhbHVlOiBzZWxlY3Rpb24ucHJlZnMu +cmVxdWlyZVBhc3RlYm9hcmRLZXlfECRzZWxlY3Rpb24ucHJlZnMucmVxdWlyZVBhc3RlYm9hcmRLZXnX +AA4AogjcCN0AowCkCN4I3wgrCRII8ARICRUBRIECF4EB1IECJYECGoEBfIECJF8QJ3ZhbHVlOiBzZWxl +Y3Rpb24ucHJlZnMuYXV0b2NoZWNrVmVyc2lvbl8QIHNlbGVjdGlvbi5wcmVmcy5hdXRvY2hlY2tWZXJz +aW9u1wAOAKII3AjdAKMApAjeCN8IKwkbCOIEMwkeAUSBAheBAdSBAiiBAhWA04ECJ18QKGVuYWJsZWQ6 +IHNlbGVjdGlvbi5wcmVmcy5zaGFyZVBhc3RlYm9hcmRfEB9zZWxlY3Rpb24ucHJlZnMuc2hhcmVQYXN0 +ZWJvYXJk2QAOAKII3AjdCSIAowCkCOwI3gjfCCsI4QkmAGkEMwkpCSoBRF8QE05TUHJldmlvdXNDb25u +ZWN0b3KBAheBAdSBAhaBAiuBAiaA04ECKoECLF8QGmVuYWJsZWQyOiBzZWxlY3Rpb24uYWN0aXZlWGVu +YWJsZWQy0wAOBeAAPgYeCS8JNIEBPKQJMAkxCTIJM4ECLYECLoECL4ECMKQJNQk1CTUJNYECMYECMYEC +MYECMV8QEU5TTnVsbFBsYWNlaG9sZGVyXxAaTlNOb3RBcHBsaWNhYmxlUGxhY2Vob2xkZXJfEBhOU05v +U2VsZWN0aW9uUGxhY2Vob2xkZXJfEBtOU011bHRpcGxlVmFsdWVzUGxhY2Vob2xkZXIT///////////X +AA4AogjcCN0AowCkCN4I3wgrCUEJQgQ8CUQBRIECF4EB1IECNYECNID3gQIzXxAtc2VsZWN0ZWRUYWc6 +IHNlbGVjdGlvbi5wcmVmcy5wYXN0ZWJvYXJkS2V5VGFnW3NlbGVjdGVkVGFnXxAgc2VsZWN0aW9uLnBy +ZWZzLnBhc3RlYm9hcmRLZXlUYWfXAA4AogjcCN0AowCkCN4I3wgrCUsI8AQ4CU4BRIECF4EB1IECOIEC +GoDngQI3XxAgdmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5jb3B5RmlsZXNfEBlzZWxlY3Rpb24ucHJlZnMu +Y29weUZpbGVz1wAOAKII3AjdAKMApAjeCN8IKwlUCPAEOwlXAUSBAheBAdSBAjuBAhqA84ECOl8QIXZh +bHVlOiBzZWxlY3Rpb24ucHJlZnMucmVxdWlyZUtleV8QGnNlbGVjdGlvbi5wcmVmcy5yZXF1aXJlS2V5 +1wAOAKII3AjdAKMApAjeCN8IKwldCPAERglgAUSBAheBAdSBAj6BAhqBAXCBAj1fECJ2YWx1ZTogc2Vs +ZWN0aW9uLnByZWZzLnN3aXRjaERlbGF5XxAbc2VsZWN0aW9uLnByZWZzLnN3aXRjaERlbGF51wAOAKII +3AjdAKMApAjeCN8IKwlmCUICQglpAUSBAheBAdSBAkGBAjSAdYECQF8QMXNlbGVjdGVkVGFnOiBzZWxl +Y3Rpb24ucHJlZnMudHJ1c3RSZXF1ZXN0QmVoYXZpb3JfECRzZWxlY3Rpb24ucHJlZnMudHJ1c3RSZXF1 +ZXN0QmVoYXZpb3LXAA4AogjcCN0AowCkCN4I3wgrCW8I8AQ+CXIBRIECF4EB1IECRIECGoEBGYECQ18Q +IHZhbHVlOiBzZWxlY3Rpb24ucHJlZnMud2FrZU9uTEFOXxAZc2VsZWN0aW9uLnByZWZzLndha2VPbkxB +TtcADgCiCNwI3QCjAKQI3gjfCCsJeAjwBD8JewFEgQIXgQHUgQJHgQIagQEdgQJGXxAkdmFsdWU6IHNl +bGVjdGlvbi5wcmVmcy5kZWxheWVkU3dpdGNoXxAdc2VsZWN0aW9uLnByZWZzLmRlbGF5ZWRTd2l0Y2jX +AA4AogjcCN0AowCkCN4I3wgrCYEI4gI+CYQBRIECF4EB1IECSoECFYBogQJJXxApZW5hYmxlZDogc2Vs +ZWN0aW9uLnByZWZzLmVuYWJsZUVuY3J5cHRpb25fECBzZWxlY3Rpb24ucHJlZnMuZW5hYmxlRW5jcnlw +dGlvbtkADgCiCNwI3QkiAKMApAjsCN4I3wgrCOEJJgByAj4JKQmPAUSBAheBAdSBAhaBAiuBAkiAaIEC +KoECTNMADgXgAD4GHgmSCZeBATykCTAJMQkyCTOBAi2BAi6BAi+BAjCkCTUJNQk1CTWBAjGBAjGBAjGB +AjHXAA4AogjcCN0AowCkCN4I3wgrCRsI8AQ6CaIBRIECF4EB1IECKIECGoDvgQJOXxAmdmFsdWU6IHNl +bGVjdGlvbi5wcmVmcy5zaGFyZVBhc3RlYm9hcmTXAA4AogjcCN0AowCkCN4I3wgrCRsI4gRBCR4BRIEC +F4EB1IECKIECFYEBJYECJ9kADgCiCNwI3QkiAKMApAjsCN4I3wgrCOEJJgB1BEEJKQmzAUSBAheBAdSB +AhaBAiuBAk+BASWBAiqBAlHTAA4F4AA+Bh4Jtgm7gQE8pAkwCTEJMgkzgQItgQIugQIvgQIwpAk1CTUJ +NQk1gQIxgQIxgQIxgQIx2QAOAKII3AjdCSIAowCkCOwI3gjfCCsJwwnEAHYEQQnHCcgBRIECF4EB1IEC +VYECVIECUIEBJYECU4ECVl8QLWVuYWJsZWQzOiBzZWxlY3Rpb24ucHJlZnMubGltaXRQYXN0ZWJvYXJk +U2l6ZVhlbmFibGVkM18QI3NlbGVjdGlvbi5wcmVmcy5saW1pdFBhc3RlYm9hcmRTaXpl0wAOBeAAPgYe +Cc4J04EBPKQJMAkxCTIJM4ECLYECLoECL4ECMKQJNQk1CTUJNYECMYECMYECMYECMdcADgCiCNwI3QCj +AKQI3gjfCCsJVAjiBEIJ3gFEgQIXgQHUgQI7gQIVgQFSgQJYXxAjZW5hYmxlZDogc2VsZWN0aW9uLnBy +ZWZzLnJlcXVpcmVLZXnZAA4AogjcCN0JIgCjAKQI7AjeCN8IKwjhCSYAeARCCSkJ6AFEgQIXgQHUgQIW +gQIrgQJXgQFSgQIqgQJa0wAOBeAAPgYeCesJ8IEBPKQJMAkxCTIJM4ECLYECLoECL4ECMKQJNQk1CTUJ +NYECMYECMYECMYECMdcADgCiCNwI3QCjAKQI3gjfCCsJgQjwAkAJ+wFEgQIXgQHUgQJKgQIagG2BAlxf +ECd2YWx1ZTogc2VsZWN0aW9uLnByZWZzLmVuYWJsZUVuY3J5cHRpb27XAA4AogjcCN0AowCkCN4I3wgr +CYEI4gDQCYQBRIECF4EB1IECSoECFYAUgQJJ2QAOAKII3AjdCSIAowCkCOwI3gjfCCsI4QkmAHsA0Akp +CgwBRIECF4EB1IECFoECK4ECXYAUgQIqgQJf0wAOBeAAPgYeCg8KFIEBPKQJMAkxCTIJM4ECLYECLoEC +L4ECMKQJNQk1CTUJNYECMYECMYECMYECMdcADgCiCNwI3QCjAKQI3gjfCCsI4QjiAkII5AFEgQIXgQHU +gQIWgQIVgHWBAhTXAA4AogjcCN0AowCkCN4I3wgrCOEI4gQ7COQBRIECF4EB1IECFoECFYDzgQIU1wAO +AKII3AjdAKMApAjeCN8IKwjhCOIENwjkAUSBAheBAdSBAhaBAhWA44ECFNcADgCiCNwI3QCjAKQI3gjf +CCsI4QjiBEMI5AFEgQIXgQHUgQIWgQIVgQFjgQIU1wAOAKII3AjdAKMApAjeCN8IKwl4COIERgo7AUSB +AheBAdSBAkeBAhWBAXCBAmVfECZlbmFibGVkOiBzZWxlY3Rpb24ucHJlZnMuZGVsYXllZFN3aXRjaNkA +DgCiCNwI3QkiAKMApAjsCN4I3wgrCOEJJgCBBEYJKQpFAUSBAheBAdSBAhaBAiuBAmSBAXCBAiqBAmfT +AA4F4AA+Bh4KSApNgQE8pAkwCTEJMgkzgQItgQIugQIvgQIwpAk1CTUJNQk1gQIxgQIxgQIxgQIx1wAO +AKII3AjdAKMApAjeCN8IKwkbCOIEOQkeAUSBAheBAdSBAiiBAhWA64ECJ9kADgCiCNwI3QkiAKMApAjs +CN4I3wgrCOEJJgCDBDkJKQphAUSBAheBAdSBAhaBAiuBAmiA64ECKoECatMADgXgAD4GHgpkCmmBATyk +CTAJMQkyCTOBAi2BAi6BAi+BAjCkCTUJNQk1CTWBAjGBAjGBAjGBAjHXAA4AogjcCN0AowCkCN4I3wgr +CnEI8AQ3CnQBRIECF4EB1IECbYECGoDjgQJsXxAldmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5zaG93U3Rh +dHVzSXRlbV8QHnNlbGVjdGlvbi5wcmVmcy5zaG93U3RhdHVzSXRlbdcADgCiCNwI3QCjAKQI3gjfCCsK +egjwBEAKfQFEgQIXgQHUgQJwgQIagQEhgQJvXxAqdmFsdWU6IHNlbGVjdGlvbi5wcmVmcy5zd2l0Y2hX +aXRoRG91YmxlVGFwXxAjc2VsZWN0aW9uLnByZWZzLnN3aXRjaFdpdGhEb3VibGVUYXDXAA4AogjcCN0A +owCkCN4I3wgrCOEI4gHSCOQBRIECF4EB1IECFoECFYBIgQIU1wAOAKII3AjdAKMApAjeCN8IKwjhCOIE +OgjkAUSBAheBAdSBAhaBAhWA74ECFNcADgCiCNwI3QCjAKQI3gjfCCsKkQlCBEIKlAFEgQIXgQHUgQJ1 +gQI0gQFSgQJ0XxApc2VsZWN0ZWRUYWc6IHNlbGVjdGlvbi5wcmVmcy5zd2l0Y2hLZXlUYWdfEBxzZWxl +Y3Rpb24ucHJlZnMuc3dpdGNoS2V5VGFn1wAOAKII3AjdAKMApAjeCN8IKwkJCOIEPAqdAUSBAheBAdSB +AiKBAhWA94ECd18QLWVuYWJsZWQ6IHNlbGVjdGlvbi5wcmVmcy5yZXF1aXJlUGFzdGVib2FyZEtledkA +DgCiCNwI3QkiAKMApAjsCN4I3wgrCRsJJgCKBDwKpgqnAUSBAheBAdSBAiiBAiuBAnaA94ECeYECel8Q +KWVuYWJsZWQyOiBzZWxlY3Rpb24ucHJlZnMuc2hhcmVQYXN0ZWJvYXJk0wAOBeAAPgYeCqsKsIEBPKQJ +MAkxCTIJM4ECLYECLoECL4ECMKQJNQk1CTUJNYECMYECMYECMYECMdkADgCiCNwI3QkiAKMApAjsCN4I +3wgrCOEJxACLBDwKvAq9AUSBAheBAdSBAhaBAlSBAniA94ECfIECfV8QGmVuYWJsZWQzOiBzZWxlY3Rp +b24uYWN0aXZl0wAOBeAAPgYeCsEKxoEBPKQJMAkxCTIJM4ECLYECLoECL4ECMKQJNQk1CTUJNYECMYEC +MYECMYECMdcADgCiCNwI3QCjAKQI3gjfCCsI4QjiBEAI5AFEgQIXgQHUgQIWgQIVgQEhgQIU1wAOAKII +3AjdAKMApAjeCN8IKwjhCOIESAjkAUSBAheBAdSBAhaBAhWBAXyBAhTXAA4AogjcCN0AowCkCN4I3wgr +COEI8AFlCt8BRIECF4EB1IECFoECGoBBgQKBXxAXdmFsdWU6IHNlbGVjdGlvbi5hY3RpdmXXAA4Aogjc +CN0AowCkCN4I3wgrCOEI4gQ4COQBRIECF4EB1IECFoECFYDngQIU2QAOAKII3AjdCSIAowCkCOwI3gjf +CCsK6wkmAJAEOArvCvABRIECF4EB1IEChYECK4ECgoDngQKEgQKGXxAuZW5hYmxlZDI6IHNlbGVjdGlv +bi5sb2NhbEhvc3Quc3VwcG9ydERyYWdORHJvcF8QJHNlbGVjdGlvbi5sb2NhbEhvc3Quc3VwcG9ydERy +YWdORHJvcNMADgXgAD4GHgr1CvqBATykCTAJMQkyCTOBAi2BAi6BAi+BAjCkCTUJNQk1CTWBAjGBAjGB +AjGBAjHXAA4AogjcCN0AowCkCN4I3wgrCwII8ARBCwUBRIECF4EB1IECiYECGoEBJYECiF8QKHZhbHVl +OiBzZWxlY3Rpb24ucHJlZnMubWF4UGFzdGVib2FyZFNpemVfECFzZWxlY3Rpb24ucHJlZnMubWF4UGFz +dGVib2FyZFNpemXXAA4AogjcCN0AowCkCN4I3wgrCOEI4gQ/COQBRIECF4EB1IECFoECFYEBHYECFNcA +DgCiCNwI3QCjAKQI3gjfCCsI4QjiBD4I5AFEgQIXgQHUgQIWgQIVgQEZgQIU1wAOAKII3AjdAKMApAje +CN8IKwnDCPAEMwscAUSBAheBAdSBAlWBAhqA04ECjV8QKnZhbHVlOiBzZWxlY3Rpb24ucHJlZnMubGlt +aXRQYXN0ZWJvYXJkU2l6ZdcADgCiCNwI3QCjAKQI3gjfCCsI4QjiAkAI5AFEgQIXgQHUgQIWgQIVgG2B +AhTXAA4AogjcCN0AowCkCN4I3wgrCOEI4gMICOQBRIECF4EB1IECFoECFYCMgQIU1wAOAKII3AjdAKMA +pAjeCN8IKwsvCPABYwsyAUSBAheBAdSBApKBAhqAMYECkV8QI3ZhbHVlOiBzZWxlY3Rpb24ucHJlZnMu +YWxsb3dDb250cm9sXxAcc2VsZWN0aW9uLnByZWZzLmFsbG93Q29udHJvbNQADgCiCzYLNws4AWMLOgs7 +WE5TTWFya2VyVk5TRmlsZYECloAxgQKVgQKUXxAQTlNUb29sVGlwSGVscEtleV8QRU1hcmNhciBwYXJh +IHBlcm1pdGlyIGEgb3Ryb3MgdXN1YXJpb3MgZGUgdGVsZXBvcnQgY29udHJvbGFyIGVzdGUgTWFjLtIA +NwA4Cz8LQKILQAA7XxARTlNJQkhlbHBDb25uZWN0b3LUAA4Aogs2CzcLOAFkC0QLO4ECloA4gQKYgQKU +bxAPAEEAYgBvAHUAdAAgAHQAZQBsAGUAcABvAHIAdCAm1AAOAKILNgs3CzgBZQtKCzuBApaAQYECmoEC +lF8QHU1hcmNhciBwYXJhIGFjdGl2YXIgdGVsZXBvcnQu1AAOAKILNgs3CzgAsAtQCzuBApaAC4ECnIEC +lG8QcABBAHIAcgBhAHMAdAByAGEAIABsAG8AcwAgAGgAbwBzAHQAcwAgAGMAbwBuAHQAcgBvAGwAYQBk +AG8AcwAgAHkAIABwAG8AbgBsAG8AcwAgAGUAbgAgAGwAYQAgAGQAaQBzAHAAbwBzAGkAYwBpAPMAbgAs +ACAAcgBlAGwAYQB0AGkAdgBhACAAYQAgAGwAYQAgAHAAYQBuAHQAYQBsAGwAYQAgAGQAZQBsACAATQBh +AGMAIABwAHIAaQBuAGMAaQBwAGEAbAAsACAAcQB1AGUAIABkAGUAcwBlAGUAcwAu1AAOAKILNgs3CzgA +pgtQCzuBApaACoECnIEClNQADgCiCzYLNws4BD4LWws7gQKWgQEZgQKfgQKUbxDAAFMA8wBsAG8AIABl +AG4AIABvAHIAZABlAG4AYQBkAG8AcgBlAHMAIABjAG8AbgBlAGMAdABhAGQAbwBzACAAcABvAHIAIABF +AHQAaABlAHIAbgBlAHQAIAB5ACAAYwBvAG4AIABsAGEAIABvAHAAYwBpAPMAbgAgIBwAQQBjAHQAaQB2 +AGEAcgAgAGUAbAAgAG8AcgBkAGUAbgBhAGQAbwByACAAcABhAHIAYQAgAHAAZQByAG0AaQB0AGkAcgAg +AGEAYwBjAGUAZABlAHIAIABhAGwAIABhAGQAbQBpAG4AaQBzAHQAcgBhAGQAbwByACAAZABlACAAcgBl +AGQAZQBzACAAZQB0AGgAZQByAG4AZQB0IB0AIABoAGEAYgBpAGwAaQB0AGEAZABhACAAZQBuACAAZQBs +ACAAcABhAG4AZQBsACAAZABlACAAcAByAGUAZgBlAHIAZQBuAGMAaQBhAHMAIABFAGMAbwBuAG8AbQBp +AHoAYQBkAG8AcgAu1AAOAKILNgs3CzgEPwthCzuBApaBAR2BAqGBApRfEGVMbGV2YXIgZWwgY3Vyc29y +IGhhc3RhIGVsIGJvcmRlIGRlIGxhIHBhbnRhbGxhIHkgbWFudGVuZXJsbyBicmV2ZW1lbnRlIGhhc3Rh +IHF1ZSBzZSBhY3RpdmEgZWwgY2FtYmlvLtQADgCiCzYLNws4BEALZws7gQKWgQEhgQKjgQKUbxCNAEwA +bABlAHYAYQByACAAZQBsACAAYwB1AHIAcwBvAHIAIABhAGwAIABiAG8AcgBkAGUAIABkAGUAIABsAGEA +IABwAGEAbgB0AGEAbABsAGEALAAgAHIAZQB0AGkAcgBhAHIAbABvACAAdQBuACAAcABhAHIAIABkAGUA +IABjAGUAbgB0AO0AbQBlAHQAcgBvAHMAIAB5ACAAdgBvAGwAdgBlAHIAIABhACAAbABsAGUAdgBhAHIA +bABvACAAaABhAHMAdABhACAAZQBsACAAYgBvAHIAZABlACAAbwB0AHIAYQAgAHYAZQB6ACAAcABhAHIA +YQAgAGEAYwB0AGkAdgBhAHIAIABlAGwAIABjAGEAbQBiAGkAbwAu0gAOAD4LawtsgQKnrxB/BDMBZAW4 +AkMApggJBxUEQwdYB60B1wiBAvMA1QJFBawEQAI+AwgCgAJKBF4ERQV5C4UESAdZANgCQQP6AasGZwKt +BOMHwAC6B14BLQVoBmMDIAQ7BEcENgNfBE0FKwFXBswCQgQ3AkQB0gI9Bz0GWQdmBS4AsASfBPoEQQWK +B5YBiQKbBDQEPwE8BD0CHAaAAN4CbwOoCCsEOgNRA5EEGwQ+BMEFmwQoBS0H2AR+BPUGugUsA1AEsARC +AkACGwddAckBawSOBDwGfgZ/Bn0HQAQ1AWMBZQbcB1wEbgQ5BEYCrALrBDgERAKcBAoHBQdaBNIHWwap +B/QDTwJcANCA04A4gQEogIiACoEBxoEBfoEBY4EBmIEBsYBKgQH3gLiAFYDGgQEngQEhgGiAjIBzgGaA +2YEBbIEBG4ECpYEBfIEBp4AXgHGAxIBDgQFXgH+A+YEBtYBFgQHEgCiBAReBAVaAkIDzgQF4gN+AoIDV +gQEGgC+BAW6AdYDjgMKASIBkgQGRgQFUgQGigQEPgAuA6YD8gQElgQEfgQGpgDqAhIDXgQEdgC2BARWA +zYEBYYBhgG+AsIEB1IDvgKqAqYDPgQEZgPGBASOA0YEBDIEBu4DhgPuBAWmBAQmApYDtgQFSgG2AYIEB +v4BcgDOA5YD3gQFdgQFfgQFbgQGVgNuAMYBBgQFygQG4gN2A64EBcIB9gLyA54EBZ4B4gMiBAXqBAa2A +9YEBs4EBZYEBwYCYgGqAFNIADgAyADML7oAEgQKmXU5TQXBwbGljYXRpb27SADcAOAvxAhKiAhIAO9IA +DgA+C2sL9IECp68QfwQoAVcFrADVALAHXgRIBCgHQAdaAdIAHwJDAN4A1QRBBCgA1QJDAkECPQQ0BCgE +PgAfBCgHQADQANUCRAFlBlkCQgQ8B1sBVwdAAB8EPQZnAkMEKAQoBCgDTwQzBPoBPARFANUEKADVALAA +1QAfBEIHWAT6AckEOATjBCgEPwdZAWQCQgQoBCgBLQQoALoGZwIbAkADUQAfBCgDCANQAhwEKAQ6BEAE +GwT6B1wENgT6BEQE+gMIBDkEKADVALoHQAC6AWMENwQoBmcGZwZnBz0EKAFXAVcERgdABDUEKAQoAkIC +QwQoBCgCQgJFBEcHQAQ7B0AEQwddAwgCPgDVgNGAL4EBJ4AVgAuBAcSBAXyA0YEBlYEBrYBIgAKAiIBh +gBWBASWA0YAVgIiAcYBkgNeA0YEBGYACgNGBAZWAFIAVgMKAQYEBVIB1gPeBAbOAL4EBlYACgQEVgQFX +gIiA0YDRgNGAmIDTgPyALYEBbIAVgNGAFYALgBWAAoEBUoEBmID8gFyA54D5gNGBAR2BAaeAOIB1gNGA +0YAogNGARYEBV4BggG2AqoACgNGAjIClgM2A0YDvgQEhgM+A/IEBuIDfgPyBAWeA/ICMgOuA0YAVgEWB +AZWARYAxgOOA0YEBV4EBV4EBV4EBkYDRgC+AL4EBcIEBlYDbgNGA0YB1gIiA0YDRgHWAxoEBeIEBlYDz +gQGVgQFjgQG/gIyAaIAV0gAOAD4Lawx2gQKnrxCABDMBZAJcAkMApgW4BxUEQwdYB60B1wiBAvMA1QJF +BawAHwRAAj4DCAKAAkoERQReB1kLhQRIBXkA2AJBA/oBqwKtBmcHwATjB14AugEtBWgGYwMgBDsERwQ2 +A18ETQUrAVcCQgbMBDcCRAHSBz0CPQdmBlkFLgCwBJ8EQQT6B5YFigGJApsENAQ/ATwEPQIcAN4Cbwgr +BoADqAQ6A1EDkQQbBD4H2AWbBCgEwQUtBH4E9Qa6BSwDUARCBLACQAddAhsByQFrBI4EPAZ+Bn8GfQdA +BDUBYwFlBtwHXARuBDkERgLrAqwEOAREBAoCnAcFB1oHWwTSBqkH9ANPANAICYDTgDiAaoCIgAqBASiB +AX6BAWOBAZiBAbGASoEB94C4gBWAxoEBJ4ACgQEhgGiAjIBzgGaBAWyA2YEBp4ECpYEBfIEBG4AXgHGA +xIBDgH+BAVeBAbWA+YEBxIBFgCiBAReBAVaAkIDzgQF4gN+AoIDVgQEGgC+AdYEBboDjgMKASIEBkYBk +gQGigQFUgQEPgAuA6YEBJYD8gQGpgQEfgDqAhIDXgQEdgC2BARWAzYBhgG+BAdSBAWGAsIDvgKqAqYDP +gQEZgQG7gQEjgNGA8YEBDIDhgPuBAWmBAQmApYEBUoDtgG2BAb+AYIBcgDOA5YD3gQFdgQFfgQFbgQGV +gNuAMYBBgQFygQG4gN2A64EBcIC8gH2A54EBZ4DIgHiBAXqBAa2BAbOA9YEBZYEBwYCYgBSBAcbSAA4A +PgtrDPmBAqevEIAM+gz7DPwM/Qz+DP8NAA0BDQINAw0EDQUNBg0HDQgNCQ0KDQsNDA0NDQ4NDw0QDREN +Eg0TDRQNFQ0WDRcNGA0ZDRoNGw0cDR0NHg0fDSANIQ0iDSMNJA0lDSYNJw0oDSkNKg0rDSwNLQ0uDS8N +MA0xDTINMw00DTUNNg03DTgNOQ06DTsNPA09DT4NPw1ADUENQg1DDUQNRQ1GDUcNSA1JDUoNSw1MDU0N +Tg1PDVANUQ1SDVMNVA1VDVYNVw1YDVkNWg1bDVwNXQ1eDV8NYA1hDWINYw1kDWUNZg1nDWgNaQ1qDWsN +bA1tDW4Nbw1wDXENcg1zDXQNdQ12DXcNeA15gQKrgQKsgQKtgQKugQKvgQKwgQKxgQKygQKzgQK0gQK1 +gQK2gQK3gQK4gQK5gQK6gQK7gQK8gQK9gQK+gQK/gQLAgQLBgQLCgQLDgQLEgQLFgQLGgQLHgQLIgQLJ +gQLKgQLLgQLMgQLNgQLOgQLPgQLQgQLRgQLSgQLTgQLUgQLVgQLWgQLXgQLYgQLZgQLagQLbgQLcgQLd +gQLegQLfgQLggQLhgQLigQLjgQLkgQLlgQLmgQLngQLogQLpgQLqgQLrgQLsgQLtgQLugQLvgQLwgQLx +gQLygQLzgQL0gQL1gQL2gQL3gQL4gQL5gQL6gQL7gQL8gQL9gQL+gQL/gQMAgQMBgQMCgQMDgQMEgQMF +gQMGgQMHgQMIgQMJgQMKgQMLgQMMgQMNgQMOgQMPgQMQgQMRgQMSgQMTgQMUgQMVgQMWgQMXgQMYgQMZ +gQMagQMbgQMcgQMdgQMegQMfgQMggQMhgQMigQMjgQMkgQMlgQMmgQMngQMogQMpgQMqbxAfAEMAaABl +AGMAawAgAEIAbwB4ACAAKABTAPMAbABvACAAcwBpACAAZQBzACAAbQBlAG4AbwByACAAZABlAClvEBUA +QgBlAHYAZQBsACAAQgB1AHQAdABvAG4AIAAoAEEAYgBvAHUAdCAmAClvECIAQgB1AHQAdABvAG4AIABD +AGUAbABsACAAKABFAHMAYwBvAGcAZQByACAAYwBlAHIAdABpAGYAaQBjAGEAZABvICYAKVtTY3JvbGwg +Vmlld1tMYXlvdXQgVmlld18QEE51bWJlciBGb3JtYXR0ZXJvECgAQgB1AHQAdABvAG4AIABDAGUAbABs +ACAAKABDAG8AbQBwAHIAbwBiAGEAcgAgAHYAZQByAHMAaQDzAG4AIABhAGwAIABhAGIAcgBpAHIAKV8Q +N0NoZWNrIEJveCAoTW9zdHJhciBiaXNlbCBtaWVudHJhcyBzZSBjb250cm9sYSBvdHJvIE1hYylfEBVJ +bWFnZSBWaWV3ICh0ZWxlcG9ydClfEBtUZXh0IEZpZWxkIENlbGwgKDx2ZXJzaW9uPilfEGVUZXh0IEZp +ZWxkIENlbGwgKE9yZ2FuaXphIGxvcyBNYWNzIGNvbXBhcnRpZG9zIGRlIGFycmliYSBhbHJlZGVkb3Ig +ZGUgdHUgcGFudGFsbGEgcGFyYSBjb250cm9sYXJsb3MuKV8QEVRydXN0ZWRIb3N0c1RhYmxlXxARVmVy +dGljYWwgU2Nyb2xsZXJdQ3VzdG9tIFZpZXctMl8QIVN0YXRpYyBUZXh0IChIb3N0cyBkZSBjb25maWFu +emE6KV8QD1RleHQgRmllbGQgQ2VsbFxGaWxlJ3MgT3duZXJfEC9DaGVjayBCb3ggKENhbWJpYXIgYWwg +dG9jYXIgZWwgYm9yZGUgZG9zIHZlY2VzKW8QIgBQAHUAcwBoACAAQgB1AHQAdABvAG4AIAAoAEUAcwBj +AG8AZwBlAHIAIABjAGUAcgB0AGkAZgBpAGMAYQBkAG8gJgApbxArAFQAYQBiAGwAZQAgAFYAaQBlAHcA +IAAoAE4AbwBtAGIAcgBlACwAIABEAGkAcgBlAGMAYwBpAPMAbgAsACAAQwBlAHIAdABpAGYAaQBjAGEA +ZABvAClvEB8AVABlAHgAdAAgAEYAaQBlAGwAZAAgAEMAZQBsAGwAIAAoAEMAbwBkAGkAZgBpAGMAYQBj +AGkA8wBuADoAKV8QX1RleHQgRmllbGQgQ2VsbCAoUGFyYSBlbGltaW5hciB1biBob3N0IGRlIGNvbmZp +YW56YSwgc2VsZWNjaW9uYXJsbyB5IHByZXNpb25hciBsYSB0ZWNsYSBib3JyYXIpXxAVU3RhdGljIFRl +eHQgKEVzdGFkbzopXxAhVGV4dCBGaWVsZCBDZWxsIChUcmFuc2ZlcmVuY2lhczopXxAWU3RhdGljIFRl +eHQgKHRlbGVwb3J0KVtBcHBsaWNhdGlvbm8QJgBDAGgAZQBjAGsAIABCAG8AeAAgACgAQwBvAG0AcABy +AG8AYgBhAHIAIAB2AGUAcgBzAGkA8wBuACAAYQBsACAAYQBiAHIAaQByAClfEDNCdXR0b24gQ2VsbCAo +SW50ZW50YXIgZGVzcGVydGFyIGxvcyBNYWNzIGVuIHJlcG9zbylfECFCdXR0b24gQ2VsbCAoTW9zdHJh +ciBjZXJ0aWZpY2FkbylvEBsAUwB0AGEAdABpAGMAIABUAGUAeAB0ACAAKABDAG8AZABpAGYAaQBjAGEA +YwBpAPMAbgA6AClfEChUZXh0IEZpZWxkIENlbGwgKFBldGljaW9uZXMgZGUgY29udHJvbDopXxAeQnV0 +dG9uIENlbGwgKEFjdGl2YXIgdGVsZXBvcnQpbxAlAEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgAQQBj +AGUAcAB0AGEAcgAgAGEAdQB0AG8AbQDhAHQAaQBjAGEAbQBlAG4AdABlAClfEBFNZW51IChPdGhlclZp +ZXdzKW8QdgBUAGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgAqQAgADIAMAAwADMALQAyADAA +MAA4ACAAYQBiAHkAcwBzAG8AZgB0AAoAQgBhAHMAYQBkAG8AIABlAG4AIAB1AG4AYQAgAGkAZABlAGEA +IABkAGUAIABEAGEAdgBlACAAUwBhAGcAIAB5ACAASgBlAHIAZQBtAHkAIABRAHUAaQBuAG4ALgAKAEcA +cgBhAGMAaQBhAHMAIABhACAATQBhAG4AdQAwADIALAAgAEoAdQBsAGkAZQBuACAAeQAgAFAAYQB1AGwA +LgApbxAgAFAAbwBwACAAVQBwACAAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKCMYACAAQwBvAG0AYQBu +AGQAbwApAC0AMW8QGQBMAGkAbgBrACAAVABlAHgAdAAgAEYAaQBlAGwAZAAgACgnpAAgAGQAbwBuAGEA +cgApbxAvAFQAbwBwACAAVABhAGIAIABWAGkAZQB3ACAAKABEAGkAcwBwAG8AcwBpAGMAaQDzAG4ALAAg +AFMAZQBnAHUAcgBpAGQAYQBkACwAIABPAHAAYwBpAG8AbgBlAHMAKVhQcmVmUGFuZV8QF1RleHQgRmll +bGQgQ2VsbCAoQ29ydG8pbxAVAE0AZQBuAHUAIABJAHQAZQBtACAAKCMYACAAQwBvAG0AYQBuAGQAbwAp +XxARVGFibGUgSGVhZGVyIFZpZXdvECQAQwBoAGUAYwBrACAAQgBvAHgAIAAoAEMAYQBtAGIAaQBhAHIA +IABzAPMAbABvACAAcwBpACAAcAByAGUAcwBpAG8AbgBvAClfEBNTdGF0aWMgVGV4dCAoTGFyZ28pXxAV +U3RhdGljIFRleHQgKENhbWJpbzopXxAdVGV4dCBGaWVsZCBDZWxsIChUZXh0IENlbGwpLTFvECEAQgB1 +AHQAdABvAG4AIABDAGUAbABsACAAKABTAPMAbABvACAAcwBpACAAZQBzACAAbQBlAG4AbwByACAAZABl +AClvEBYATQBlAG4AdQAgAEkAdABlAG0AIAAoIyUAIABPAHAAYwBpAPMAbgApAC0AMVtDdXN0b20gVmll +d1ZNYXRyaXhfEBlUZXh0IEZpZWxkIENlbGwgKEVzdGFkbzopbxAvAEMAaABlAGMAawAgAEIAbwB4ACAA +KABNAG8AcwB0AHIAYQByACAAZQBzAHQAYQBkAG8AIABlAG4AIABsAGEAIABiAGEAcgByAGEAIABkAGUA +IABtAGUAbgD6AHMAKV8QJFN0YXRpYyBUZXh0IChQZXRpY2lvbmVzIGRlIGNvbnRyb2w6KV8QYVN0YXRp +YyBUZXh0IChPcmdhbml6YSBsb3MgTWFjcyBjb21wYXJ0aWRvcyBkZSBhcnJpYmEgYWxyZWRlZG9yIGRl +IHR1IHBhbnRhbGxhIHBhcmEgY29udHJvbGFybG9zLilVQWJvdXRfEFtTdGF0aWMgVGV4dCAoUGFyYSBl +bGltaW5hciB1biBob3N0IGRlIGNvbmZpYW56YSwgc2VsZWNjaW9uYXJsbyB5IHByZXNpb25hciBsYSB0 +ZWNsYSBib3JyYXIpXxAVSW1hZ2UgQ2VsbCAodGVsZXBvcnQpbxAeAFAAbwBwACAAVQBwACAAQgB1AHQA +dABvAG4AIABDAGUAbABsACAAKCMYACAAQwBvAG0AYQBuAGQAbwApbxAfAE0AZQBuAHUAIABJAHQAZQBt +ACAAKCHqACAAQgBsAG8AcQB1AGUAbwAgAE0AYQB5APoAcwBjAC4AKQAtADFWVmlldy0yXxA0QnV0dG9u +IENlbGwgKEFycmFzdHJhciB5IFNvbHRhciBhcmNoaXZvcyBlbnRyZSBNYWNzKVpUZXh0IEZpZWxkXxAT +TWVudSAoT3RoZXJWaWV3cyktMV8QGlRleHQgRmllbGQgQ2VsbCAodGVsZXBvcnQpXxAlQnV0dG9uIENl +bGwgKENhbWJpYXIgdHJhcyB1biByZXRhcmRvKW8QFABCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAEEA +YgBvAHUAdCAmAClfECdQcm90b3R5cGUgUHJvdG90eXBlIEJ1dHRvbiBDZWxsIChSYWRpbylfEB1TdGF0 +aWMgVGV4dCAoVHJhbnNmZXJlbmNpYXM6KV8QI0NoZWNrIEJveCAoQ2FtYmlhciB0cmFzIHVuIHJldGFy +ZG8pXENvbnRlbnQgVmlld18QE1N0YXRpYyBUZXh0IChDb3J0bylfEBhUYWIgVmlldyBJdGVtIChPcGNp +b25lcylWVmlldy0xbxAkAEIAdQB0AHQAbwBuACAAQwBlAGwAbAAgACgASABhAGIAaQBsAGkAdABhAHIA +IABjAG8AZABpAGYAaQBjAGEAYwBpAPMAbgApXxASUHJlZlBhbmVDb250cm9sbGVybxAdAE0AZQBuAHUA +IABJAHQAZQBtACAAKCHqACAAQgBsAG8AcQB1AGUAbwAgAE0AYQB5APoAcwBjAC4AKV8QEUJ1dHRvbiBD +ZWxsIChWZXIpXxAnQ2hlY2sgQm94IChTaW5jcm9uaXphciBlbCBwb3J0YXBhcGVsZXMpXxAaVGFibGUg +Q29sdW1uIChjZXJ0aWZpY2F0ZSlfEBtUZXh0IEZpZWxkIENlbGwgKFRleHQgQ2VsbClfEDFDaGVjayBC +b3ggKEludGVudGFyIGRlc3BlcnRhciBsb3MgTWFjcyBlbiByZXBvc28pbxAdAFQAZQB4AHQAIABGAGkA +ZQBsAGQAIABDAGUAbABsACAAKCekACAAcwBpAHQAaQBvACAAdwBlAGIAKV8QMUJ1dHRvbiBDZWxsIChD +YW1iaWFyIGFsIHRvY2FyIGVsIGJvcmRlIGRvcyB2ZWNlcyldQ3VzdG9tIFZpZXctMV8QKUJ1dHRvbiBD +ZWxsIChTaW5jcm9uaXphciBlbCBwb3J0YXBhcGVsZXMpbxAaAE0AZQBuAHUAIABJAHQAZQBtACAAKCHn +ACAATQBhAHkA+gBzAGMAdQBsAGEAcwApAC0AMV8QGVRleHQgRmllbGQgQ2VsbCAoQ2FtYmlvOilvEBcA +TQBlAG4AdQAgAEkAdABlAG0AIAAoIxgAIABDAG8AbQBhAG4AZABvACkALQAxXxAdQnV0dG9uIENlbGwg +KENvbXByb2JhciBBaG9yYSlvEBcATQBlAG4AdQAgAEkAdABlAG0AIAAoIwMAIABDAG8AbgB0AHIAbwBs +ACkALQAxXxAWVGFibGUgQ29sdW1uIChhZGRyZXNzKW8QGABQAG8AcAB1AHAAIABCAHUAdAB0AG8AbgAg +ACgjGAAgAEMAbwBtAGEAbgBkAG8AKW8QHgBCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAFMA8wBsAG8A +IABzAGkAIABwAHIAZQBzAGkAbwBuAG8AKW8QIgBDAGgAZQBjAGsAIABCAG8AeAAgACgASABhAGIAaQBs +AGkAdABhAHIAIABjAG8AZABpAGYAaQBjAGEAYwBpAPMAbgApbxAZAEwAaQBuAGsAIABUAGUAeAB0ACAA +RgBpAGUAbABkACAAKCekACAAZgBvAHIAbwBzAClfEBlUYWIgVmlldyBJdGVtIChTZWd1cmlkYWQpbxAb +AFQAYQBiACAAVgBpAGUAdwAgAEkAdABlAG0AIAAoAEQAaQBzAHAAbwBzAGkAYwBpAPMAbgApXxAgQnV0 +dG9uIENlbGwgKENvbXBhcnRpciBlc3RlIE1hYylvEDEAQgB1AHQAdABvAG4AIABDAGUAbABsACAAKABN +AG8AcwB0AHIAYQByACAAZQBzAHQAYQBkAG8AIABlAG4AIABsAGEAIABiAGEAcgByAGEAIABkAGUAIABt +AGUAbgD6AHMAKW8QGgBQAG8AcAB1AHAAIABCAHUAdAB0AG8AbgAgACgjGAAgAEMAbwBtAGEAbgBkAG8A +KQAtADFvEBUATQBlAG4AdQAgAEkAdABlAG0AIAAoIwMAIABDAG8AbgB0AHIAbwBsAClvEBgATQBlAG4A +dQAgAEkAdABlAG0AIAAoIecAIABNAGEAeQD6AHMAYwB1AGwAYQBzAClvEBQATQBlAG4AdQAgAEkAdABl +AG0AIAAoIyUAIABPAHAAYwBpAPMAbgApXkNvbnRlbnQgVmlldy0xbxAcAFMAdABhAHQAaQBjACAAVABl +AHgAdAAgACgAQQBjAHQAdQBhAGwAaQB6AGEAYwBpAPMAbgA6AClfEB5DaGVjayBCb3ggKENvbXBhcnRp +ciBlc3RlIE1hYylfEBxDaGVjayBCb3ggKEFjdGl2YXIgdGVsZXBvcnQpW1NsaWRlciBDZWxsbxAdAEwA +aQBuAGsAIABUAGUAeAB0ACAARgBpAGUAbABkACAAKCekACAAcwBpAHQAaQBvACAAdwBlAGIAKW8QIABU +AGUAeAB0ACAARgBpAGUAbABkACAAQwBlAGwAbAAgACgAQQBjAHQAdQBhAGwAaQB6AGEAYwBpAPMAbgA6 +AClvEBwAQwBoAGUAYwBrACAAQgBvAHgAIAAoAFMA8wBsAG8AIABzAGkAIABwAHIAZQBzAGkAbwBuAG8A +KV8QEUhvcml6b250YWwgU2xpZGVyXxATSG9yaXpvbnRhbCBTY3JvbGxlcm8QOABCAHUAdAB0AG8AbgAg +AEMAZQBsAGwAIAAoAFIAZQBjAGgAYQB6AGEAcgAgAHMAaQAgAGUAbAAgAGgAbwBzAHQAIABuAG8AIABl +AHMAdADhACAAeQBhACAAZQBuACAAbABhACAAbABpAHMAdABhAClfEDJDaGVjayBCb3ggKEFycmFzdHJh +ciB5IFNvbHRhciBhcmNoaXZvcyBlbnRyZSBNYWNzKV8QHVB1c2ggQnV0dG9uIChDb21wcm9iYXIgQWhv +cmEpXxAlVGV4dCBGaWVsZCBDZWxsIChIb3N0cyBkZSBjb25maWFuemE6KV8QMUJ1dHRvbiBDZWxsIChQ +cmVndW50YXJtZSBzaSBjb25maWFyIGVuIGVzdGUgaG9zdClfEBdUZXh0IEZpZWxkIENlbGwgKExhcmdv +KV8QHlZlcnNpb24gVGV4dCBGaWVsZCAoPHZlcnNpb24+KW8QcgBTAHQAYQB0AGkAYwAgAFQAZQB4AHQA +IAAoAKkAIAAyADAAMAAzAC0AMgAwADAAOAAgAGEAYgB5AHMAcwBvAGYAdAAKAEIAYQBzAGEAZABvACAA +ZQBuACAAdQBuAGEAIABpAGQAZQBhACAAZABlACAARABhAHYAZQAgAFMAYQBnACAAeQAgAEoAZQByAGUA +bQB5ACAAUQB1AGkAbgBuAC4ACgBHAHIAYQBjAGkAYQBzACAAYQAgAE0AYQBuAHUAMAAyACwAIABKAHUA +bABpAGUAbgAgAHkAIABQAGEAdQBsAC4AKW8QJgBCAHUAdAB0AG8AbgAgAEMAZQBsAGwAIAAoAEMAYQBt +AGIAaQBhAHIAIABzAPMAbABvACAAcwBpACAAcAByAGUAcwBpAG8AbgBvAClfEDlCdXR0b24gQ2VsbCAo +TW9zdHJhciBiaXNlbCBtaWVudHJhcyBzZSBjb250cm9sYSBvdHJvIE1hYylvEBkAVABlAHgAdAAgAEYA +aQBlAGwAZAAgAEMAZQBsAGwAIAAoJ6QAIABmAG8AcgBvAHMAKV8QE1RhYmxlIENvbHVtbiAobmFtZSlf +ECFQdXNoIEJ1dHRvbiAoTW9zdHJhciBjZXJ0aWZpY2FkbylvEBkAVABlAHgAdAAgAEYAaQBlAGwAZAAg +AEMAZQBsAGwAIAAoJ6QAIABkAG8AbgBhAHIAKdIADgA+C2sN+4ECp6UDCAdcB10HXgdagIyBAbiBAb+B +AcSBAa3SAA4APgtrDgOBAqelAygH3AfcB9wHsICNgQG5gQG5gQG5gQGu0gAOAD4Law4LgQKnrxDZAGQB +ZAJDAJYAXQetAdcAmQJFAJMAkgI+AwgAjQJKBF4FeQRIANgAhAP6AasGZwKtAJoAfQB8AHoAugVoAyAE +OwRHAIoENgBtBDcGWQBZAFcAagBIBJ8E+gRBAGcFigeWAFgAnACXBD8GgACfAhwA3gOoCCsAlQQ6A1ED +kQQ+BMEFmwUtB9gAUgCOAFEDUASwAckAhQCCAWsATwCAAIMGfwCBB0AENQBTAWMAdACbAWUHXABvBDkC +6wQ4AJ0ECgCgAGsHWgBoA08AXAW4AJgEMwJcANAAiwCmAFsHFQBNAF8EQwdYCIEC8wDVBawAHwRAAJEA +jwB7AoAAYgRFB1kLhQCJAIcCQQBOAFAAfwB+BOMHwAdeAS0GYwBuAHADXwRNBSsBVwbMAkIAdgBsAkQB +0gI9Bz0HZgUuALAAaQB4AGUBiQKbBDQAXgBjAm8BPAQ9AFoASQQbAJQAdQBKBCgEfgBUBPUGugCMBSwE +QgCIAkACGwddAIYAVQBgBI4EPAZ+Bn0ATAB5AHIAVgCQAHMAcQB3BtwEbgKsBEYERAKcAEsAngcFB1sE +0gapAGEH9ABmCAmBAhOAOICIgQKOgQIHgQGxgEqBApOAxoECioECh4BogIyBAn6AZoDZgQEbgQF8gBeB +AmmAxIBDgQFXgH+BApeBAmCBAl6BAluARYEBF4CQgPOBAXiBAnaA34ECOYDjgQFUgQIAgQH8gQIpgAmA +6YD8gQElgQIggQEfgQGpgQH+gQKbgQKPgQEdgQFhgQKggM2AYYCwgQHUgQKMgO+AqoCpgQEZgPGBASOB +AQyBAbuBAdOBAn+BAdGApYDtgFyBAmuBAmaAM4EBkIECY4ECaIEBX4ECZIEBlYDbgQHwgDGBAk2BApmA +QYEBuIECP4DrgLyA54ECnYDIgQKigQIygQGtgQIjgJiBAgaBASiBApCA04BqgBSBAniACoECBIEBfoEB +jIECC4EBY4EBmIEB94C4gBWBASeAAoEBIYECg4ECgIECXYBzgQIQgQFsgQGngQKlgQJzgQJxgHGBAY6B +Ac+BAmKBAmGA+YEBtYEBxIAogQFWgQI8gQJCgKCA1YEBBoAvgQFugHWBAlCBAjaAwoBIgGSBAZGBAaKB +AQ+AC4ECJoECV4ECGIA6gISA14ECCYECEoBvgC2BARWBAgKAE4DPgQKLgQJPgCOA0YDhgQHygPuBAWmB +AnuBAQmBAVKBAnKAbYBggQG/gQJugQH0gQIMgOWA94EBXYEBW4AngQJZgQJIgQH2gQKCgQJLgQJFgQJS +gQFygN2AfYEBcIEBZ4B4gCWBAp6BAXqBAbOA9YEBZYECDoEBwYECH4EBxtIADgA+C2sO54ECp68Q2Q7o +DukO6g7rDuwO7Q7uDu8O8A7xDvIO8w70DvUO9g73DvgO+Q76DvsO/A79Dv4O/w8ADwEPAg8DDwQPBQ8G +DwcPCA8JDwoPCw8MDw0PDg8PDxAPEQ8SDxMPFA8VDxYPFw8YDxkPGg8bDxwPHQ8eDx8PIA8hDyIPIw8k +DyUPJg8nDygPKQ8qDysPLA8tDy4PLw8wDzEPMg8zDzQPNQ82DzcPOA85DzoPOw88Dz0PPg8/D0APQQ9C +D0MPRA9FD0YPRw9ID0kPSg9LD0wPTQ9OD08PUA9RD1IPUw9UD1UPVg9XD1gPWQ9aD1sPXA9dD14PXw9g +D2EPYg9jD2QPZQ9mD2cPaA9pD2oPaw9sD20Pbg9vD3APcQ9yD3MPdA91D3YPdw94D3kPeg97D3wPfQ9+ +D38PgA+BD4IPgw+ED4UPhg+HD4gPiQ+KD4sPjA+ND44Pjw+QD5EPkg+TD5QPlQ+WD5cPmA+ZD5oPmw+c +D50Png+fD6APoQ+iD6MPpA+lD6YPpw+oD6kPqg+rD6wPrQ+uD68PsA+xD7IPsw+0D7UPtg+3D7gPuQ+6 +D7sPvA+9D74Pvw/AgQMvgQMwgQMxgQMygQMzgQM0gQM1gQM2gQM3gQM4gQM5gQM6gQM7gQM8gQM9gQM+ +gQM/gQNAgQNBgQNCgQNDgQNEgQNFgQNGgQNHgQNIgQNJgQNKgQNLgQNMgQNNgQNOgQNPgQNQgQNRgQNS +gQNTgQNUgQNVgQNWgQNXgQNYgQNZgQNagQNbgQNcgQNdgQNegQNfgQNggQNhgQNigQNjgQNkgQNlgQNm +gQNngQNogQNpgQNqgQNrgQNsgQNtgQNugQNvgQNwgQNxgQNygQNzgQN0gQN1gQN2gQN3gQN4gQN5gQN6 +gQN7gQN8gQN9gQN+gQN/gQOAgQOBgQOCgQODgQOEgQOFgQOGgQOHgQOIgQOJgQOKgQOLgQOMgQONgQOO +gQOPgQOQgQORgQOSgQOTgQOUgQOVgQOWgQOXgQOYgQOZgQOagQObgQOcgQOdgQOegQOfgQOggQOhgQOi +gQOjgQOkgQOlgQOmgQOngQOogQOpgQOqgQOrgQOsgQOtgQOugQOvgQOwgQOxgQOygQOzgQO0gQO1gQO2 +gQO3gQO4gQO5gQO6gQO7gQO8gQO9gQO+gQO/gQPAgQPBgQPCgQPDgQPEgQPFgQPGgQPHgQPIgQPJgQPK +gQPLgQPMgQPNgQPOgQPPgQPQgQPRgQPSgQPTgQPUgQPVgQPWgQPXgQPYgQPZgQPagQPbgQPcgQPdgQPe +gQPfgQPggQPhgQPigQPjgQPkgQPlgQPmgQPngQPogQPpgQPqgQPrgQPsgQPtgQPugQPvgQPwgQPxgQPy +gQPzgQP0gQP1gQP2gQP3gQP4gQP5gQP6gQP7gQP8gQP9gQP+gQP/gQQAgQQBgQQCgQQDgQQEgQQFgQQG +gQQHEQFXEKwQ9hEBfxC3EgABh0YSAAGHOBIABJUeEQFqEQFdEQHHEgAElPcQ9xIABJUSEgABiFkSAAGI +CxIAAYhbEQFhEgABiCERAaQSAAGIWBIAAYdOEQGGEQG3EgAElR8RAcYRAYMRAYAQuhIAAYhgEgAElNYQ +whEBwREBphEBbhEBQhEBCBIAAYglEgAElPoQzhEBWRCREgABiDIRAZ0RAUYRAaUSAAGHrxIAAYdFEMoS +AASVIREBrxEBDxEBihIABJUkELwRAWYRAWkRASkRAUkQvxEBaBIAAYeZEQG7EgABh18SAASVDxEBohIA +AYfDEQEqEQFkEJwQ+RIAAYg7ELsRAUQRAcQSAAGHKBCrEgAElQ0RAaMRAYsRAcMQohEBbxDNEIgRAUES +AASVIBCuEQEjEQG6EQGbEgADDjYRAZISAASVIhIAAYgKEgAElSURAakQphEBYxD4EQFOEQFAEQFFEgAE +lPgRAYERAacQiRIABJT8EgABiAEQrxC4EgAElQcQpBEBlhIAAYeWEgAElRsSAAGH5hIABJUdEgAElQ4R +AcURAVYRAYISAASVChDpEQFwEKUT//////////0RAZERAbASAASVCREBUREBXhEBXBIAAYg8EgABh0gR +ASUQDBEBiBEBwhEBvhIAAYeYEgABh+URAZ4QyRIAAYgQEQGyEQFaEQGVEQG4EJgRAbkQoRIAAYdEEQGf +EL0RAUoRAY8SAASVDBIAAYdMEgABiFIRAWsSAASU+REBAxIAAYgdEQHAEQEEEgAElPsQvhEBvREBSxDo +EgAElRwSAAGIDhEBChEBoRIAAYfwEQGoEQGgEQGFEQFYEQF9EQFlEgAElRERARAQqhIAAYeoEQGcEQGH +EQGJEBoSAASVBREBlxEBkxIABJUGEQFDEQFbEgABiF8SAAGIDxEBthEBvxEBUBEBtRDqEgAElSMSAAGI +YRCoEgABh2ISAASVCBCtEgABh8QRAWASAAGHxdIADgA+AEYQl4BXoNIADgA+C2sQmoECp6DSAA4APgtr +EJ2BAqeg0gA3ADgQnxCgohCgADteTlNJQk9iamVjdERhdGEACAAZACIAJwAxADoAPwBEAFIAVABmCIII +iAjTCNoI4QjvCQEJHQkrCTcJQwlRCVwJagmGCZQJpwm5CdMJ3QnqCewJ7wnyCfUJ+An6Cf0J/woCCgUK +CAoLCg0KDwoSChUKGgodCiYKMgo0CjYKSQpSClsKZgprCnoKgwqWCp8KqgqsCq0Ktgq9CsoK0ArZCtsL +kAuSC5QLlguYC5oLnQugC6MLpgupC6wLrwuyC7ULuAu7C74LwQvEC8cLygvNC9AL0wvWC9kL3AvfC+IL +5QvoC+sL7gvxC/QL9wv6C/0MAAwDDAYMCQwMDA8MEgwVDBgMGwweDCEMJAwnDCoMLQwwDDMMNgw5DDwM +PwxCDEUMSAxLDE4MUQxUDFcMWgxdDGAMYwxmDGkMbAxvDHIMdQx4DHsMfgyBDIQMhwyKDI0MkAyTDJYM +pwy1DL4MxgzIDMoMzAzODO8NAQ0JDRINGw0nDTMNNQ03DTkNPA0+DUANQg1EDWENbA1uDXANcg10DXcN +eQ17DZQNoQ2oDbENug3HDdMN3g3nDe4OBQ4UDiUOJw4pDisOLQ5KDlEOWw5dDl8OYQ5jDmYOZw5pDoYO +kg6UDpYOmA6bDp0Onw6hDrsO7A74Dw4PIw8yD0QPTw9ZD2cPeQ+GD5QPmQ+bD50Pnw+hD6MPpQ+nD6kP +qw+wD7kPzw/gD+cP7g/3D/kQAhAEEAcQFBAdECIQKRAqEDMQPRA/EEgQTxBhEGoQcxB8EIkQlhCfEKoQ +sxC9ENYQ3xDmEP4RDxERERMRFREXESURNhE4EToRPBE+EUsRXBFeEWARYhFlEZYRoxGwEb4RyBHWEeMR +7RH/EhMSHRIpEisSLhIxEjMSOBI6EjwSPxJBEkMSRhJIEmMSexKEEoYSixKoEqoSrBKuErESsxK2Er8S +wRLEEsYS5xLpEusS7RLwEvIS9BL2Ev8TARMKEwwTDhMQExITMxM1EzcTORM7EzwTPhNAE1oTjxOiE6cT +qROrE60TrxOxE7MTtRO3E7wT0RPaE+YT6BPqE/MT/BQBFBcUOBQ6FDwUPhRAFEMURBRGFEgUYRSWFKQU +phSoFKoUrBSuFLAUshS0FLkUxhTTFOIU5BTmFOgU8BT7FQQVCRUcFSUVJxVIFUoVTBVOFVAVURVTFVUV +bhWjFaUVpxWpFasVrRWvFbEVxBX5FggWEhYmFj8WVxZZFlwWXhZgFmIWZBZmFmcWaRZqFmwWdRZ3FnoW +fBaFFocWjBaOFpAWsRazFrUWtxa5FrwWvRa/FsEW2Rb6Fw4XGhccFx4XIBciFyQXKRcrF4EXkheUF50X +nxeiF7cXvxfMF9gX5hfoF+oX7BfuF/AX9xgEGBEYGRgbGB0YKRgyGDcYTBhOGFAYUhhUGGcYdBh2GHkY +ghiLGJ0YphixGL0YxhjNGNwY5Bj9GQYZDRkmGS8ZMRk4GToZPBk+GVcZZBluGXAZchl0GXYZeBl6GYEZ +mBmhGaYZtBnJGcsZzRnPGdEZ0xnoGeoZ7BnuGfAZ+Rn7Gf4aABoJGgsaHhogGiIaJBomGigaKhosGi4a +MBpNGk8aURpTGlUaWBpZGlsadBqVGpcamRqbGp0anxqkGqYa9hsTGxUbFxsZGxsbJBslGycbQRtyG3Qb +dht4G3obfBt+G6kbshu0G9Eb0xvVG9cb2RvaG9wb9hwrHC0cLxwxHDMcNRw3HDkcaByFHIcciRyLHI0c +jhyQHKkcyhzMHM4c0BzSHNQc2RzbHPYdQR1NHVcdZh1yHYodlR2fHbQdwh3KHcwdzh3QHdId1B3WHdgd +2h3cHd4d3x3hHeMd6B3qHgMeDB4OHhUeFx4ZHhseUB5ZHlseXR5fHmEeYx5lHmcejR6WHpgemh6oHrEe +sx7sHvIe9B72Hvge+h78Hv4fAB9XH5Afkh+UH5YfmB+aH5wfnh+gH6Mf1B/eH+UgDiAQIBIgFCAWIBwg +KSArIC4gNyBCIEsghCCQIJkgpiC5IMYg0iDgIO4g8CDyIPQg9iD4IPog/CEPIREhEyEVIRchICEiIS0h +LyExITMhNSE3IWAhaiF0IX4hgCGCIYQhhiGIIYshjSGPIZEhkyGcIZ4hoSGjIfoiHCIyIj8iVCJuIooi +pSKxItAi3yLrIu0i7yL0IvYi+CL6Ivsi/SMGIw8jESMSIxQjFiMYIxojIyMvIzsjRiNfI2EjYyNlI2cj +aSOSI5QjliOYI5ojnCOeI6AjoiOsI7UjviPSI+cj6SPrI+0j7yQGJA8kGCQmJC8kMSQ4JDokPCQ+JGck +diSDJIskliSlJLAkuyS8JL4kwCTCJMskzSTWJN8k4STmJQMlCCUKJQwlDiUQJRIlGSUmJSglNCVJJUsl +TSVPJVElYyVsJXcliyWsJbElsyW1JbcluSW7JcAlwiXMJeEl4yXlJecl6SYCJgsmECYeJkcmSCZKJkwm +TiZXJlkmYiZkJmwmiSaLJo0mjyaRJpMmpibHJskmyybNJs8m0SbTJvQm9ib4JvonAycFJw4nECccJzkn +Oyc9Jz8nQSdDJ08nZCdmJ2gnaidsJ3gnqSerJ60nryexJ7MntSe3J7wnxSfJJ94n4CfiJ+Qn5ifwJ/0n +/ygEKA0oEighKDkoQihLKFYodyiAKIkokyiVKJcomSibKJ0onyioKMQo0SjaKOUo8CkVKRcpGSkbKR0p +HykhKSopRilPKVEpVClWKWwphymQKZkppinDKcUpxynJKcspzCnOKeYqByoJKgsqDSoPKhEqEyosKkkq +SypNKk8qUSpSKlQqbCqNKo8qkSqTKpUqlyqZKq8quirEKt0q3yrhKuMq5SrnKuoq8isHKwkrCysOKxAr +GSsbKx4rICs9Kz8rQStDK0YrSCtKK1MrVSuEK4YriCuKK4wrjiuQK5IrlCuWK5grmyueK6ErpCunK6or +rSuwK7Mrtiu5K7wr2SvbK90r3yvhK+Ir5Cv+LDMsNSw3LDksOyw9LD8sQSxqLIcsiSyLLI0sjyyQLJIs +qyzMLM4s0CzSLNQs1izYLOotBy0JLQstDS0PLRAtEi0qLUstTS1PLVEtUy1VLVctdC2RLZMtlS2XLZkt +mi2cLbUt1i3YLdot3C3eLeAt4i3qLgcuCS4LLg0uDy4QLhIuKy5gLmIuZC5mLmguai5sLm4uty7ULtYu +2C7aLtwu3S7fLvkvLi8wLzIvNC82LzgvOi88L2Uvgi+EL4YviC+KL4svjS+nL9wv3i/gL+Iv5C/mL+gv +6jANMCowLDAuMDAwMjAzMDUwTzCEMIYwiDCKMIwwjjCQMJIwsDDNMM8w0TDTMNUw1jDYMPIxJzEpMSsx +LTEvMTExMzE1MWgxhTGHMYoxjDGOMY8xkTGrMfYyEzIlMjAyQjJXMmUybDJ1MnYyeDJ7Mn0yfzKBMoMy +hDKFMogyijKPMpgymjLLMtMy5zLyMwAzCjMXMx8zITMkMyYzKzMtMzIzNDM3MzkzPDM+M0szVzNaM10z +YDNzM4AzgjOEM4YzmDOlM6czqTOsM78z0zPcM+Ez6jPsM/c0ADQCNA00DzQSNBU0GDQbNEg0SjRNNFA0 +UjRUNFc0WTRcNF40bzScNJ40oTSkNKY0qDSrNK00sDSyNMU08jT0NPc0+jT8NP41ATUDNQY1CDUhNU41 +UDVTNVY1WDVaNV01XzViNWQ1iTWSNZc1oDWtNcE10DXZNeY19DYRNhM2FTYYNhs2HDYeNjc2WDZaNlw2 +XzZhNmQ2aTZrNnE2jjaQNpI2lTaYNpk2mza1Nuo27DbuNvA28jb1Nvc2+jciNz83QTdDN0Y3STdKN0w3 +ZjebN503nzehN6M3pjeoN6s3xTfiN+Q35jfpN+w37TfvOAk4PjhAOEI4RDhGOEk4SzhOOHQ4kTiTOJU4 +mDibOJw4nji3OOA47DkCOQs5DTkQORI5FTkYOR05HjkhOSQ5azlyOX05hDmQOZg5qzmyOcA51DnmOfo6 +DToZOiA6LTo/OkI6RTpIOks6TTpQOlM6VjpZOlw6XTpgOmI6ZTpoOmk6ajp3On86gjqbOp46oTqkOqc6 +qjqtOrA6szq2Ork6vDq/Otg62zreOuE65DrnOuo67TrwOvM69jr5Ovw7BDsTOy07TztiO3Y7fTuKO5I7 +qju5O8076jv1PAE8EDwaPCY8Mjw1PDY8STxKPFM8WDx1PHg8hTySPJU8mDybPJ08qjytPK48rzy4PL08 +yjzTPNg87Tz6PP08/jz/PQI9BT0OPRw9Hz0hPSo9Lz04PVU9WD1ZPWw9bT1vPXg9fz2VPZ49oT2kPbE9 +tD23Pbo9vj3HPc494j3vPgQ+Bj4IPgs+DT4jPjg+Oj48Pj8+QT5LPmg+aj5tPnA+cz50PnY+kD7bPtw+ +3j7hPuQ+5j7pPuw+7T7uPvE++j78Py0/MD8zPzU/Nz85Pzw/Pz9CP08/Uj9VP1g/YT9jP2w/bj95P3w/ +fz+CP4U/iD+1P7g/uz++P8A/wj/FP8g/yz/4P/s//kABQANABUAIQAtADkA7QD5AQUBEQEZASEBLQE5A +UUB+QIFAhECHQIlAi0COQJFAlECxQLNAtUC4QLtAvEC+QNdBDEEOQRBBEkEUQRdBGUEcQUpBZ0FpQWtB +bkFxQXJBdEGMQb1Bv0HBQcRBx0HJQcxB3kHnQelCBkIIQgpCDUIQQhFCE0IrQkxCTkJQQlNCVUJYQlpC +YkJ/QoFChEKHQopCi0KNQqZC30LnQv1DEkMdQyhDM0NBQ15DZ0NqQ21DcENzQ3xDhUOKQ4tDlEOVQ55D +oEOxQ7NDvEO/Q8lD0kPbQ+hD8UP8RAVEIkQkRCZEKUQsRC1EL0RIRGlEa0RtRHBEckR1RHdEfUSaRJxE +nkShRKREpUSnRMBE9UT3RPlE+0T9RQBFAkUFRTxFRUVORVdFYkVtRYZFlEWxRbpFv0XSRdpF60XtRe9F +8UX0RglGGkYcRh5GIUYkRjJGQ0ZFRkdGSkZNRn5GgUaERodGiUaORpFGlEaXRppGnUa4RspG00bVRvJG +9Ub3RvpG/UcARwNHDEcORx1HIEcjRyZHKUcsRy9HMkdbR2ZHckd1R3hHe0d8R39HgkeDR4ZHiUeSR5RH +o0emR6lHrEevR7JHtUe4R9RH8EgWSC5IYkiDSKBIukjbSONI60jzSP5JA0kGSQlJDkkPSRxJHkkgSSNJ +LEk1STxJSElRSVxJaEmJSYxJjkmRSZRJlUmYSZtJtEnVSddJ2UncSd9J4knnSelJ+kn8SgVKCEocSkVK +SEpKSk1KUEpTSlRKV0paSl1KckqLSqxKrkqwSrNKtUq4SrpKxErlSuhK6krtSvBK8Ur0SvdLD0swSzJL +NEs3SzpLPUtCS0RMD0wgTCJMK0wtTDBMWUxcTF5MYUxkTGdMakxrTG5McUx0TIZMnEzFTMpMzEzOTNFM +1EzXTNhM20zdTPRNBU0HTQlNKE1RTVRNVk1ZTVxNX01gTWNNZk1pTYBNpU2nTalNrE2vTbJNtU23TcZN ++k4jTiZOKE4rTi5OMU4yTjVOOE47TlJOd055TntOfk6BToROh06JTphPSE9TT15PZ094T3pPfE9+T4FP +jE+dT59PoU+jT6ZPvU/OT9BP0k/VT9hP6U/7UApQDVAQUBFQFFAdUB9QTlBRUFRQV1BaUF1QYFBjUGZQ +aVBsUG9QclB1UHhQe1B+UIFQhFCHUIpQjVCQUKVQvVDQUOZQ/VEZUTRRO1FUUXlRklGnUblR1lHvUgxS +HlIyUk9SaVKCUp5So1KmUq9StFK9UsRS2VLmUu5S/1MBUwNTBVMIUyJTM1M1UzdTOVM8U0tTXFNeU2FT +Y1NmU35Tj1ORU5NTllOZU6pTvlPBU8RTx1PKU9NT1VPYU9tT8lP3U/pUC1QNVA9UEVQUVClUOlQ8VD5U +QFRDVEhUWVRbVF1UX1RiVHxUjVSPVJFUk1SWVK5Uv1TBVMNUxVTIVOJU81T1VPdU+VT8VQ1VD1USVRRV +F1UqVTtVPVU/VUFVRFVcVW1Vb1VxVXNVdlWHVYlVjFWOVZFVnVWuVbBVslW0VbdVyVXaVdxV3lXgVeNV +9VYGVghWClYMVg9WLFY2VkBWX1ZiVmVWaFZrVm1WcFaMVpRWp1awVrdWz1bwVvpW/VcAVwNXBlcJVwxX +D1c5Vz9XYldvV3JXdVd4V3tXfleXV6lXxlfJV8xXz1fSV9VX2Ff1V/hX+1f+WAFYA1gGWDRYW1h4WHtY +fliBWIRYh1iKWLRY11j0WPdY+lj9WQBZAlkFWTBZUll3WY1ZkFmTWZZZmVmcWZ5ZoVmkWcFZylnXWdpZ +41nmWelZ7FnvWfhZ+1n+WgFaBFoYWjVaUFpuWndalFqXWppanVqgWqJapVrVWuFbBFshWyRbJ1sqWy1b +L1syW1VbcVuOW5FblFuXW5pbnFufW8Nb4Fv9XABcA1wGXAlcDFwPXDRcUlxvXHJcdVx4XHtcfVyAXLRc +21z4XPtc/l0BXQRdB10KXS1dSV1mXWldbF1vXXJddV14XZ9dv13cXd9d4l3lXehd6l3tXhlePF5hXmRe +Z15qXm1ecF5yXnVeeF6FXohekV6UXpdeml6dXqZeqV6sXq9esl7PXtJe1V7YXtte3V7gXwlfJl8pXyxf +L18yXzVfOF9dX2BfY19mX2lfbF9vX3JfdV+CX4Vfjl+RX5Rfl1+aX6Nfpl+pX6xfr1/UX9df2l/dX+Bf +41/mX+lf7GAcYCVgS2BYYFtgZGBnYGpgbWBwYHlgfGB/YIJghWCiYKVgqGCrYK5gsWC0YNpg/2ECYQVh +CGELYQ5hEWEUYRdhJGEnYTBhM2E2YTlhPGFFYUhhS2FOYVFhbmFxYXRhd2F6YXxhf2GpYcZhyWHMYc9h +0mHUYddh/GH/YgJiBWIIYgtiDWIQYhNiIGIjYixiL2IyYjViOGJBYkRiR2JKYk1iamJtYnBic2J2Ynhi +e2KYYptinmKhYqRipmKpYsZiyWLMYs9i0mLUYtdi9GL3Yvpi/WMAYwNjBmMjYyZjKWMsYy9jMmM1Y15j +g2OGY4ljjGOPY5JjlWOYY5tjqGOrY7Rjt2O6Y71jwGPJY8xjz2PSY9Vj8mP1Y/hj+2P+ZABkA2QoZCtk +LmQxZDRkN2Q5ZDxkP2RMZE9kWGRbZF5kYWRkZG1kcGRzZHZkeWSWZJlknGSfZKJkpGSnZM9k8GUNZRBl +E2UWZRllHGUfZUxlcmWPZZJllWWYZZtlnWWgZb1lwGXDZcZlyWXLZc5l62XuZfFl9GX3Zfpl/WYpZkhm +ZWZoZmtmbmZxZnNmdmamZstmzmbRZtRm12baZtxm32biZw5nG2ceZydnKmctZzBnM2c8Zz9nQmdFZ0hn +bWdwZ3Nndmd5Z3xnfmeBZ4RnoWeuZ7Fnume9Z8Bnw2fGZ89n0mfVZ9hn22f4Z/tn/mgBaARoB2gKaCdo +KmgtaDBoM2g2aDloVmhZaFxoX2hiaGRoZ2iBaJ5ooWikaKdoqmisaK9o1GjXaNpo3WjgaONo5WjoaOtp +HGlDaVBpU2lcaV9pYmllaWhpcWl0aXdpeml9aZppnWmgaaNppmmpaaxp12n7ahhqG2oeaiFqJGonaipq +R2pKak1qUGpTalZqWWp2anlqfGp/aoJqhGqHarRq0WrUatdq2mrdat9q4mr/awJrBWsIawtrDWsQay1r +MGszazZrOWs7az5rZGuDa5RrnWuka6drqWusa69rwmwKbBNsGGwsbD1sQGxCbEVsSGxpbHpsfWx/bIJs +hWylbLZsuWy7bL5swW2kbbVtuG26bb1twG3RbdRt123abd1vYG9xb3Rvd296b31v5W/2b/lv/G//cAJx +H3EocStyLHIucjByM3I1cjdyOnI9ckByQ3JGckhyS3JNck9yUXJUcldyWXJbcl1yX3JhcmRyZ3Jqcm1y +cHJycnRydnJ4cntyfXJ/coJyhHKHcolyjHKPcpFyk3KWcphymnKccp9yoXKkcqZyqHKqcqxyrnKxcrRy +t3K6crxyvnLAcsNyxnLJcstyzXLPctJy1HLXctly3HLecuBy4nLlcudy6XLrcu1y8HLycvVy93L6cv1y +/3MBcwRzB3MJcwtzDnMQcxJzFXMXcxlzG3MdcyBzI3MmcylzK3Mtcy9zMnM1czdzOXM8cz5zQHNCc0Vz +R3NJc0xzT3NRc1RzV3Nac1xzXnNgc2lza3Nuc3xzhXOKc5NzlnSXdJl0m3SedKB0onSldKh0qnStdLB0 +snS0dLZ0uHS6dL10v3TBdMN0xXTHdMl0y3TOdNB00nTVdNd02XTbdN104HTidOR053TpdOx07nTxdPR0 +9nT4dPp0/HT+dQB1AnUEdQd1CXULdQ11D3URdRN1FnUZdRt1HXUfdSF1I3UmdSl1K3UtdS91MXUzdTV1 +N3U6dTx1PnVAdUJ1RHVGdUh1SnVMdU51UXVTdVV1WHVadVx1X3VhdWN1ZXVndWl1a3VudXB1cnV0dXZ1 +eXV8dX91gnWEdYZ1iHWLdY51kHWSdZR1lnWYdZp1nHWedaB1o3Wmdah1q3WudbF1s3W1dbd1wHXDdsZ2 +yHbKdsx2znbQdtN21nbZdtx233bhduR25nbodup27XbvdvJ29Hb2dvh2+nb9dv93AncFdwh3C3cNdw93 +EXcTdxV3GHcbdx13IHcidyR3J3cqdyx3LncxdzN3NXc3dzp3PHc+d0F3Q3dFd0d3SndMd093UndVd1d3 +WXdcd153YXdkd2Z3aHdqd213b3dyd3R3dnd4d3t3fneAd4J3hHeGd4h3i3eOd5F3k3eVd5h3mnecd593 +onekd6d3qXerd653sHeyd7R3tne4d7t3vnfBd8R3xnfId8p3zXfQd9J31HfXd9l323fdd+B34nfkd+d3 +6nftd+938nf1d/d3+Xf8eAV4CHkLeQ55EXkUeRd5GnkdeSB5I3kmeSl5LHkveTJ5NXk4eTt5PnlBeUR5 +R3lKeU15UHlTeVZ5WXlceV95YnlleWh5a3lueXF5dHl3eXp5fXmAeYN5hnmJeYx5j3mSeZV5mHmbeZ55 +oXmkead5qnmtebB5s3m2ebl5vHm/ecJ5xXnIect5znnRedR513naed154HnjeeZ56Xnsee958nn1efh5 ++3n+egF6BHoHegp6DXoQehN6FnoZehx6H3oieiV6KHorei56MXo0ejd6Ono9ekB6Q3pGekl6THpPelJ6 +VXpYelt6XnphemR6Z3pqem16cHpzenZ6eXp8en96gnqFeoh6i3rMevl7QHtMe1h7a3u+e/h8EHwufJZ8 +qny+fMx88H0CfQ99QX2IfeF+In6Efpx+wH7ZfuV/NH9qf45/x3/ygBOAYIB0gWOBpoHbgjyCRYJfgoyC +oILrgwGDGYM5g36DrYO5g8CD3IQ9hGSEyITOhSyFRIWDhcSFy4YChg2GI4ZAhmiGk4a9ht2HA4cQhyaH +QYdIh5OHqIflh/mII4hAiF6IkojPiQOJEYk9iXSJkInBieGKEooril6KnYrkixmLNYtui5GL9owtjFqM +jYy4jMeNAo0jjUKNTo2Ljc6OCY4djjOOpo7bjvuPI49Xj3GPkpB5kMiRBJE5kU+Rc5GokbGRtJG/kcGR +xJHHkcqRzZHWkdmR5JHmkemR7JHvkfKR+5H+k7OTtpO4k7qTvZPAk8OTxZPIk8qTzZPQk9KT1JPXk9mT +25Pek+GT45Pmk+iT6pPtk++T8pP1k/iT+5P9lACUApQElAeUCpQMlA+UEZQUlBeUGpQdlB+UIZQjlCaU +KZQslC+UMpQ1lDiUO5Q+lEGUQ5RFlEeUSpRNlE+UUZRTlFaUWJRblF6UYZRklGeUapRslG6UcJRzlHaU +eJR7lH6UgZSElIeUipSMlI+UkZSUlJeUmZSclJ+UoZSjlKWUqJSqlK2UsJSzlLaUuJS7lL6UwZTDlMWU +x5TKlMyUz5TSlNWU2JTblN6U4ZTjlOWU6JTqlO2U8JTzlPaU+JT7lP6VAZUElQeVCpUMlQ+VEpUVlRiV +GpUdlSCVIpUllSiVK5UtlS+VMpU0lTeVOZU8lT+VQZVDlUWVSJVLlU6VUJVTlVaVWZVblV2VX5VilWWV +Z5VplWyVb5VxlXOVdpV5lXuVfZV/lYKVhJWHlYqVjZWQlZOVlZWXlZqVnZWglaOVpZWnlaqVrZWvlbKV +tZW4lbuVvpXBlcSVx5XJlcuVzpXRldOV1ZXYlduV3pXgleOV5pXpleyV75X4lfuXsJezl7aXuZe8l7+X +wpfFl8iXy5fOl9GX1JfXl9qX3Zfgl+OX5pfpl+yX75fyl/WX+Jf7l/6YAZgEmAeYCpgNmBCYE5gWmBmY +HJgfmCKYJZgomCuYLpgxmDSYN5g6mD2YQJhDmEaYSZhMmE+YUphVmFiYW5hemGGYZJhnmGqYbZhwmHOY +dph5mHyYf5iCmIWYiJiLmI6YkZiUmJeYmpidmKCYo5immKmYrJivmLKYtZi4mLuYvpjBmMSYx5jKmM2Y +0JjTmNaY2ZjcmN+Y4pjlmOiY65jumPGY9Jj3mPqY/ZkAmQOZBpkJmQyZD5kSmRWZGJkbmR6ZIZkkmSeZ +KpktmTCZM5k2mTmZPJk/mUKZRZlImUuZTplRmVSZV5lamV2ZYJljmWaZaZlsmW+Zcpl1mXiZe5l+mYGZ +hJmHmYqZjZmQmZOZlpmZmZyZn5mimaWZqJmrma6ZsZm0mbeZupm9mcCZw5nGmcmZzJnPmdKZ1ZnYmduZ +3pnhmeSZ55nqme2Z8JnzmfaZ+Zn8mf+aApoFmgiaC5oOmhGaFJoXmhqaHZogmiOaJpopmiyaL5oymjWa +OJo7mj6aQJpCmkWaR5pMmlGaVppZmlyaX5pkmmaaa5pwmnWaepp9moKahZqKmo+akpqVmpqanZqgmqOa +pZqqmq+asZq0mreaupq9msCaxZrKmsyaz5rRmtaa2Zrcmt+a5Jrpmuua8Jrzmvaa+Zr+mwCbA5sGmwmb +DJsOmxGbFpsZmx6bI5smmyubLpsxmzObNZs6mzybP5tCm0ebSZtOm1GbVJtXm1mbXJtem2CbY5tom2qb +bZtwm3ObeJt7m4CbhZuKm42bj5uSm5Sbl5uam52bopulm6ibqpuvm7Sbtpu4m72bv5vCm8ebzJvRm9ab +25vem+Gb5Jvpm+ub7pvwm/mb/Jv/nAScB5wKnA2cEpwXnBqcHJwfnCKcJZwqnC+cMpw0nDmcPJw/nEKc +RZxHnEqcTJxRnFScVpxZnFycYZxmnGucbpxznHace5x+nIGchpyInIucjpyQnJWcmpydnKCcpZyonKuc +rpyxnLSct5y8nL+cwZzGnMmczJzPnNGc1pzZnNyc4ZzknOec7JzxnPSc95z6nP2c/50EnQmdC50QnRWd +F50cnR+dJJ0tnS+dMJ05nTydPZ1GnUmdSp1TnVgAAAAAAAACAgAAAAAAABChAAAAAAAAAAAAAAAAAACd +Zw + + + diff --git a/Sparkle.framework/Headers b/Sparkle.framework/Headers new file mode 120000 index 0000000..a177d2a --- /dev/null +++ b/Sparkle.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/Sparkle.framework/Modules b/Sparkle.framework/Modules new file mode 120000 index 0000000..5736f31 --- /dev/null +++ b/Sparkle.framework/Modules @@ -0,0 +1 @@ +Versions/Current/Modules \ No newline at end of file diff --git a/Sparkle.framework/Resources b/Sparkle.framework/Resources new file mode 120000 index 0000000..953ee36 --- /dev/null +++ b/Sparkle.framework/Resources @@ -0,0 +1 @@ +Versions/Current/Resources \ No newline at end of file diff --git a/Sparkle.framework/Sparkle b/Sparkle.framework/Sparkle new file mode 120000 index 0000000..b2c5273 --- /dev/null +++ b/Sparkle.framework/Sparkle @@ -0,0 +1 @@ +Versions/Current/Sparkle \ No newline at end of file diff --git a/Sparkle.framework/Versions/A/Headers/SUAppcast.h b/Sparkle.framework/Versions/A/Headers/SUAppcast.h new file mode 100644 index 0000000..9b4c677 --- /dev/null +++ b/Sparkle.framework/Versions/A/Headers/SUAppcast.h @@ -0,0 +1,34 @@ +// +// SUAppcast.h +// Sparkle +// +// Created by Andy Matuschak on 3/12/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUAPPCAST_H +#define SUAPPCAST_H + +#import +#import "SUExport.h" + +@protocol SUAppcastDelegate; + +@class SUAppcastItem; +SU_EXPORT @interface SUAppcast : NSObject + +@property (weak) id delegate; +@property (copy) NSString *userAgentString; +@property (copy) NSDictionary *httpHeaders; + +- (void)fetchAppcastFromURL:(NSURL *)url; + +@property (readonly, copy) NSArray *items; +@end + +@protocol SUAppcastDelegate +- (void)appcastDidFinishLoading:(SUAppcast *)appcast; +- (void)appcast:(SUAppcast *)appcast failedToLoadWithError:(NSError *)error; +@end + +#endif diff --git a/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h b/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h new file mode 100644 index 0000000..551d389 --- /dev/null +++ b/Sparkle.framework/Versions/A/Headers/SUAppcastItem.h @@ -0,0 +1,42 @@ +// +// SUAppcastItem.h +// Sparkle +// +// Created by Andy Matuschak on 3/12/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUAPPCASTITEM_H +#define SUAPPCASTITEM_H + +#include "SUExport.h" + +SU_EXPORT @interface SUAppcastItem : NSObject +@property (copy, readonly) NSString *title; +@property (copy, readonly) NSDate *date; +@property (copy, readonly) NSString *itemDescription; +@property (strong, readonly) NSURL *releaseNotesURL; +@property (copy, readonly) NSString *DSASignature; +@property (copy, readonly) NSString *minimumSystemVersion; +@property (copy, readonly) NSString *maximumSystemVersion; +@property (strong, readonly) NSURL *fileURL; +@property (copy, readonly) NSString *versionString; +@property (copy, readonly) NSString *displayVersionString; +@property (copy, readonly) NSDictionary *deltaUpdates; +@property (strong, readonly) NSURL *infoURL; + +// Initializes with data from a dictionary provided by the RSS class. +- (instancetype)initWithDictionary:(NSDictionary *)dict; +- (instancetype)initWithDictionary:(NSDictionary *)dict failureReason:(NSString **)error; + +@property (getter=isDeltaUpdate, readonly) BOOL deltaUpdate; +@property (getter=isCriticalUpdate, readonly) BOOL criticalUpdate; + +// Returns the dictionary provided in initWithDictionary; this might be useful later for extensions. +@property (readonly, copy) NSDictionary *propertiesDictionary; + +- (NSURL *)infoURL; + +@end + +#endif diff --git a/Sparkle.framework/Versions/A/Headers/SUErrors.h b/Sparkle.framework/Versions/A/Headers/SUErrors.h new file mode 100644 index 0000000..e624bb0 --- /dev/null +++ b/Sparkle.framework/Versions/A/Headers/SUErrors.h @@ -0,0 +1,44 @@ +// +// SUErrors.h +// Sparkle +// +// Created by C.W. Betts on 10/13/14. +// Copyright (c) 2014 Sparkle Project. All rights reserved. +// + +#ifndef SUERRORS_H +#define SUERRORS_H + +#import +#import "SUExport.h" + +/** + * Error domain used by Sparkle + */ +SU_EXPORT extern NSString *const SUSparkleErrorDomain; + +typedef NS_ENUM(OSStatus, SUError) { + // Appcast phase errors. + SUAppcastParseError = 1000, + SUNoUpdateError = 1001, + SUAppcastError = 1002, + SURunningFromDiskImageError = 1003, + + // Downlaod phase errors. + SUTemporaryDirectoryError = 2000, + + // Extraction phase errors. + SUUnarchivingError = 3000, + SUSignatureError = 3001, + + // Installation phase errors. + SUFileCopyFailure = 4000, + SUAuthenticationFailure = 4001, + SUMissingUpdateError = 4002, + SUMissingInstallerToolError = 4003, + SURelaunchError = 4004, + SUInstallationError = 4005, + SUDowngradeError = 4006 +}; + +#endif diff --git a/Sparkle.framework/Versions/A/Headers/SUExport.h b/Sparkle.framework/Versions/A/Headers/SUExport.h new file mode 100644 index 0000000..3e3f8a1 --- /dev/null +++ b/Sparkle.framework/Versions/A/Headers/SUExport.h @@ -0,0 +1,18 @@ +// +// SUExport.h +// Sparkle +// +// Created by Jake Petroules on 2014-08-23. +// Copyright (c) 2014 Sparkle Project. All rights reserved. +// + +#ifndef SUEXPORT_H +#define SUEXPORT_H + +#ifdef BUILDING_SPARKLE +#define SU_EXPORT __attribute__((visibility("default"))) +#else +#define SU_EXPORT +#endif + +#endif diff --git a/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h b/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h new file mode 100644 index 0000000..73ac968 --- /dev/null +++ b/Sparkle.framework/Versions/A/Headers/SUStandardVersionComparator.h @@ -0,0 +1,37 @@ +// +// SUStandardVersionComparator.h +// Sparkle +// +// Created by Andy Matuschak on 12/21/07. +// Copyright 2007 Andy Matuschak. All rights reserved. +// + +#ifndef SUSTANDARDVERSIONCOMPARATOR_H +#define SUSTANDARDVERSIONCOMPARATOR_H + +#import "SUExport.h" +#import "SUVersionComparisonProtocol.h" + +/*! + Sparkle's default version comparator. + + This comparator is adapted from MacPAD, by Kevin Ballard. + It's "dumb" in that it does essentially string comparison, + in components split by character type. +*/ +SU_EXPORT @interface SUStandardVersionComparator : NSObject + +/*! + Returns a singleton instance of the comparator. +*/ ++ (SUStandardVersionComparator *)defaultComparator; + +/*! + Compares version strings through textual analysis. + + See the implementation for more details. +*/ +- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; +@end + +#endif diff --git a/Sparkle.framework/Versions/A/Headers/SUUpdater.h b/Sparkle.framework/Versions/A/Headers/SUUpdater.h new file mode 100644 index 0000000..90d606f --- /dev/null +++ b/Sparkle.framework/Versions/A/Headers/SUUpdater.h @@ -0,0 +1,327 @@ +// +// SUUpdater.h +// Sparkle +// +// Created by Andy Matuschak on 1/4/06. +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SUUPDATER_H +#define SUUPDATER_H + +#import "SUExport.h" +#import "SUVersionComparisonProtocol.h" +#import "SUVersionDisplayProtocol.h" + +@class SUUpdateDriver, SUAppcastItem, SUHost, SUAppcast; + +@protocol SUUpdaterDelegate; + +/*! + The main API in Sparkle for controlling the update mechanism. + + This class is used to configure the update paramters as well as manually + and automatically schedule and control checks for updates. + */ +SU_EXPORT @interface SUUpdater : NSObject + +@property (unsafe_unretained) IBOutlet id delegate; + ++ (SUUpdater *)sharedUpdater; ++ (SUUpdater *)updaterForBundle:(NSBundle *)bundle; +- (instancetype)initForBundle:(NSBundle *)bundle; + +@property (readonly, strong) NSBundle *hostBundle; + +@property BOOL automaticallyChecksForUpdates; + +@property NSTimeInterval updateCheckInterval; + +/*! + * The URL of the appcast used to download update information. + * + * This property must be called on the main thread. + */ +@property (copy) NSURL *feedURL; + +@property (nonatomic, copy) NSString *userAgentString; + +@property (copy) NSDictionary *httpHeaders; + +@property BOOL sendsSystemProfile; + +@property BOOL automaticallyDownloadsUpdates; + +/*! + Explicitly checks for updates and displays a progress dialog while doing so. + + This method is meant for a main menu item. + Connect any menu item to this action in Interface Builder, + and Sparkle will check for updates and report back its findings verbosely + when it is invoked. + */ +- (IBAction)checkForUpdates:(id)sender; + +/*! + Checks for updates, but does not display any UI unless an update is found. + + This is meant for programmatically initating a check for updates. That is, + it will display no UI unless it actually finds an update, in which case it + proceeds as usual. + + If the fully automated updating is turned on, however, this will invoke that + behavior, and if an update is found, it will be downloaded and prepped for + installation. + */ +- (void)checkForUpdatesInBackground; + +/*! + Returns the date of last update check. + + \returns \c nil if no check has been performed. + */ +@property (readonly, copy) NSDate *lastUpdateCheckDate; + +/*! + Begins a "probing" check for updates which will not actually offer to + update to that version. + + However, the delegate methods + SUUpdaterDelegate::updater:didFindValidUpdate: and + SUUpdaterDelegate::updaterDidNotFindUpdate: will be called, + so you can use that information in your UI. + */ +- (void)checkForUpdateInformation; + +/*! + Appropriately schedules or cancels the update checking timer according to + the preferences for time interval and automatic checks. + + This call does not change the date of the next check, + but only the internal NSTimer. + */ +- (void)resetUpdateCycle; + +@property (readonly) BOOL updateInProgress; + +@end + +// ----------------------------------------------------------------------------- +// SUUpdater Notifications for events that might be interesting to more than just the delegate +// The updater will be the notification object +// ----------------------------------------------------------------------------- +SU_EXPORT extern NSString *const SUUpdaterDidFinishLoadingAppCastNotification; +SU_EXPORT extern NSString *const SUUpdaterDidFindValidUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterDidNotFindUpdateNotification; +SU_EXPORT extern NSString *const SUUpdaterWillRestartNotification; +#define SUUpdaterWillRelaunchApplicationNotification SUUpdaterWillRestartNotification; +#define SUUpdaterWillInstallUpdateNotification SUUpdaterWillRestartNotification; + +// Key for the SUAppcastItem object in the SUUpdaterDidFindValidUpdateNotification userInfo +SU_EXPORT extern NSString *const SUUpdaterAppcastItemNotificationKey; +// Key for the SUAppcast object in the SUUpdaterDidFinishLoadingAppCastNotification userInfo +SU_EXPORT extern NSString *const SUUpdaterAppcastNotificationKey; + +// ----------------------------------------------------------------------------- +// SUUpdater Delegate: +// ----------------------------------------------------------------------------- + +/*! + Provides methods to control the behavior of an SUUpdater object. + */ +@protocol SUUpdaterDelegate +@optional + +/*! + Returns whether to allow Sparkle to pop up. + + For example, this may be used to prevent Sparkle from interrupting a setup assistant. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)updater; + +/*! + Returns additional parameters to append to the appcast URL's query string. + + This is potentially based on whether or not Sparkle will also be sending along the system profile. + + \param updater The SUUpdater instance. + \param sendingProfile Whether the system profile will also be sent. + + \return An array of dictionaries with keys: "key", "value", "displayKey", "displayValue", the latter two being specifically for display to the user. + */ +- (NSArray *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile; + +/*! + Returns a custom appcast URL. + + Override this to dynamically specify the entire URL. + + \param updater The SUUpdater instance. + */ +- (NSString *)feedURLStringForUpdater:(SUUpdater *)updater; + +/*! + Returns whether Sparkle should prompt the user about automatic update checks. + + Use this to override the default behavior. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SUUpdater *)updater; + +/*! + Called after Sparkle has downloaded the appcast from the remote server. + + Implement this if you want to do some special handling with the appcast once it finishes loading. + + \param updater The SUUpdater instance. + \param appcast The appcast that was downloaded from the remote server. + */ +- (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast; + +/*! + Returns the item in the appcast corresponding to the update that should be installed. + + If you're using special logic or extensions in your appcast, + implement this to use your own logic for finding a valid update, if any, + in the given appcast. + + \param appcast The appcast that was downloaded from the remote server. + \param updater The SUUpdater instance. + */ +- (SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SUUpdater *)updater; + +/*! + Called when a valid update is found by the update driver. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + */ +- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)item; + +/*! + Called when a valid update is not found. + + \param updater The SUUpdater instance. + */ +- (void)updaterDidNotFindUpdate:(SUUpdater *)updater; + +/*! + Called immediately before installing the specified update. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + */ +- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)item; + +/*! + Returns whether the relaunch should be delayed in order to perform other tasks. + + This is not called if the user didn't relaunch on the previous update, + in that case it will immediately restart. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + \param invocation The invocation that must be completed before continuing with the relaunch. + + \return \c YES to delay the relaunch until \p invocation is invoked. + */ +- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)item untilInvoking:(NSInvocation *)invocation; + +/*! + Returns whether the application should be relaunched at all. + + Some apps \b cannot be relaunched under certain circumstances. + This method can be used to explicitly prevent a relaunch. + + \param updater The SUUpdater instance. + */ +- (BOOL)updaterShouldRelaunchApplication:(SUUpdater *)updater; + +/*! + Called immediately before relaunching. + + \param updater The SUUpdater instance. + */ +- (void)updaterWillRelaunchApplication:(SUUpdater *)updater; + +/*! + Returns an object that compares version numbers to determine their arithmetic relation to each other. + + This method allows you to provide a custom version comparator. + If you don't implement this method or return \c nil, + the standard version comparator will be used. + + \sa SUStandardVersionComparator + + \param updater The SUUpdater instance. + */ +- (id)versionComparatorForUpdater:(SUUpdater *)updater; + +/*! + Returns an object that formats version numbers for display to the user. + + If you don't implement this method or return \c nil, + the standard version formatter will be used. + + \sa SUUpdateAlert + + \param updater The SUUpdater instance. + */ +- (id)versionDisplayerForUpdater:(SUUpdater *)updater; + +/*! + Returns the path which is used to relaunch the client after the update is installed. + + The default is the path of the host bundle. + + \param updater The SUUpdater instance. + */ +- (NSString *)pathToRelaunchForUpdater:(SUUpdater *)updater; + +/*! + Called before an updater shows a modal alert window, + to give the host the opportunity to hide attached windows that may get in the way. + + \param updater The SUUpdater instance. + */ +- (void)updaterWillShowModalAlert:(SUUpdater *)updater; + +/*! + Called after an updater shows a modal alert window, + to give the host the opportunity to hide attached windows that may get in the way. + + \param updater The SUUpdater instance. + */ +- (void)updaterDidShowModalAlert:(SUUpdater *)updater; + +/*! + Called when an update is scheduled to be silently installed on quit. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that is proposed to be installed. + \param invocation Can be used to trigger an immediate silent install and relaunch. + */ +- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)item immediateInstallationInvocation:(NSInvocation *)invocation; + +/*! + Calls after an update that was scheduled to be silently installed on quit has been canceled. + + \param updater The SUUpdater instance. + \param item The appcast item corresponding to the update that was proposed to be installed. + */ +- (void)updater:(SUUpdater *)updater didCancelInstallUpdateOnQuit:(SUAppcastItem *)item; + +/*! + Called after an update is aborted due to an error. + + \param updater The SUUpdater instance. + \param error The error that caused the abort + */ +- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error; + +@end + +#endif diff --git a/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h b/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h new file mode 100644 index 0000000..10c4266 --- /dev/null +++ b/Sparkle.framework/Versions/A/Headers/SUVersionComparisonProtocol.h @@ -0,0 +1,30 @@ +// +// SUVersionComparisonProtocol.h +// Sparkle +// +// Created by Andy Matuschak on 12/21/07. +// Copyright 2007 Andy Matuschak. All rights reserved. +// + +#ifndef SUVERSIONCOMPARISONPROTOCOL_H +#define SUVERSIONCOMPARISONPROTOCOL_H + +#import +#import "SUExport.h" + +/*! + Provides version comparison facilities for Sparkle. +*/ +@protocol SUVersionComparison + +/*! + An abstract method to compare two version strings. + + Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, + and NSOrderedSame if they are equivalent. +*/ +- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB; // *** MAY BE CALLED ON NON-MAIN THREAD! + +@end + +#endif diff --git a/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h b/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h new file mode 100644 index 0000000..97fae4c --- /dev/null +++ b/Sparkle.framework/Versions/A/Headers/SUVersionDisplayProtocol.h @@ -0,0 +1,25 @@ +// +// SUVersionDisplayProtocol.h +// EyeTV +// +// Created by Uli Kusterer on 08.12.09. +// Copyright 2009 Elgato Systems GmbH. All rights reserved. +// + +#import +#import "SUExport.h" + +/*! + Applies special display formatting to version numbers. +*/ +@protocol SUVersionDisplay + +/*! + Formats two version strings. + + Both versions are provided so that important distinguishing information + can be displayed while also leaving out unnecessary/confusing parts. +*/ +- (void)formatVersion:(NSString **)inOutVersionA andVersion:(NSString **)inOutVersionB; + +@end diff --git a/Sparkle.framework/Versions/A/Headers/Sparkle.h b/Sparkle.framework/Versions/A/Headers/Sparkle.h new file mode 100644 index 0000000..fe97bae --- /dev/null +++ b/Sparkle.framework/Versions/A/Headers/Sparkle.h @@ -0,0 +1,23 @@ +// +// Sparkle.h +// Sparkle +// +// Created by Andy Matuschak on 3/16/06. (Modified by CDHW on 23/12/07) +// Copyright 2006 Andy Matuschak. All rights reserved. +// + +#ifndef SPARKLE_H +#define SPARKLE_H + +// This list should include the shared headers. It doesn't matter if some of them aren't shared (unless +// there are name-space collisions) so we can list all of them to start with: + +#import +#import +#import +#import +#import +#import +#import + +#endif diff --git a/Sparkle.framework/Versions/A/Modules/module.modulemap b/Sparkle.framework/Versions/A/Modules/module.modulemap new file mode 100644 index 0000000..af3fe6d --- /dev/null +++ b/Sparkle.framework/Versions/A/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module Sparkle { + umbrella header "Sparkle.h" + + export * + module * { export * } +} diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist new file mode 100644 index 0000000..024d737 --- /dev/null +++ b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Info.plist @@ -0,0 +1,50 @@ + + + + + BuildMachineOSBuild + 14D109a + CFBundleDevelopmentRegion + English + CFBundleExecutable + Autoupdate + CFBundleIconFile + AppIcon + CFBundleIdentifier + org.sparkle-project.Sparkle.Autoupdate + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.9.0 git-e7ed4aa + CFBundleSignature + ???? + CFBundleVersion + 1.9.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 6C131e + DTPlatformVersion + GM + DTSDKBuild + 14A383 + DTSDKName + macosx10.10 + DTXcode + 0620 + DTXcodeBuild + 6C131e + LSBackgroundOnly + 1 + LSMinimumSystemVersion + 10.7 + LSUIElement + 1 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate new file mode 100755 index 0000000..6aeabd2 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/MacOS/Autoupdate differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo new file mode 100644 index 0000000..bd04210 --- /dev/null +++ b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns new file mode 100644 index 0000000..b6fa734 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/AppIcon.icns differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib new file mode 100644 index 0000000..2a63346 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/SUStatus.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings new file mode 100644 index 0000000..28c2c21 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ar.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings new file mode 100644 index 0000000..a50d6bf Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/cs.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings new file mode 100644 index 0000000..8c349bd Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/da.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings new file mode 100644 index 0000000..fe50db5 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/de.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings new file mode 100644 index 0000000..015c213 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/el.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings new file mode 100644 index 0000000..d2e5e5c Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/en.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings new file mode 100644 index 0000000..ee69407 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/es.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings new file mode 100644 index 0000000..c7b221b Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/fr.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings new file mode 100644 index 0000000..68ae6e0 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/is.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings new file mode 100644 index 0000000..99955af Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/it.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings new file mode 100644 index 0000000..faa546c Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ja.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings new file mode 100644 index 0000000..d5df79d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ko.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings new file mode 100644 index 0000000..05a426d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nb.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings new file mode 100644 index 0000000..95923ee Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/nl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings new file mode 100644 index 0000000..d10801f Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings new file mode 100644 index 0000000..0f2ef4d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_BR.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings new file mode 100644 index 0000000..69853a3 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/pt_PT.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings new file mode 100644 index 0000000..3cd694d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ro.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings new file mode 100644 index 0000000..349f17f Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/ru.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings new file mode 100644 index 0000000..38bb70d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sk.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings new file mode 100644 index 0000000..889a658 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings new file mode 100644 index 0000000..e7e9307 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/sv.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings new file mode 100644 index 0000000..323bb70 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/th.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings new file mode 100644 index 0000000..74e17e4 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/tr.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings new file mode 100644 index 0000000..bf13d96 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/uk.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings new file mode 100644 index 0000000..49b68ce Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings new file mode 100644 index 0000000..d9229c9 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/Autoupdate.app/Contents/Resources/zh_TW.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/Info.plist b/Sparkle.framework/Versions/A/Resources/Info.plist new file mode 100644 index 0000000..e980c7c --- /dev/null +++ b/Sparkle.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,48 @@ + + + + + BuildMachineOSBuild + 14D109a + CFBundleDevelopmentRegion + en + CFBundleExecutable + Sparkle + CFBundleIdentifier + org.andymatuschak.Sparkle + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Sparkle + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.9.0 git-e7ed4aa + CFBundleSignature + ???? + CFBundleVersion + 1.9.0 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 6C131e + DTPlatformVersion + GM + DTSDKBuild + 14A383 + DTSDKName + macosx10.10 + DTXcode + 0620 + DTXcodeBuild + 6C131e + SUAppendVersionNumber + 1 + SUEnableAutomatedDowngrades + 0 + SUNormalizeInstalledApplicationName + 0 + SURelaunchToolName + Autoupdate + + diff --git a/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist b/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist new file mode 100644 index 0000000..74db0ff --- /dev/null +++ b/Sparkle.framework/Versions/A/Resources/SUModelTranslation.plist @@ -0,0 +1,228 @@ + + + + + ADP2,1 + Developer Transition Kit + iMac1,1 + iMac G3 (Rev A-D) + iMac4,1 + iMac (Core Duo) + iMac4,2 + iMac for Education (17 inch, Core Duo) + iMac5,1 + iMac (Core 2 Duo, 17 or 20 inch, SuperDrive) + iMac5,2 + iMac (Core 2 Duo, 17 inch, Combo Drive) + iMac6,1 + iMac (Core 2 Duo, 24 inch, SuperDrive) + iMac8,1 + iMac (Core 2 Duo, 20 or 24 inch, Early 2008 ) + iMac9,1 + iMac (Core 2 Duo, 20 or 24 inch, Early or Mid 2009 ) + iMac10,1 + iMac (Core 2 Duo, 21.5 or 27 inch, Late 2009 ) + iMac11,1 + iMac (Core i5 or i7, 27 inch Late 2009) + iMac11,2 + iMac (Core i3 or i5, 27 inch Mid 2010) + iMac11,3 + iMac (Core i5 or i7, 27 inch Mid 2010) + iMac12,1 + iMac (Core i3 or i5 or i7, 21.5 inch Mid 2010 or Late 2011) + iMac12,2 + iMac (Core i5 or i7, 27 inch Mid 2011) + iMac13,1 + iMac (Core i3 or i5 or i7, 21.5 inch Late 2012 or Early 2013) + iMac13,2 + iMac (Core i5 or i7, 27 inch Late 2012) + iMac14,1 + iMac (Core i5, 21.5 inch Late 2013) + iMac14,2 + iMac (Core i5 or i7, 27 inch Late 2013) + iMac14,3 + iMac (Core i5 or i7, 21.5 inch Late 2013) + MacBook1,1 + MacBook (Core Duo) + MacBook2,1 + MacBook (Core 2 Duo) + MacBook4,1 + MacBook (Core 2 Duo Feb 2008) + MacBook5,1 + MacBook (Core 2 Duo, Late 2008, Unibody) + MacBook5,2 + MacBook (Core 2 Duo, Early 2009, White) + MacBook6,1 + MacBook (Core 2 Duo, Late 2009, Unibody) + MacBook7,1 + MacBook (Core 2 Duo, Mid 2010, White) + MacBookAir1,1 + MacBook Air (Core 2 Duo, 13 inch, Early 2008) + MacBookAir2,1 + MacBook Air (Core 2 Duo, 13 inch, Mid 2009) + MacBookAir3,1 + MacBook Air (Core 2 Duo, 11 inch, Late 2010) + MacBookAir3,2 + MacBook Air (Core 2 Duo, 13 inch, Late 2010) + MacBookAir4,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2011) + MacBookAir4,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2011) + MacBookAir5,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2012) + MacBookAir5,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2012) + MacBookAir6,1 + MacBook Air (Core i5 or i7, 11 inch, Mid 2013 or Early 2014) + MacBookAir6,2 + MacBook Air (Core i5 or i7, 13 inch, Mid 2013 or Early 2014) + MacBookPro1,1 + MacBook Pro Core Duo (15-inch) + MacBookPro1,2 + MacBook Pro Core Duo (17-inch) + MacBookPro2,1 + MacBook Pro Core 2 Duo (17-inch) + MacBookPro2,2 + MacBook Pro Core 2 Duo (15-inch) + MacBookPro3,1 + MacBook Pro Core 2 Duo (15-inch LED, Core 2 Duo) + MacBookPro3,2 + MacBook Pro Core 2 Duo (17-inch HD, Core 2 Duo) + MacBookPro4,1 + MacBook Pro (Core 2 Duo Feb 2008) + Macmini1,1 + Mac Mini (Core Solo/Duo) + MacPro1,1 + Mac Pro (four-core) + MacPro2,1 + Mac Pro (eight-core) + MacPro3,1 + Mac Pro (January 2008 4- or 8- core "Harpertown") + MacPro4,1 + Mac Pro (March 2009) + MacPro5,1 + Mac Pro (August 2010) + PowerBook1,1 + PowerBook G3 + PowerBook2,1 + iBook G3 + PowerBook2,2 + iBook G3 (FireWire) + PowerBook2,3 + iBook G3 + PowerBook2,4 + iBook G3 + PowerBook3,1 + PowerBook G3 (FireWire) + PowerBook3,2 + PowerBook G4 + PowerBook3,3 + PowerBook G4 (Gigabit Ethernet) + PowerBook3,4 + PowerBook G4 (DVI) + PowerBook3,5 + PowerBook G4 (1GHz / 867MHz) + PowerBook4,1 + iBook G3 (Dual USB, Late 2001) + PowerBook4,2 + iBook G3 (16MB VRAM) + PowerBook4,3 + iBook G3 Opaque 16MB VRAM, 32MB VRAM, Early 2003) + PowerBook5,1 + PowerBook G4 (17 inch) + PowerBook5,2 + PowerBook G4 (15 inch FW 800) + PowerBook5,3 + PowerBook G4 (17-inch 1.33GHz) + PowerBook5,4 + PowerBook G4 (15 inch 1.5/1.33GHz) + PowerBook5,5 + PowerBook G4 (17-inch 1.5GHz) + PowerBook5,6 + PowerBook G4 (15 inch 1.67GHz/1.5GHz) + PowerBook5,7 + PowerBook G4 (17-inch 1.67GHz) + PowerBook5,8 + PowerBook G4 (Double layer SD, 15 inch) + PowerBook5,9 + PowerBook G4 (Double layer SD, 17 inch) + PowerBook6,1 + PowerBook G4 (12 inch) + PowerBook6,2 + PowerBook G4 (12 inch, DVI) + PowerBook6,3 + iBook G4 + PowerBook6,4 + PowerBook G4 (12 inch 1.33GHz) + PowerBook6,5 + iBook G4 (Early-Late 2004) + PowerBook6,7 + iBook G4 (Mid 2005) + PowerBook6,8 + PowerBook G4 (12 inch 1.5GHz) + PowerMac1,1 + Power Macintosh G3 (Blue & White) + PowerMac1,2 + Power Macintosh G4 (PCI Graphics) + PowerMac10,1 + Mac Mini G4 + PowerMac10,2 + Mac Mini (Late 2005) + PowerMac11,2 + Power Macintosh G5 (Late 2005) + PowerMac12,1 + iMac G5 (iSight) + PowerMac2,1 + iMac G3 (Slot-loading CD-ROM) + PowerMac2,2 + iMac G3 (Summer 2000) + PowerMac3,1 + Power Macintosh G4 (AGP Graphics) + PowerMac3,2 + Power Macintosh G4 (AGP Graphics) + PowerMac3,3 + Power Macintosh G4 (Gigabit Ethernet) + PowerMac3,4 + Power Macintosh G4 (Digital Audio) + PowerMac3,5 + Power Macintosh G4 (Quick Silver) + PowerMac3,6 + Power Macintosh G4 (Mirrored Drive Door) + PowerMac4,1 + iMac G3 (Early/Summer 2001) + PowerMac4,2 + iMac G4 (Flat Panel) + PowerMac4,4 + eMac + PowerMac4,5 + iMac G4 (17-inch Flat Panel) + PowerMac5,1 + Power Macintosh G4 Cube + PowerMac6,1 + iMac G4 (USB 2.0) + PowerMac6,3 + iMac G4 (20-inch Flat Panel) + PowerMac6,4 + eMac (USB 2.0, 2005) + PowerMac7,2 + Power Macintosh G5 + PowerMac7,3 + Power Macintosh G5 + PowerMac8,1 + iMac G5 + PowerMac8,2 + iMac G5 (Ambient Light Sensor) + PowerMac9,1 + Power Macintosh G5 (Late 2005) + RackMac1,1 + Xserve G4 + RackMac1,2 + Xserve G4 (slot-loading, cluster node) + RackMac3,1 + Xserve G5 + Xserve1,1 + Xserve (Intel Xeon) + Xserve2,1 + Xserve (January 2008 quad-core) + + diff --git a/Sparkle.framework/Versions/A/Resources/SUStatus.nib b/Sparkle.framework/Versions/A/Resources/SUStatus.nib new file mode 100644 index 0000000..2a63346 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/SUStatus.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..f91cb9d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..88fe442 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..97ac81d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ar.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings new file mode 100644 index 0000000..28c2c21 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ar.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..4ff8afe Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..24e9bd1 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..84148e6 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/cs.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings new file mode 100644 index 0000000..a50d6bf Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/cs.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..3fc9d17 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/da.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..73595ca Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..2febced Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/da.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings new file mode 100644 index 0000000..8c349bd Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/da.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..b2b9554 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/de.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..aef60e5 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..9361c8c Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/de.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings new file mode 100644 index 0000000..fe50db5 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/de.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..229df3c Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/el.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..0d43c97 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..a267fe7 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/el.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings new file mode 100644 index 0000000..015c213 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/el.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..a5a019b Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..1eb0179 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..0976744 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings new file mode 100644 index 0000000..d2e5e5c Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/en.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..b2666b7 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/es.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..c45ad33 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..09bff16 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/es.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings new file mode 100644 index 0000000..ee69407 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/es.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..52eca82 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..cae35bf Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..cd61c12 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/fr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings new file mode 100644 index 0000000..c7b221b Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/fr.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/fr_CA.lproj b/Sparkle.framework/Versions/A/Resources/fr_CA.lproj new file mode 120000 index 0000000..f9834a3 --- /dev/null +++ b/Sparkle.framework/Versions/A/Resources/fr_CA.lproj @@ -0,0 +1 @@ +fr.lproj \ No newline at end of file diff --git a/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..68083c2 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/is.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..0680f4f Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..e88e4b1 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/is.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings new file mode 100644 index 0000000..68ae6e0 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/is.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..9c257be Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/it.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..d19109a Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..496f6f4 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/it.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings new file mode 100644 index 0000000..99955af Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/it.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..7e4b758 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..bbafb69 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..bddffcb Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ja.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings new file mode 100644 index 0000000..faa546c Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ja.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..4012e8c Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..5b35ce4 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..a7b44a1 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ko.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings new file mode 100644 index 0000000..d5df79d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ko.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..fe29652 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..a58e36c Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..d4918ca Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/nb.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings new file mode 100644 index 0000000..05a426d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/nb.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..67b36a8 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..9bdf54b Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..0f403fd Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/nl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings new file mode 100644 index 0000000..95923ee Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/nl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..ec1ad6d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..da545e5 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..9f2c455 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings new file mode 100644 index 0000000..d10801f Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/pt.lproj b/Sparkle.framework/Versions/A/Resources/pt.lproj new file mode 120000 index 0000000..3c1c9f6 --- /dev/null +++ b/Sparkle.framework/Versions/A/Resources/pt.lproj @@ -0,0 +1 @@ +pt_BR.lproj \ No newline at end of file diff --git a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..4eb1c1a Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..13f3374 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..71417f5 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings new file mode 100644 index 0000000..0f2ef4d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pt_BR.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..8add884 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..1b678d3 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..849ad45 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings new file mode 100644 index 0000000..69853a3 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/pt_PT.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..3200815 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..91f4335 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..3a75548 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ro.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings new file mode 100644 index 0000000..3cd694d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ro.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..f501887 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..6b224f7 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..59ff6f6 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ru.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings new file mode 100644 index 0000000..349f17f Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/ru.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..6de310e Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..3ef6fb0 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..87ad9b0 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings new file mode 100644 index 0000000..38bb70d Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sk.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..3c922a9 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..2957889 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..8109ab6 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sl.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings new file mode 100644 index 0000000..889a658 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sl.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..44caab3 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..4c803c1 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..b0522f9 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sv.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings new file mode 100644 index 0000000..e7e9307 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/sv.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..5dd71dc Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/th.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..4d87385 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..fec5659 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/th.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings new file mode 100644 index 0000000..323bb70 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/th.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..285513a Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..9339388 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..09f8528 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/tr.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings new file mode 100644 index 0000000..74e17e4 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/tr.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..3d1c58b Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..639d2c3 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..c136140 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/uk.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings new file mode 100644 index 0000000..bf13d96 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/uk.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..6533a98 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..f10d922 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..b13d675 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings new file mode 100644 index 0000000..49b68ce Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/zh_CN.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib new file mode 100644 index 0000000..3d0448f Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUAutomaticUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib new file mode 100644 index 0000000..5566620 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdateAlert.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib new file mode 100644 index 0000000..593b437 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/SUUpdatePermissionPrompt.nib differ diff --git a/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings new file mode 100644 index 0000000..d9229c9 Binary files /dev/null and b/Sparkle.framework/Versions/A/Resources/zh_TW.lproj/Sparkle.strings differ diff --git a/Sparkle.framework/Versions/A/Sparkle b/Sparkle.framework/Versions/A/Sparkle new file mode 100755 index 0000000..16d5fdb Binary files /dev/null and b/Sparkle.framework/Versions/A/Sparkle differ diff --git a/Sparkle.framework/Versions/Current b/Sparkle.framework/Versions/Current new file mode 120000 index 0000000..8c7e5a6 --- /dev/null +++ b/Sparkle.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/TPAnimationManager.h b/TPAnimationManager.h new file mode 100644 index 0000000..d0859d3 --- /dev/null +++ b/TPAnimationManager.h @@ -0,0 +1,28 @@ +// +// TPAnimationManager.h +// teleport +// +// Created by Julien Robert on 16/01/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import + +@interface NSObject (TPAnimationManagerDelegate) + +- (void)animationDidComplete; + +@end + +@interface TPAnimationManager : NSObject +{ + id _delegate; + int _step; +} + ++ (NSRect)rect:(NSRect)rect centeredAtPoint:(NSPoint)point; ++ (NSRect)rect:(NSRect)rect snappedInsideRect:(NSRect)containingRect margin:(float)margin; + ++ (void)flipAnimationFromView:(NSView*)initialView toView:(NSView*)finalView invertRotation:(BOOL)invertRotation delegate:(id)delegate; + +@end diff --git a/TPAnimationManager.m b/TPAnimationManager.m new file mode 100644 index 0000000..9d2aa02 --- /dev/null +++ b/TPAnimationManager.m @@ -0,0 +1,240 @@ +// +// TPAnimationManager.m +// teleport +// +// Created by Julien Robert on 16/01/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import "TPAnimationManager.h" + +#import + +#define FLIP_DURATION ((([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) != 0) ? 5.0 : 0.5) +#define ROTATION_ANGLE M_PI_2 + +@interface TPAnimationManager () + +- (void)startFlipAnimationFromView:(NSView*)initialView toView:(NSView*)finalView invertRotation:(BOOL)invertRotation delegate:(id)delegate; +- (void)continueFlipAnimationFromAnimation:(CAAnimation*)anim; +- (void)endFlipAnimationFromAnimation:(CAAnimation*)anim; + +@end + +@implementation TPAnimationManager + ++ (NSRect)rect:(NSRect)rect centeredAtPoint:(NSPoint)point +{ + NSRect centeredRect = rect; + centeredRect.origin.x = point.x - NSWidth(centeredRect)/2.0; + centeredRect.origin.y = point.y - NSHeight(centeredRect)/2.0; + return centeredRect; +} + ++ (NSRect)rect:(NSRect)rect snappedInsideRect:(NSRect)containingRect margin:(float)margin +{ + NSRect snappedRect = rect; + NSRect parentRect = NSInsetRect(containingRect, margin, margin); + + if(NSContainsRect(parentRect, rect)) { + return snappedRect; + } + + if(NSWidth(rect) > NSWidth(parentRect) || NSHeight(rect) > NSHeight(parentRect)) { + return snappedRect; + } + + if(NSMinX(rect) < NSMinX(parentRect)) { + snappedRect.origin.x = NSMinX(parentRect); + } + else if(NSMaxX(rect) > NSMaxX(parentRect)) { + snappedRect.origin.x = NSMaxX(parentRect) - NSWidth(rect); + } + + if(NSMinY(rect) < NSMinY(parentRect)) { + snappedRect.origin.y = NSMinY(parentRect); + } + else if(NSMaxY(rect) > NSMaxY(parentRect)) { + snappedRect.origin.y = NSMaxY(parentRect) - NSHeight(rect); + } + + return snappedRect; +} + ++ (void)flipAnimationFromView:(NSView*)initialView toView:(NSView*)finalView invertRotation:(BOOL)invertRotation delegate:(id)delegate +{ + TPAnimationManager * manager = [[TPAnimationManager alloc] init]; + [manager startFlipAnimationFromView:initialView toView:finalView invertRotation:invertRotation delegate:delegate]; +} + +- (void)startFlipAnimationFromView:(NSView*)initialView toView:(NSView*)finalView invertRotation:(BOOL)invertRotation delegate:(id)delegate +{ + _delegate = delegate; + _step = 0; + + [CATransaction begin]; + [CATransaction setValue:@YES forKey:kCATransactionDisableActions]; + + NSView * rootView = [initialView superview]; + + NSRect initialFrame = [initialView frame]; + NSRect finalFrame = [finalView frame]; + NSRect parentFrame = [TPAnimationManager rect:NSInsetRect(NSUnionRect(initialFrame, finalFrame), -5.0, -5.0) centeredAtPoint:NSMakePoint(NSMidX(initialFrame), NSMidY(initialFrame))]; + + NSView * parentView = [[NSView alloc] initWithFrame:parentFrame]; + [rootView addSubview:parentView]; + + NSRect parentBounds = [parentView bounds]; + NSPoint centerPoint = NSMakePoint(NSMidX(parentBounds), NSMidY(parentBounds)); + [initialView removeFromSuperview]; + [initialView setFrame:[TPAnimationManager rect:initialFrame centeredAtPoint:centerPoint]]; + [parentView addSubview:initialView]; + + [finalView removeFromSuperview]; + [finalView setFrame:[TPAnimationManager rect:finalFrame centeredAtPoint:centerPoint]]; + [parentView addSubview:finalView]; + + CALayer * parentLayer = [parentView layer]; + CALayer * initialLayer = [initialView layer]; + CALayer * finalLayer = [finalView layer]; + + initialLayer.anchorPoint = CGPointMake(0.5, 0.5); + initialLayer.frame = NSRectToCGRect([initialView frame]); + + finalLayer.anchorPoint = CGPointMake(0.5, 0.5); + finalLayer.frame = NSRectToCGRect([finalView frame]); + + parentLayer.anchorPoint = CGPointMake(0.5, 0.5); + parentLayer.masksToBounds = NO; + + float zDistance = 500; + CATransform3D sublayerTransform = CATransform3DIdentity; + sublayerTransform.m34 = 1. / -zDistance; + + parentLayer.sublayerTransform = sublayerTransform; + parentLayer.frame = NSRectToCGRect([parentView frame]); + + CGFloat xRatio = NSWidth(initialFrame)/NSWidth(finalFrame); + CGFloat yRatio = NSHeight(initialFrame)/NSHeight(finalFrame); + + CABasicAnimation * transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"]; + transformAnimation.duration = FLIP_DURATION/2.0; + transformAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; + + CGFloat angle = invertRotation ? -ROTATION_ANGLE : ROTATION_ANGLE; + CATransform3D finalTransform = CATransform3DMakeRotation(angle, 1.0, 0.0, 0.0); + finalTransform = CATransform3DScale(finalTransform, 0.5 + 1/(2*xRatio), 0.5 + 1/(2*yRatio), 1.0); + + transformAnimation.toValue = [NSValue valueWithCATransform3D:finalTransform]; + transformAnimation.fillMode = kCAFillModeForwards; + transformAnimation.delegate = self; + transformAnimation.removedOnCompletion = NO; + + [transformAnimation setValue:initialView forKey:@"initialView"]; + [transformAnimation setValue:finalView forKey:@"finalView"]; + + [transformAnimation setValue:@(xRatio) forKey:@"xRatio"]; + [transformAnimation setValue:@(yRatio) forKey:@"yRatio"]; + [transformAnimation setValue:@(invertRotation) forKey:@"invertRotation"]; + [transformAnimation setValue:[NSValue valueWithRect:finalFrame] forKey:@"finalFrame"]; + + [initialLayer addAnimation:transformAnimation forKey:@"transform"]; + + CABasicAnimation * translateAnimation = [CABasicAnimation animationWithKeyPath:@"transform"]; + translateAnimation.duration = FLIP_DURATION; + translateAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + + CATransform3D finalTranslate = CATransform3DMakeTranslation(NSMidX(finalFrame) - NSMidX(initialFrame), NSMidY(finalFrame) - NSMidY(initialFrame), 0.0); + + translateAnimation.toValue = [NSValue valueWithCATransform3D:finalTranslate]; + translateAnimation.fillMode = kCAFillModeForwards; + translateAnimation.removedOnCompletion = NO; + + [parentLayer addAnimation:translateAnimation forKey:@"translate"]; + + [CATransaction commit]; +} + +- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag +{ + if(_step == 0) { + _step++; + [self continueFlipAnimationFromAnimation:anim]; + } + else { + [self endFlipAnimationFromAnimation:anim]; + } +} + +- (void)continueFlipAnimationFromAnimation:(CAAnimation*)anim +{ + [CATransaction begin]; + [CATransaction setValue:@YES forKey:kCATransactionDisableActions]; + + NSView * initialView = [anim valueForKey:@"initialView"]; + NSView * finalView = [anim valueForKey:@"finalView"]; + BOOL invertRotation = [[anim valueForKey:@"invertRotation"] boolValue]; + + CALayer * finalLayer = [finalView layer]; + + [initialView setHidden:YES]; + [finalView setHidden:NO]; + + CGFloat xRatio = [[anim valueForKey:@"xRatio"] doubleValue]; + CGFloat yRatio = [[anim valueForKey:@"yRatio"] doubleValue]; + + CABasicAnimation * transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"]; + transformAnimation.duration = anim.duration; + transformAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; + + CGFloat angle = invertRotation ? ROTATION_ANGLE : -ROTATION_ANGLE; + CATransform3D initialTransform = CATransform3DMakeRotation(angle, 1.0, 0.0, 0.0); + + initialTransform = CATransform3DScale(initialTransform, 0.5 + xRatio/2.0, 0.5 + yRatio/2.0, 1.0); + + CATransform3D finalTransform = CATransform3DMakeRotation(0.0, 1.0, 0.0, 0.0); + + transformAnimation.fromValue = [NSValue valueWithCATransform3D:initialTransform]; + transformAnimation.toValue = [NSValue valueWithCATransform3D:finalTransform]; + transformAnimation.fillMode = kCAFillModeForwards; + + transformAnimation.removedOnCompletion = YES; + + transformAnimation.delegate = self; + + [transformAnimation setValue:initialView forKey:@"initialView"]; + [transformAnimation setValue:finalView forKey:@"finalView"]; + [transformAnimation setValue:[anim valueForKey:@"finalFrame"] forKey:@"finalFrame"]; + + + [finalLayer addAnimation:transformAnimation forKey:@"transform"]; + + [CATransaction commit]; +} + +- (void)endFlipAnimationFromAnimation:(CAAnimation*)anim +{ + NSView * initialView = [anim valueForKey:@"initialView"]; + NSView * finalView = [anim valueForKey:@"finalView"]; + NSView * parentView = [initialView superview]; + NSView * rootView = [parentView superview]; + NSRect finalFrame = [[anim valueForKey:@"finalFrame"] rectValue]; + + NSRect frame = [parentView convertRect:[initialView frame] toView:rootView]; + [initialView removeFromSuperview]; + [initialView setFrame:frame]; + [rootView addSubview:initialView]; + + [finalView removeFromSuperview]; + [finalView setFrame:finalFrame]; + [rootView addSubview:finalView]; + + [parentView removeFromSuperview]; + + if(_delegate && [_delegate respondsToSelector:@selector(animationDidComplete)]) { + [_delegate animationDidComplete]; + } + +} + +@end diff --git a/TPApplication.h b/TPApplication.h new file mode 100644 index 0000000..e68b072 --- /dev/null +++ b/TPApplication.h @@ -0,0 +1,18 @@ +/* TPApplication */ + +#import + +@protocol TPEventDelegate + +- (BOOL)applicationWillSendEvent:(NSEvent*)event; + +@end + +@interface TPApplication : NSApplication +{ + id _eventsDelegate; +} + +- (void)setEventDelegate:(id)eventDelegate; + +@end diff --git a/TPApplication.m b/TPApplication.m new file mode 100644 index 0000000..6023744 --- /dev/null +++ b/TPApplication.m @@ -0,0 +1,25 @@ +#import "TPApplication.h" + +@implementation TPApplication + +- (void)setEventDelegate:(id)eventDelegate +{ + _eventsDelegate = eventDelegate; +} + +- (void)sendEvent:(NSEvent *)event +{ + BOOL sendToSuper = YES; + +// DebugLog(@"sendEvent: %@ (%d)", event, [event type]); + + if(_eventsDelegate != nil && [_eventsDelegate respondsToSelector:@selector(applicationWillSendEvent:)]) { + if(![_eventsDelegate applicationWillSendEvent:event]) + sendToSuper = NO; + } + + if(sendToSuper) + [super sendEvent:event]; +} + +@end diff --git a/TPAuthenticationManager.h b/TPAuthenticationManager.h new file mode 100644 index 0000000..c129715 --- /dev/null +++ b/TPAuthenticationManager.h @@ -0,0 +1,38 @@ +// +// TPAuthenticationManager.h +// teleport +// +// Created by JuL on Thu Mar 04 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPAuthenticationRequest.h" + +@class TPRemoteHost, TPNetworkConnection; + +@interface TPAuthenticationManager : NSObject +{ + NSMutableArray * _trustedHosts; + TPNetworkConnection * _currentConnection; +} + ++ (TPAuthenticationManager*)defaultManager; + +/* Persistence */ +- (void)loadHosts; +- (void)saveHosts; + +/* Authentication requests - client side */ +- (void)requestAuthenticationOnHost:(TPRemoteHost*)host; +- (void)abortAuthenticationRequest; + +/* Authentication answer - server side */ +- (void)authenticationRequestedFromHost:(TPRemoteHost*)host onConnection:(TPNetworkConnection*)connection; +- (void)replyToAuthenticationRequest:(TPAuthenticationRequest*)authRequest withResult:(TPAuthenticationResult)result; + +/* Trusted hosts */ +@property (nonatomic, readonly, copy) NSArray *trustedHosts; +- (void)host:(TPRemoteHost*)remoteHost setTrusted:(BOOL)trusted; +- (BOOL)isHostTrusted:(TPRemoteHost*)remoteHost; + +@end diff --git a/TPAuthenticationManager.m b/TPAuthenticationManager.m new file mode 100644 index 0000000..7ae0a3b --- /dev/null +++ b/TPAuthenticationManager.m @@ -0,0 +1,320 @@ +// +// TPAuthenticationManager.m +// teleport +// +// Created by JuL on Thu Mar 04 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPAuthenticationManager.h" +#import "TPPreferencesManager.h" +#import "TPHostsManager.h" +#import "TPAuthenticationRequest.h" +#import "TPMainController.h" +#import "TPLocalHost.h" +#import "TPRemoteHost.h" +#import "TPConnectionsManager.h" +#import "TPTransfersManager.h" +#import "TPBackgroundImageTransfer.h" +#import "TPTCPSecureSocket.h" +#import "TPMessage.h" + +#define TRUSTED_HOSTS_VERSION 2 + +NSString * TPTrustedHostsVersionKey = @"TPTrustedHostsVersion"; +NSString * TPTrustedHostsKey = @"TPTrustedHosts"; + +static TPAuthenticationManager * _defaultManager = nil; + +static BOOL TPCertificateEqual(SecCertificateRef cert1Ref, SecCertificateRef cert2Ref) +{ + CSSM_DATA certData1, certData2; + + SecCertificateGetData(cert1Ref, &certData1); + SecCertificateGetData(cert2Ref, &certData2); + + if(certData1.Length != certData2.Length) + return NO; + + if(memcmp(certData1.Data, certData2.Data, certData1.Length) == 0) + return YES; + else + return NO; +} + + +@implementation TPAuthenticationManager + ++ (TPAuthenticationManager*)defaultManager +{ + if(_defaultManager == nil) + _defaultManager = [[TPAuthenticationManager alloc] init]; + return _defaultManager; +} + +- (instancetype)init +{ + self = [super init]; + + _trustedHosts = [[NSMutableArray alloc] init]; + + return self; +} + + + +#pragma mark - +#pragma mark Loading/Saving + +- (void)loadHosts +{ + if([[TPPreferencesManager sharedPreferencesManager] intForPref:TPTrustedHostsVersionKey] != TRUSTED_HOSTS_VERSION) // do not load hosts from an earlier version + return; + + NSData * archivedData = [[TPPreferencesManager sharedPreferencesManager] valueForPref:TPTrustedHostsKey]; + if(archivedData == nil) + return; + + NSArray * trustedHosts = nil; + + @try { + trustedHosts = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData]; + } + @catch(NSException * e) { + trustedHosts = nil; + } + + if(trustedHosts != nil) + [_trustedHosts addObjectsFromArray:trustedHosts]; +} + +- (void)saveHosts +{ + NSData * archivedData = [NSKeyedArchiver archivedDataWithRootObject:_trustedHosts]; + [[TPPreferencesManager sharedPreferencesManager] setValue:archivedData forKey:TPTrustedHostsKey]; + [[TPPreferencesManager sharedPreferencesManager] setValue:@TRUSTED_HOSTS_VERSION forKey:TPTrustedHostsVersionKey]; +} + + +#pragma mark - +#pragma mark Authentication requests - client side + +- (void)requestAuthenticationOnHost:(TPRemoteHost*)host +{ + [[TPConnectionsManager manager] connectToHost:host withDelegate:self infoDict:nil]; +} + +- (void)connectionToServerSucceeded:(TPNetworkConnection*)connection infoDict:(NSDictionary*)infoDict +{ + _currentConnection = connection; + [_currentConnection setDelegate:self]; + [_currentConnection sendMessage:[TPMessage messageWithType:TPAuthenticationRequestMsgType]]; +} + +- (void)connectionToServerFailed:(TPRemoteHost*)host infoDict:(NSDictionary*)infoDict +{ + [host setHostState:TPHostSharedState]; + + NSString * msgTitle = [NSString stringWithFormat:NSLocalizedString(@"Connection to \\U201C%@\\U201D failed.", @"Title for connection failure"), [host computerName]]; + NSAlert * alert = [NSAlert alertWithMessageText:msgTitle defaultButton:NSLocalizedString(@"OK", nil) alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(@"The server may be down, or an encryption problem may have occured. If encryption is enabled, please check that the certificate algorithms match.", nil)]; + [(TPMainController*)[NSApp delegate] performSelector:@selector(presentAlert:) withObject:alert afterDelay:0]; +} + +- (void)connection:(TPNetworkConnection*)connection receivedMessage:(TPMessage*)message +{ + TPMsgType type = [message msgType]; + TPRemoteHost * host = [connection connectedHost]; + +#if DEBUG_GENERAL + DebugLog(@"authentication manager receive msg %ld", type); +#endif + switch(type) { + case TPAuthenticationInProgressMsgType: + { + break; + } + case TPAuthenticationSuccessMsgType: + { + [host setHostState:TPHostPeeredOnlineState]; + _currentConnection = nil; + break; + } + case TPAuthenticationFailureMsgType: + { + NSString * msgTitle = [NSString stringWithFormat:NSLocalizedString(@"The host \\U201C%@\\U201D rejected your trust request.", @"Title for trust failure"), [host computerName]]; + NSString * explanation = [message infoDict][@"reason"]; + if(explanation == nil) + explanation = NSLocalizedString(@"The computer you tried to control rejected your request. You should try on a computer you own.", nil); + + + [host setHostState:TPHostSharedState]; + + NSAlert * alert = [NSAlert alertWithMessageText:msgTitle defaultButton:NSLocalizedString(@"OK", nil) alternateButton:nil otherButton:nil informativeTextWithFormat:@"%@", explanation]; + [(TPMainController*)[NSApp delegate] presentAlert:alert]; + + _currentConnection = nil; + break; + } + case TPTransferRequestMsgType: + [[TPTransfersManager manager] receiveTransferRequestWithInfoDict:[message infoDict] onConnection:connection isClient:NO]; + break; + case TPTransferSuccessMsgType: + [[TPTransfersManager manager] startTransferWithUID:[message infoDict][TPTransferUIDKey] usingConnection:connection onPort:[[message infoDict][TPTransferPortKey] intValue]]; + break; + case TPTransferFailureMsgType: + [[TPTransfersManager manager] abortTransferWithUID:[message string]]; + break; + default: + NSLog(@"invalid msg: %ld", type); + } +} + +- (void)abortAuthenticationRequest +{ + if(_currentConnection != nil) + [_currentConnection sendMessage:[TPMessage messageWithType:TPAuthenticationAbortMsgType]]; +} + + +#pragma mark - +#pragma mark Authentication requests - server side + +- (void)authenticationRequestedFromHost:(TPRemoteHost*)host onConnection:(TPNetworkConnection*)connection +{ + if([self isHostTrusted:host]) { + if([[TPLocalHost localHost] hasCustomBackgroundImage]) + [[TPTransfersManager manager] beginTransfer:[TPOutgoingBackgroundImageTransfer transfer] usingConnection:connection]; + [connection sendMessage:[TPMessage messageWithType:TPAuthenticationSuccessMsgType]]; + } + else { + if([[TPPreferencesManager sharedPreferencesManager] boolForPref:TRUST_LOCAL_CERTIFICATE]) { + SecCertificateRef remoteHostCertRef = [host certificate]; + if(remoteHostCertRef != NULL) { + SecIdentityRef identityRef = [[TPLocalHost localHost] identity]; + SecCertificateRef localHostCertRef = NULL; + if(SecIdentityCopyCertificate(identityRef, &localHostCertRef) == noErr) { + if(localHostCertRef != NULL) { + if(TPCertificateEqual(remoteHostCertRef, localHostCertRef)) { + [self host:host setTrusted:YES]; + if([[TPLocalHost localHost] hasCustomBackgroundImage]) + [[TPTransfersManager manager] beginTransfer:[TPOutgoingBackgroundImageTransfer transfer] usingConnection:connection]; + [connection sendMessage:[TPMessage messageWithType:TPAuthenticationSuccessMsgType]]; + return; + } + CFRelease(localHostCertRef); + } + } + } + } + + int trustRequestBehavior = [[TPPreferencesManager sharedPreferencesManager] intForPref:TRUST_REQUEST_BEHAVIOR]; + + switch(trustRequestBehavior) { + case TRUST_REQUEST_REJECT: + [connection sendMessage:[TPMessage messageWithType:TPAuthenticationFailureMsgType + andInfoDict:@{@"reason": NSLocalizedString(@"Host not accepting any more trusted hosts.", @"Reason for trust failure")}]]; + DebugLog(@"Rejecting authentication: no more authentication"); + break; + + case TRUST_REQUEST_ACCEPT: + [self host:host setTrusted:YES]; + if([[TPLocalHost localHost] hasCustomBackgroundImage]) + [[TPTransfersManager manager] beginTransfer:[TPOutgoingBackgroundImageTransfer transfer] usingConnection:connection]; + [connection sendMessage:[TPMessage messageWithType:TPAuthenticationSuccessMsgType]]; + break; + + case TRUST_REQUEST_ASK: + default: + { + [connection sendMessage:[TPMessage messageWithType:TPAuthenticationInProgressMsgType]]; + + TPAuthenticationRequest * authenticationRequest = [[TPAuthenticationRequest alloc] initWithNetworkConnection:connection demandingHost:host]; + + TPAuthenticationResult result = [authenticationRequest ask]; + [self replyToAuthenticationRequest:authenticationRequest withResult:result]; + + [connection setDelegate:self]; + break; + } + } + } +} + +- (void)replyToAuthenticationRequest:(TPAuthenticationRequest*)authRequest withResult:(TPAuthenticationResult)result +{ + switch(result) { + case TPAuthenticationAcceptedAndRejectOthersResult: + [[TPPreferencesManager sharedPreferencesManager] setValue:@(TRUST_REQUEST_REJECT) forKey:TRUST_REQUEST_BEHAVIOR]; + // no break + case TPAuthenticationAcceptedResult: + { + TPRemoteHost * host = [authRequest demandingHost]; + DebugLog(@"authentication request from host %@ accepted", host); + + [self host:host setTrusted:YES]; + + TPNetworkConnection * connection = [authRequest connection]; + if([[TPLocalHost localHost] hasCustomBackgroundImage]) + [[TPTransfersManager manager] beginTransfer:[TPOutgoingBackgroundImageTransfer transfer] usingConnection:connection]; + [connection sendMessage:[TPMessage messageWithType:TPAuthenticationSuccessMsgType]]; + break; + } + case TPAuthenticationRejectedResult: + [[authRequest connection] sendMessage:[TPMessage messageWithType:TPAuthenticationFailureMsgType + andInfoDict:@{@"reason": NSLocalizedString(@"User rejected your trust request.", @"Reason for trust failure")}]]; + DebugLog(@"Rejecting authentication: user decision"); + break; + case TPAuthenticationAbortedResult: + DebugLog(@"Authentication aborted"); + } +} + + +#pragma mark - +#pragma mark Trusted hosts + +- (NSArray*)trustedHosts +{ + return _trustedHosts; +} + +- (void)host:(TPRemoteHost*)remoteHost setTrusted:(BOOL)trusted +{ + [_trustedHosts removeObject:remoteHost]; + + if(trusted) + [_trustedHosts addObject:remoteHost]; + + [self saveHosts]; +} + +- (BOOL)isHostTrusted:(TPRemoteHost*)remoteHost +{ + BOOL trusted = NO; +#if LEGACY_BUILD + unsigned index; +#else + NSUInteger index; +#endif + index = [_trustedHosts indexOfObject:remoteHost]; + if(index != NSNotFound) { + TPRemoteHost * trustedRemoteHost = _trustedHosts[index]; + SecCertificateRef trustedCertRef = [trustedRemoteHost certificate]; + if(trustedCertRef == NULL) { + trusted = YES; // trusting a non certified host + } + else { + SecCertificateRef certRef = [remoteHost certificate]; + if(certRef != NULL) { + trusted = TPCertificateEqual(trustedCertRef, certRef); + } + } + } + + if(trusted) + [self host:remoteHost setTrusted:YES]; + + return trusted; +} + +@end diff --git a/TPAuthenticationRequest.h b/TPAuthenticationRequest.h new file mode 100644 index 0000000..e34583c --- /dev/null +++ b/TPAuthenticationRequest.h @@ -0,0 +1,32 @@ +// +// TPAuthenticationRequest.h +// teleport +// +// Created by JuL on 28/02/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +typedef NS_ENUM(NSInteger, TPAuthenticationResult) { + TPAuthenticationAcceptedResult, + TPAuthenticationRejectedResult, + TPAuthenticationAcceptedAndRejectOthersResult, + TPAuthenticationAbortedResult + +} ; + +@class TPRemoteHost, TPNetworkConnection; + +@interface TPAuthenticationRequest : NSObject +{ + TPNetworkConnection * _connection; + TPRemoteHost * _demandingHost; +} + +- (instancetype) initWithNetworkConnection:(TPNetworkConnection*)connection demandingHost:(TPRemoteHost*)demandingHost NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readonly) TPAuthenticationResult ask; + +@property (nonatomic, readonly, strong) TPNetworkConnection *connection; +@property (nonatomic, readonly, strong) TPRemoteHost *demandingHost; + +@end diff --git a/TPAuthenticationRequest.m b/TPAuthenticationRequest.m new file mode 100644 index 0000000..08f7569 --- /dev/null +++ b/TPAuthenticationRequest.m @@ -0,0 +1,97 @@ +// +// TPAuthenticationRequest.m +// teleport +// +// Created by JuL on 28/02/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import "TPAuthenticationRequest.h" + +#import "TPMessage.h" +#import "TPLocalHost.h" +#import "TPRemoteHost.h" +#import "TPNetworkConnection.h" +#import "TPMainController.h" + +@implementation TPAuthenticationRequest + +- (instancetype) initWithNetworkConnection:(TPNetworkConnection*)connection demandingHost:(TPRemoteHost*)demandingHost +{ + self = [super init]; + + _connection = connection; + _demandingHost = demandingHost; + + [_connection setDelegate:self]; + + return self; +} + + +- (TPAuthenticationResult)ask +{ + NSString * msgString; + NSString * textString; + + if([[TPLocalHost localHost] pairWithHost:_demandingHost hasCapability:TPHostEncryptionCapability]) { + msgString = [NSString stringWithFormat:NSLocalizedString(@"Trust request from certified host \\U201C%@\\U201D.", @"teleport trust request title - certified version"), [_demandingHost computerName]]; + textString = NSLocalizedString(@"This will allow the demanding host to take control of your mouse and keyword. All keystrokes and transfers will be encrypted with this host.\nIf you didn't request the control, you should Reject it.", nil); + } + else { + msgString = [NSString stringWithFormat:NSLocalizedString(@"Trust request from uncertified host \\U201C%@\\U201D.", @"teleport trust request title - uncertified version"), [_demandingHost computerName]]; + textString = NSLocalizedString(@"This will allow the demanding host to take control of your mouse and keyword. Warning: the keystrokes and transfers will be sent clear on your network. You should enable encryption on both Macs to encrypt these.\nIf you didn't request the control, you should Reject it.", nil); + } + + NSAlert * authenticationAlert; + authenticationAlert = [NSAlert alertWithMessageText:msgString + defaultButton:NSLocalizedString(@"Accept", nil) + alternateButton:NSLocalizedString(@"Reject", nil) + otherButton:NSLocalizedString(@"Accept and Reject Others", nil) + informativeTextWithFormat:@"%@", textString]; + + int result = [(TPMainController*)[NSApp delegate] presentAlert:authenticationAlert]; + + switch(result) { + case NSAlertDefaultReturn: + return TPAuthenticationAcceptedResult; + case NSAlertAlternateReturn: + return TPAuthenticationRejectedResult; + case NSAlertOtherReturn: + return TPAuthenticationAcceptedAndRejectOthersResult; + case NSRunAbortedResponse: + return TPAuthenticationAbortedResult; + } + + return TPAuthenticationRejectedResult; +} + +- (void)connection:(TPNetworkConnection*)connection receivedMessage:(TPMessage*)message +{ + TPMsgType type = [message msgType]; + + switch(type) { + case TPAuthenticationAbortMsgType: + [NSApp abortModal]; + break; + default: + break; + } +} + +- (void)connectionDisconnected:(TPNetworkConnection*)connection +{ + [NSApp abortModal]; +} + +- (TPNetworkConnection*)connection +{ + return _connection; +} + +- (TPRemoteHost*)demandingHost +{ + return _demandingHost; +} + +@end diff --git a/TPBackgroundImageTransfer.h b/TPBackgroundImageTransfer.h new file mode 100644 index 0000000..55cbd38 --- /dev/null +++ b/TPBackgroundImageTransfer.h @@ -0,0 +1,17 @@ +// +// TPBackgroundImageTransfer.h +// teleport +// +// Created by JuL on 10/05/06. +// Copyright 2006 abyssoft. All rights reserved. +// + +#import + +#import "TPTransfer.h" + +@interface TPOutgoingBackgroundImageTransfer : TPOutgoingTransfer +@end + +@interface TPIncomingBackgroundImageTransfer : TPIncomingTransfer +@end diff --git a/TPBackgroundImageTransfer.m b/TPBackgroundImageTransfer.m new file mode 100644 index 0000000..0a8dcda --- /dev/null +++ b/TPBackgroundImageTransfer.m @@ -0,0 +1,70 @@ +// +// TPBackgroundImageTransfer.m +// teleport +// +// Created by JuL on 10/05/06. +// Copyright 2006 abyssoft. All rights reserved. +// + +#import "TPBackgroundImageTransfer.h" +#import "TPTransfer_Private.h" + +#import "TPHostsManager.h" +#import "TPLocalHost.h" +#import "TPRemoteHost.h" + +NSString * TPBackgroundImageTransferIdentifierKey = @"TPBackgroundImageTransferIdentifier"; +NSString * TPBackgroundImageTransferDataKey = @"TPBackgroundImageTransferData"; + +#define ENABLE_BACKGROUND_IMAGE_TRANSFER 1 + +@implementation TPOutgoingBackgroundImageTransfer + +- (NSString*)type +{ + return @"TPIncomingBackgroundImageTransfer"; +} + +- (NSData*)dataToTransfer +{ +#if ENABLE_BACKGROUND_IMAGE_TRANSFER + NSDictionary * dict = @{TPBackgroundImageTransferIdentifierKey: [[TPLocalHost localHost] identifier], + TPBackgroundImageTransferDataKey: [[TPLocalHost localHost] backgroundImageData]}; + return [NSKeyedArchiver archivedDataWithRootObject:dict]; +#else + return nil; +#endif +} + +@end + +@implementation TPIncomingBackgroundImageTransfer + +- (void)_receiverDataTransferCompleted +{ + DebugLog(@"_receiverDataTransferCompleted %@ on thread %@", self, [NSThread currentThread]); + + NSMutableDictionary * dict = nil; + + @try { + dict = [NSKeyedUnarchiver unarchiveObjectWithData:_data]; + } + @catch(NSException *e) { + DebugLog(@"exception when unarchiving dictionary: %@", e); + } + + if(dict != nil) { + NSString * identifier = dict[TPBackgroundImageTransferIdentifierKey]; + NSData * backgroundImageData = dict[TPBackgroundImageTransferDataKey]; + TPRemoteHost * host = [[TPHostsManager defaultManager] hostWithIdentifier:identifier]; + + if(host != nil && backgroundImageData != nil) { + NSImage * backgroundImage = [[NSImage alloc] initWithData:backgroundImageData]; + [host setBackgroundImage:backgroundImage]; + } + } + + [super _receiverDataTransferCompleted]; +} + +@end diff --git a/TPBezelController.h b/TPBezelController.h new file mode 100644 index 0000000..f007626 --- /dev/null +++ b/TPBezelController.h @@ -0,0 +1,28 @@ +// +// TPBezelController.h +// teleport +// +// Created by JuL on 27/07/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import + +@class TPBezelWindow, TPBezelView; +@class TPRemoteHost; + +@interface TPBezelController : NSObject +{ + TPBezelWindow * _bezelWindow; + TPBezelView * _bezelView; + NSTimer * _textTimer; +} + ++ (TPBezelController*)defaultController; + +- (void)showBezelWithControlledHost:(TPRemoteHost*)host; +- (void)hideBezel; + +- (void)showText:(NSString*)text withDuration:(NSTimeInterval)duration; + +@end diff --git a/TPBezelController.m b/TPBezelController.m new file mode 100644 index 0000000..6f0a795 --- /dev/null +++ b/TPBezelController.m @@ -0,0 +1,129 @@ +// +// TPBezelController.m +// teleport +// +// Created by JuL on 27/07/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import "TPBezelController.h" + +#import "TPBezelWindow.h" +#import "TPBezelView.h" + +#import "TPTransfersManager.h" +#import "TPTransfer.h" + +#import "TPRemoteHost.h" + +#define TEXT_DURATION 5.0 + +static TPBezelController * _defaultController = nil; + +@implementation TPBezelController + ++ (TPBezelController*)defaultController +{ + if(_defaultController == nil) + _defaultController = [[TPBezelController alloc] init]; + return _defaultController; +} + +- (instancetype) init +{ + self = [super init]; + + _bezelWindow = nil; + _bezelView = nil; + _textTimer = nil; + + return self; +} + + +- (void)showBezelWithControlledHost:(TPRemoteHost*)host +{ + NSRect localFrame = [[host localScreen] frame]; + + if(_bezelWindow == nil) { + _bezelWindow = [[TPBezelWindow alloc] initWithContentRect:localFrame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + + _bezelView = [[TPBezelView alloc] init]; + [_bezelWindow setContentView:_bezelView]; + } + + [_bezelView setRemoteHost:host]; + [_bezelView setShowProgress:NO]; + [_bezelWindow orderFront:nil]; + [_bezelWindow orderFrontRegardless]; + + if([_bezelWindow respondsToSelector:@selector(setCollectionBehavior:)]) { + [_bezelWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorStationary]; + } +} + +- (void)hideBezel +{ + [_bezelWindow orderOut:self]; + if(_textTimer != nil) { + [_textTimer invalidate]; + _textTimer = nil; + } + [_bezelView setText:nil]; +} + +- (void)showText:(NSString*)text withDuration:(NSTimeInterval)duration +{ + [_bezelView setText:text]; + if(_textTimer != nil) + [_textTimer invalidate]; + _textTimer = [NSTimer scheduledTimerWithTimeInterval:duration target:self selector:@selector(_removeText:) userInfo:nil repeats:NO]; +} + +- (void)_removeText:(NSTimer*)timer +{ + [_bezelView setText:nil]; + _textTimer = nil; +} + + +#pragma mark - +#pragma mark TPTransfersManager delegate + +- (void)transfersManager:(TPTransfersManager*)transfersManager beginNewTransfer:(TPTransfer*)transfer +{ + if(![transfer isIncoming]) { + [_bezelView setShowProgress:YES]; + [_bezelView setProgress:0.0]; + } +} + +- (void)transfersManager:(TPTransfersManager*)transfersManager transfer:(TPTransfer*)transfer didProgress:(float)progress +{ + if(![transfer isIncoming]) { + [_bezelView setShowProgress:YES]; + [_bezelView setProgress:progress]; + } +} + +- (void)transfersManager:(TPTransfersManager*)transfersManager completeTransfer:(TPTransfer*)transfer +{ + if(![transfer isIncoming]) { + [_bezelView setShowProgress:NO]; + NSString * message = [transfer completionMessage]; + if(message != nil) + [self showText:message withDuration:TEXT_DURATION]; + } +} + +- (void)transfersManager:(TPTransfersManager*)transfersManager cancelTransfer:(TPTransfer*)transfer +{ + if(![transfer isIncoming]) { + [_bezelView setShowProgress:NO]; + NSString * message = [transfer errorMessage]; + if(message != nil) + [self showText:message withDuration:TEXT_DURATION]; + } +} + +@end diff --git a/TPBezelView.h b/TPBezelView.h new file mode 100644 index 0000000..f4e286a --- /dev/null +++ b/TPBezelView.h @@ -0,0 +1,28 @@ +// +// TPBezelView.h +// Teleport +// +// Created by JuL on Fri Dec 12 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +@class TPRemoteHost; + +@interface TPBezelView : NSView +{ + TPRemoteHost * _remoteHost; + NSString * _text; + BOOL _showProgress; + float _progress; + NSTimer * _indeterminateTimer; + int _indeterminateDelta; +} + +- (void)setRemoteHost:(TPRemoteHost*)remoteHost; +- (void)setText:(NSString*)text; +- (void)setShowProgress:(BOOL)showProgress; +- (void)setProgress:(float)progress; + +@end diff --git a/TPBezelView.m b/TPBezelView.m new file mode 100644 index 0000000..d6c5c01 --- /dev/null +++ b/TPBezelView.m @@ -0,0 +1,315 @@ +// +// TPBezelView.m +// Teleport +// +// Created by JuL on Fri Dec 12 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPBezelView.h" +#import "TPBezierPath.h" +#import "TPRemoteHost.h" +#import "TPLocalHost.h" +#import "TPPreferencesManager.h" + +#define Y_TITLE_MARGIN 64.0 +#define X_TITLE_MARGIN 16.0 +#define Y_TEXT_MARGIN 32.0 +#define Y_ICON_ADJUSTMENT 24.0 +#define MIN_ROUNDED_RECT_SIZE 214.0 +#define ROUNDED_RECT_RADIUS 24.0 +#define ARROW_SIZE 14.0 +#define ARROW_MARGIN (ARROW_SIZE/2.0 + 8.0) +#define WINDOW_TRANSPARENCY 0.15 + +#define PROGRESSBAR_MARGIN 14.0 +#define PROGRESSBAR_INTERNAL_MARGIN 2.0 +#define PROGRESSBAR_HEIGHT 10.0 + +static NSImage * _teleportImage = nil; +static NSImage * _lockImage = nil; +static NSDictionary * _titleAttributes = nil; +static NSDictionary * _textAttributes = nil; + +@implementation TPBezelView + ++ (void)initialize +{ + if(self == [TPBezelView class]) { + _teleportImage = [NSImage imageNamed:@"whitelogo"]; + _lockImage = [NSImage imageNamed:@"Lock_White"]; + + NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + [paragraphStyle setAlignment:NSCenterTextAlignment]; + _titleAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSFont fontWithName:@"Arial Black" size:18], NSFontAttributeName, + [NSColor whiteColor], NSForegroundColorAttributeName, + paragraphStyle, NSParagraphStyleAttributeName, + nil]; + _textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSFont fontWithName:@"Arial Black" size:13], NSFontAttributeName, + [NSColor whiteColor], NSForegroundColorAttributeName, + paragraphStyle, NSParagraphStyleAttributeName, + nil]; + } +} + +- (instancetype) init +{ + self = [super init]; + + _text = nil; + _remoteHost = nil; + + return self; +} + +- (NSString*)_title +{ + return [_remoteHost computerName]; +} + +- (void)_update +{ + NSSize titleSize = [[self _title] sizeWithAttributes:_titleAttributes]; + + float width = round(MAX(MIN_ROUNDED_RECT_SIZE, titleSize.width + 2*X_TITLE_MARGIN)); + + NSWindow * window = [self window]; + + NSRect frame = [[_remoteHost localScreen] frame]; + + frame.origin.x = NSMinX(frame) + round((NSWidth(frame) - width)/2.0); + frame.origin.y = NSMinY(frame) + round((NSHeight(frame) - MIN_ROUNDED_RECT_SIZE)/2.0); + frame.size = NSMakeSize(width, MIN_ROUNDED_RECT_SIZE); + + [self setNeedsDisplay:YES]; + [window setFrame:frame display:YES animate:NO]; +} + +- (void)setRemoteHost:(TPRemoteHost*)remoteHost +{ + if(remoteHost != _remoteHost) { + [_indeterminateTimer invalidate]; + _indeterminateTimer = nil; + + _remoteHost = remoteHost; + } + + [self _update]; +} + +- (void)setText:(NSString*)text +{ + if(text != _text) { + _text = [text copy]; + + if(text == nil) { + [_indeterminateTimer invalidate]; + _indeterminateTimer = nil; + } + + [self _update]; + } +} + +- (void)setShowProgress:(BOOL)showProgress +{ + if(_showProgress != showProgress) { + _showProgress = showProgress; + + if(!showProgress) { + [_indeterminateTimer invalidate]; + _indeterminateTimer = nil; + } + + [self setNeedsDisplay:YES]; + } +} + +- (void)setProgress:(float)progress +{ + if(_progress != progress) { + _progress = progress; + + [self setNeedsDisplay:YES]; + } +} + +- (void)_progressIndeterminateTimer:(NSTimer*)timer +{ + _indeterminateDelta++; + [self setNeedsDisplay:YES]; +} + +- (void)drawRect:(NSRect)rect +{ + [[NSColor clearColor] set]; + [[NSBezierPath bezierPathWithRect:rect] fill]; + + NSRect bounds = [self bounds]; + + /* Draw rounded rect */ + [[NSColor colorWithCalibratedWhite:0.0 alpha:WINDOW_TRANSPARENCY] set]; + [[NSBezierPath bezierPathWithRoundRectInRect:bounds radius:ROUNDED_RECT_RADIUS] fill]; + + /* Draw icon */ + NSPoint imagePoint = bounds.origin; + NSSize imageSize = [_teleportImage size]; + imagePoint.x += round((NSWidth(bounds) - imageSize.width)/2.0); + imagePoint.y += round((NSHeight(bounds) - imageSize.height + (Y_TITLE_MARGIN - Y_ICON_ADJUSTMENT))/2.0); + [_teleportImage drawAtPoint:imagePoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + + /* Create arrow */ + NSBezierPath * arrow = [NSBezierPath bezierPath]; + [arrow moveToPoint:NSZeroPoint]; + [arrow lineToPoint:NSMakePoint(0, ARROW_SIZE)]; + [arrow lineToPoint:NSMakePoint(ARROW_SIZE, ARROW_SIZE/2)]; + [arrow closePath]; + + /* Setup shadow */ + NSShadow * shadow = [[NSShadow alloc] init]; + [shadow setShadowBlurRadius:4.0]; + [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.8]]; + [shadow setShadowOffset:NSMakeSize(1.5,-1.5)]; + [shadow set]; + + /* Position and draw arrow */ + NSAffineTransform * transform = [NSClassFromString(@"NSAffineTransform") transform]; // to preserve 10.3 compatibility + + NSRect localRect = [[_remoteHost localScreen] frame]; + NSRect hostRect = [_remoteHost adjustedHostRect]; + float h = NSMidX(hostRect) - NSMidX(localRect); + float v = NSMidY(hostRect) - NSMidY(localRect); + float angle = atan2f(v, h); + + [transform translateXBy:NSMidX(bounds) yBy:NSMidY(bounds)]; + [transform rotateByRadians:angle]; + [transform translateXBy:-ARROW_SIZE/2.0 yBy:-ARROW_SIZE/2.0]; + + float width = NSWidth(bounds)/2.0; + float height = NSHeight(bounds)/2.0; + float limitAngle = atan2f(height, width); + float translate; + + if(angle >= M_PI/2.0) { + angle = M_PI - angle; + } + else if(angle < 0.0 && angle >= -M_PI/2.0) { + angle = -angle; + } + else if(angle < 0.0) { + angle = angle + M_PI; + } + + if(angle < limitAngle) { + translate = width/cosf(angle); + } + else { + translate = height/sinf(angle); + } + + [transform translateXBy:round(translate - ARROW_MARGIN) yBy:0.0]; + + [arrow transformUsingAffineTransform:transform]; + + [[NSColor whiteColor] set]; + [arrow fill]; + + /* Draw title */ + NSRect titleRect = bounds; + titleRect.origin.y -= (NSHeight(bounds) - Y_TITLE_MARGIN); + [[self _title] drawInRect:titleRect withAttributes:_titleAttributes]; + + /* Eventually draw lock image */ + if(_remoteHost != nil && [[TPLocalHost localHost] pairWithHost:_remoteHost hasCapability:TPHostEncryptionCapability]) { + NSPoint lockPoint = NSMakePoint(NSMaxX(bounds) - 26.0, NSMaxY(bounds) - 26.0); + [_lockImage drawAtPoint:lockPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + } + + /* Update timer */ + BOOL needTimer = _showProgress && (_progress == -1.0); + if(needTimer && (_indeterminateTimer == nil)) { + _indeterminateTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(_progressIndeterminateTimer:) userInfo:nil repeats:YES]; + } + else if(!needTimer && (_indeterminateTimer != nil)) { + [_indeterminateTimer invalidate]; + _indeterminateTimer = nil; + + } + + /* Eventually draw progress bar or text */ + if(_text != nil) { + NSRect textRect = bounds; + textRect.origin.y -= (NSHeight(bounds) - Y_TEXT_MARGIN); + + [_text drawInRect:textRect withAttributes:_textAttributes]; + } + else if(_showProgress) { + NSRect progressBarRect = bounds; + progressBarRect.origin.x += PROGRESSBAR_MARGIN; + progressBarRect.origin.y += PROGRESSBAR_MARGIN; + progressBarRect.size.width -= 2*PROGRESSBAR_MARGIN; + progressBarRect.size.height = PROGRESSBAR_HEIGHT; + + NSImage * progressImage = [[NSImage alloc] initWithSize:progressBarRect.size]; + [progressImage lockFocus]; + + [[NSColor whiteColor] set]; + NSRect strokeRect = progressBarRect; + strokeRect.origin = NSZeroPoint; + NSBezierPath * path = [NSBezierPath bezierPathWithRect:strokeRect]; + [path setLineWidth:2.0]; + [path stroke]; + + NSRect fillRect = strokeRect; + fillRect.origin.x += PROGRESSBAR_INTERNAL_MARGIN; + fillRect.origin.y += PROGRESSBAR_INTERNAL_MARGIN; + fillRect.size.width -= 2*PROGRESSBAR_INTERNAL_MARGIN; + fillRect.size.height -= 2*PROGRESSBAR_INTERNAL_MARGIN; + + if(_progress > 0.0) { + fillRect.size.width *= _progress; + + [[NSColor whiteColor] set]; + } + else if(_progress == -1.0) { + static NSImage * patternImage = nil; + if(patternImage == nil) { +#define LINE_WIDTH 4 + CGFloat dim = 2 * LINE_WIDTH * NSHeight(fillRect); + patternImage = [[NSImage alloc] initWithSize:NSMakeSize(dim, dim)]; + [patternImage lockFocus]; + + [[NSColor whiteColor] set]; + + CGFloat x = -dim; + while(x < dim) { + NSBezierPath * path = [[NSBezierPath bezierPath] init]; + [path moveToPoint:NSMakePoint(-x - LINE_WIDTH, 0.0)]; + [path lineToPoint:NSMakePoint(-x, 0.0)]; + [path lineToPoint:NSMakePoint(-x + dim, dim)]; + [path lineToPoint:NSMakePoint(-x - LINE_WIDTH + dim, dim)]; + [path closePath]; + [path fill]; + + x += 2 * LINE_WIDTH; + } + + [patternImage unlockFocus]; + } + + [[NSGraphicsContext currentContext] setPatternPhase:NSMakePoint(_indeterminateDelta % (2 * LINE_WIDTH), 0.0)]; + + [[NSColor colorWithPatternImage:patternImage] set]; + } + + [[NSBezierPath bezierPathWithRect:fillRect] fill]; + + [progressImage unlockFocus]; + [progressImage drawAtPoint:progressBarRect.origin fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + } + +} + +@end diff --git a/TPBezelWindow.h b/TPBezelWindow.h new file mode 100644 index 0000000..e39d10f --- /dev/null +++ b/TPBezelWindow.h @@ -0,0 +1,9 @@ +/* TPBezelWindow */ + +#import + +@interface TPBezelWindow : NSWindow +{ +} + +@end diff --git a/TPBezelWindow.m b/TPBezelWindow.m new file mode 100644 index 0000000..fa8641e --- /dev/null +++ b/TPBezelWindow.m @@ -0,0 +1,22 @@ +#import "TPBezelWindow.h" + +@implementation TPBezelWindow + +#if LEGACY_BUILD +- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag +#else +- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag +#endif +{ + self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + [self setBackgroundColor:[NSColor clearColor]]; + [self setAlphaValue:1.0]; + [self setOpaque:NO]; + [self setHasShadow:NO]; + [self setLevel:kCGOverlayWindowLevel]; + [self setIgnoresMouseEvents:YES]; + + return self; +} + +@end diff --git a/TPBezierPath.h b/TPBezierPath.h new file mode 100644 index 0000000..a5f8e99 --- /dev/null +++ b/TPBezierPath.h @@ -0,0 +1,12 @@ + + +#import + + +@interface NSBezierPath (teleportAdditions) + ++ (NSBezierPath *)bezierPathWithRoundRectInRect:(NSRect)rect radius:(float)radius; ++ (void)fillLeftRoundedRectInRect:(NSRect)aRect radius:(float)radius; ++ (void)drawRect:(NSRect)rect withGradientFrom:(NSColor*)colorStart to:(NSColor*)colorEnd; + +@end diff --git a/TPBezierPath.m b/TPBezierPath.m new file mode 100644 index 0000000..4427774 --- /dev/null +++ b/TPBezierPath.m @@ -0,0 +1,66 @@ + + +#import "TPBezierPath.h" + +@implementation NSBezierPath (teleportAdditions) + ++ (NSBezierPath *)bezierPathWithRoundRectInRect:(NSRect)aRect radius:(float) radius +{ + NSBezierPath * path = [NSBezierPath bezierPath]; + NSPoint topMid = NSMakePoint(NSMidX(aRect), NSMaxY(aRect)); + NSPoint topLeft = NSMakePoint(NSMinX(aRect), NSMaxY(aRect)); + NSPoint topRight = NSMakePoint(NSMaxX(aRect), NSMaxY(aRect)); + NSPoint bottomRight = NSMakePoint(NSMaxX(aRect), NSMinY(aRect)); + [path moveToPoint:topMid]; + [path appendBezierPathWithArcFromPoint:topLeft toPoint:aRect.origin radius:radius]; + [path appendBezierPathWithArcFromPoint:aRect.origin toPoint:bottomRight radius:radius]; + [path appendBezierPathWithArcFromPoint:bottomRight toPoint:topRight radius:radius]; + [path appendBezierPathWithArcFromPoint:topRight toPoint:topLeft radius:radius]; + [path closePath]; + return path; +} + ++ (void)fillLeftRoundedRectInRect:(NSRect)aRect radius:(float)radius +{ + if(aRect.size.width < 4) + return; + NSBezierPath * path = [NSBezierPath bezierPath]; + NSRect fullRect = aRect; + fullRect.size.width += 16; + NSPoint topMid = NSMakePoint(NSMidX(aRect), NSMaxY(aRect)); + NSPoint topLeft = NSMakePoint(NSMinX(aRect), NSMaxY(aRect)); + NSPoint topRight = NSMakePoint(NSMaxX(aRect), NSMaxY(aRect)); + NSPoint bottomRight = NSMakePoint(NSMaxX(aRect), NSMinY(aRect)); + [path moveToPoint:topMid]; + [path appendBezierPathWithArcFromPoint:topLeft toPoint:aRect.origin radius:radius]; + [path appendBezierPathWithArcFromPoint:aRect.origin toPoint:bottomRight radius:radius]; + [path appendBezierPathWithArcFromPoint:bottomRight toPoint:topRight radius:0]; + [path appendBezierPathWithArcFromPoint:topRight toPoint:topLeft radius:0]; + [path closePath]; + [path fill]; + //NSEraseRect(NSMakeRect(aRect.origin.x+aRect.size.width, aRect.origin.y, 18, aRect.size.height)); +} + ++ (void)drawRect:(NSRect)rect withGradientFrom:(NSColor*)colorStart to:(NSColor*)colorEnd +{ + float fraction = 0; + float height = rect.size.height - 1; + float width = rect.size.width; + float step = 1/height; + int i; + + NSRect gradientRect = NSMakeRect(rect.origin.x, rect.origin.y, width, 1.0); + [colorEnd set]; + [NSBezierPath fillRect:gradientRect]; + + for(i = 0; i < height; i++) + { + gradientRect.origin.y++; + NSColor * gradientColor = [colorStart blendedColorWithFraction:fraction ofColor:colorEnd]; + [gradientColor set]; + [NSBezierPath fillRect:gradientRect]; + fraction += step; + } +} + +@end diff --git a/TPBonjourController.h b/TPBonjourController.h new file mode 100644 index 0000000..e85cec0 --- /dev/null +++ b/TPBonjourController.h @@ -0,0 +1,31 @@ +// +// TPBonjourController.h +// teleport +// +// Created by JuL on 30/01/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import + + +@interface TPBonjourController : NSObject +{ + NSNetService * _publishService; + NSNetServiceBrowser * _browseService; + NSMutableDictionary * _browsers; + NSMutableSet *_services; + NSMutableDictionary * _namesToIdentifiersDict; +} + ++ (TPBonjourController*)defaultController; + +- (void)publishWithPort:(int)port; +- (void)updateTXTRecordOfPublishService; + +- (void)unpublish; + +- (void)browse; +- (void)stopBrowsing; + +@end diff --git a/TPBonjourController.m b/TPBonjourController.m new file mode 100644 index 0000000..f852ed6 --- /dev/null +++ b/TPBonjourController.m @@ -0,0 +1,422 @@ +// +// TPBonjourController.m +// teleport +// +// Created by JuL on 30/01/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import "TPBonjourController.h" + +#import "TPHostsManager.h" +#import "TPNetworkConnection.h" +#import "TPPreferencesManager.h" + +#import "TPLocalHost.h" +#import "TPRemoteHost.h" + +#import +#import +#import +#import +#import + +#define SHOW_LOCALHOST DEBUG_BUILD +#define RESOLVE_TIMEOUT 10.0 + +#define TXT_VERSION 1 + +static NSString * TPRecordTXTVersionKey = @"txtvers"; +static NSString * TPRecordIDKey = @"id"; +static NSString * TPRecordNameKey = @"name"; +static NSString * TPRecordProtocolVersionKey = @"protocol"; +static NSString * TPRecordScreenSizesKey = @"screen-sizes"; +static NSString * TPRecordCapabilitiesKey = @"capabilities"; +static NSString * TPRecordOSVersionKey = @"os-vers"; +static NSString * TPRecordHideIfNotPaired = @"hide"; + +static TPBonjourController * _defaultBonjourController = nil; + +@implementation TPBonjourController + ++ (TPBonjourController*)defaultController +{ + if(_defaultBonjourController == nil) + _defaultBonjourController = [[TPBonjourController alloc] init]; + + return _defaultBonjourController; +} + ++ (NSDictionary*)dictionaryFromProtocolSpecificInformation:(NSString*)info +{ + NSMutableDictionary * dictionary = [[NSMutableDictionary alloc] init]; + NSArray * pairs = [info componentsSeparatedByString:@"\x01"]; + NSEnumerator * pairEnum = [pairs objectEnumerator]; + NSString * pair; + + while((pair = [pairEnum nextObject]) != nil) { + NSArray * keyAndValue = [pair componentsSeparatedByString:@"="]; + if([keyAndValue count] == 2) { + dictionary[keyAndValue[0]] = keyAndValue[1]; + } + } + + return dictionary; +} + ++ (NSString*)protocolSpecificInformationFromDictionary:(NSDictionary*)dictionary +{ + NSMutableString * info = [[NSMutableString alloc] init]; + NSEnumerator * keyEnum = [dictionary keyEnumerator]; + NSString * key; + + while((key = [keyEnum nextObject]) != nil) { + NSString * value = dictionary[key]; + [info appendFormat:@"%@=%@\x01", key, value]; + } + + [info replaceCharactersInRange:NSMakeRange([info length]-1, 1) withString:@""]; // remove last \n + + return info; +} + +- (instancetype) init +{ + self = [super init]; + + _publishService = nil; + _browseService = nil; + _browsers = [[NSMutableDictionary alloc] init]; + _services = [[NSMutableSet alloc] init]; + + _namesToIdentifiersDict = [[NSMutableDictionary alloc] init]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateTXTRecordOfPublishService) name:TPHostDidUpdateNotification object:[TPLocalHost localHost]]; + + return self; +} + + +#pragma mark - +#pragma mark Publisher + +- (void)publishWithPort:(int)port +{ + if(_publishService != nil) + return; + + TPLocalHost * localHost = [TPLocalHost localHost]; + + _publishService = [[NSNetService alloc] initWithDomain:@"" type:RV_SERVICE name:[localHost bonjourName] port:port]; + + if(_publishService != nil) { + [self updateTXTRecordOfPublishService]; + [_publishService setDelegate:self]; + [_publishService publish]; + } + else + DebugLog(@"Error publishing Bonjour service"); +} + +- (void)updateTXTRecordOfPublishService +{ + NSArray * screens = [[TPLocalHost localHost] screens]; + + NSDictionary * recordDict = @{TPRecordTXTVersionKey: [NSString stringWithInt:TXT_VERSION], + TPRecordIDKey: [[TPLocalHost localHost] identifier], + TPRecordNameKey: [[TPLocalHost localHost] computerName], + TPRecordProtocolVersionKey: [NSString stringWithInt:PROTOCOL_VERSION], + TPRecordScreenSizesKey: [TPScreen stringFromScreens:screens], + TPRecordCapabilitiesKey: [NSString stringWithInt:[[TPLocalHost localHost] capabilities]], + TPRecordOSVersionKey: [NSString stringWithInt:[[TPLocalHost localHost] osVersion]], + TPRecordHideIfNotPaired: ([[TPPreferencesManager sharedPreferencesManager] intForPref:TRUST_REQUEST_BEHAVIOR] == TRUST_REQUEST_REJECT) ? @"1" : @"0"}; + + if([_publishService respondsToSelector:@selector(setTXTRecordData:)]) { + NSData * recordData = [NSNetService dataFromTXTRecordDictionary:recordDict]; + if(![_publishService setTXTRecordData:recordData]) + NSLog(@"Can't set TXT record data"); + } + else { + NSString * info = [[self class] protocolSpecificInformationFromDictionary:recordDict]; + [_publishService setProtocolSpecificInformation:info]; + } +} + +- (void)unpublish +{ + if(_publishService != nil) { + [_publishService stop]; + _publishService = nil; + } +} + +- (void)netServiceWillPublish:(NSNetService *)service +{ + //DebugLog(@"willPublish"); +} + +- (void)netService:(NSNetService *)service didNotPublish:(NSDictionary *)errorDict +{ + // DebugLog(@"didNotPublish"); +} + +- (void)netServiceDidStop:(NSNetService *)service +{ + //DebugLog(@"stop"); +} + + +#pragma mark - +#pragma mark Browser + +- (void)browse +{ + if(_browseService == nil) { + _browseService = [[NSNetServiceBrowser alloc] init]; + [_browseService scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; + [_browseService setDelegate:self]; + } + + [_browseService searchForBrowsableDomains]; +} + +- (void)stopBrowsing +{ + [_browseService stop]; +} + +- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser +{ + //DebugLog(@"willSearch"); + DebugLog(@"netServiceBrowserWillSearch: %@", browser); +} + +- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser +{ + DebugLog(@"netServiceBrowserDidStopSearch: %@", browser); + +} + +- (void)netServiceBrowser:(NSNetServiceBrowser *)browser didNotSearch:(NSDictionary *)errorDict +{ + //DebugLog(@"didNotSearch"); + DebugLog(@"didNotSearch: %@", errorDict); +} + +- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindDomain:(NSString *)domainString moreComing:(BOOL)moreComing +{ + NSNetServiceBrowser *browser = [[NSNetServiceBrowser alloc] init]; + browser.delegate = self; + [browser scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; + [browser searchForServicesOfType:RV_SERVICE inDomain:domainString]; + + [_browsers setObject:browser forKey:domainString]; + + DebugLog(@"didFindDomain: %@", domainString); +} + +- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveDomain:(NSString *)domainString moreComing:(BOOL)moreComing +{ + NSNetServiceBrowser *browser = [_browsers objectForKey:domainString]; + + if (browser != nil) { + [browser stop]; + [_browsers removeObjectForKey:domainString]; + } +} + +- (void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindService:(NSNetService *)service moreComing:(BOOL)moreComing +{ + [_services addObject:service]; + + [service setDelegate:self]; + + DebugLog(@"did find service: %@", service); + + if([service respondsToSelector:@selector(resolveWithTimeout:)]) + [service resolveWithTimeout:RESOLVE_TIMEOUT]; + else + [service resolve]; +} + +- (void)netServiceBrowser:(NSNetServiceBrowser *)browser didRemoveService:(NSNetService *)service moreComing:(BOOL)moreComing +{ + NSString * identifier = _namesToIdentifiersDict[[service name]]; + TPRemoteHost * remoteHost = [[TPHostsManager defaultManager] hostWithIdentifier:identifier]; + + if(remoteHost != nil) + [[TPHostsManager defaultManager] removeBonjourHost:remoteHost]; + + [_namesToIdentifiersDict removeObjectForKey:[service name]]; + [_services removeObject:service]; +} + +- (BOOL)_updateHost:(TPRemoteHost*)host withRecordDict:(NSDictionary*)recordDict +{ + TPHostCapability capabilities = [recordDict[TPRecordCapabilitiesKey] intValue]; + [host setCapabilities:capabilities]; + + SInt32 osVersion = [recordDict[TPRecordOSVersionKey] intValue]; + [host setOSVersion:osVersion]; + + NSArray * screens = [TPScreen screensFromString:[recordDict[TPRecordScreenSizesKey] stringValue]]; + [host setScreens:screens]; + + NSString * computerName = [recordDict[TPRecordNameKey] stringValue]; + [host setComputerName:computerName]; + + return ([screens count] > 0) && (computerName != nil); +} + +- (void)netServiceDidResolveAddress:(NSNetService *)service +{ + NSDictionary * recordDict = nil; + + DebugLog(@"did resolve service: %@", service); + + if([service respondsToSelector:@selector(TXTRecordData)]) { + NSData * recordData = [service TXTRecordData]; + if(recordData != nil) + recordDict = [NSNetService dictionaryFromTXTRecordData:recordData]; + } + + if(recordDict == nil) { + NSString * info = [service protocolSpecificInformation]; + recordDict = [[self class] dictionaryFromProtocolSpecificInformation:info]; + } + + if(recordDict == nil) { + NSLog(@"Invalid host: no TXT record!"); + [service stop]; + return; + } + + if([recordDict[TPRecordTXTVersionKey] intValue] != TXT_VERSION) { + DebugLog(@"Invalid host: out of date TXT record: %d", [recordDict[TPRecordTXTVersionKey] intValue]); + [service stop]; + return; + } + + if([recordDict[TPRecordProtocolVersionKey] intValue] == PROTOCOL_VERSION) { + NSArray * addresses = [service addresses]; + DebugLog(@"addresses: %@", addresses); + + if([addresses count] == 0) + return; + + //NSArray * localAddresses = [[NSHost currentHost] addresses]; + NSString * ipAddressString = nil; + int port = 0; + + // Iterate through addresses until we find an IPv4 address + int index; + for(index = 0; index < [addresses count]; index++) { + NSData * address = addresses[index]; + struct sockaddr * socketAddress = (struct sockaddr *)[address bytes]; + + if(socketAddress->sa_family == AF_INET) { + if(socketAddress != nil) { + char buffer[256]; + if(inet_ntop(AF_INET, &((struct sockaddr_in *)socketAddress)->sin_addr, buffer, sizeof(buffer))) { +#if LEGACY_BUILD + ipAddressString = [NSString stringWithCString:buffer]; +#else + ipAddressString = @(buffer); +#endif + + //NSLog(@"ip: %@, local: %@", ipAddressString, localAddresses); + port = ntohs(((struct sockaddr_in *)socketAddress)->sin_port); + } + } + } + + if(ipAddressString != nil) { + break; + } + } + + if(ipAddressString == nil) + return; + + TPRemoteHost * remoteHost = [[TPRemoteHost alloc] initWithIdentifier:[recordDict[TPRecordIDKey] stringValue] address:ipAddressString port:port]; + DebugLog(@"remoteHost: %@", remoteHost); + + if([self _updateHost:remoteHost withRecordDict:recordDict]) { + [remoteHost setHostState:TPHostSharedState]; + + if([service respondsToSelector:@selector(startMonitoring)]) + [service startMonitoring]; + + DebugLog(@"_namesToIdentifiersDict: %@", _namesToIdentifiersDict); + DebugLog(@"[remoteHost identifier]: %@, [service name]: %@", [remoteHost identifier], [service name]); + + + _namesToIdentifiersDict[[service name]] = [remoteHost identifier]; + + if(![remoteHost isEqual:[TPLocalHost localHost]]) { + [[TPHostsManager defaultManager] addBonjourHost:remoteHost]; + } + else { +#if SHOW_LOCALHOST + [[TPHostsManager defaultManager] addBonjourHost:remoteHost]; +#endif + } + } + + } + else { + TPRemoteHost * remoteHost = [[TPRemoteHost alloc] initWithIdentifier:[recordDict[TPRecordIDKey] stringValue] address:nil port:0]; + DebugLog(@"incompatible: %d != %d", [recordDict[TPRecordProtocolVersionKey] intValue], PROTOCOL_VERSION); + + if([self _updateHost:remoteHost withRecordDict:recordDict]) { + [remoteHost setHostState:TPHostIncompatibleState]; + + _namesToIdentifiersDict[[service name]] = [remoteHost identifier]; + + if(![remoteHost isEqual:[TPLocalHost localHost]] && ([remoteHost identifier] != nil)) + [[TPHostsManager defaultManager] addBonjourHost:remoteHost]; + } + + } + +// [service stop]; +// [service release]; +} + +- (void)netService:(NSNetService *)service didNotResolve:(NSDictionary *)errorDict +{ +} + +- (void)netService:(NSNetService *)sender didUpdateTXTRecordData:(NSData *)data +{ + if(data == nil) + return; + + NSString * previousIdentifier = _namesToIdentifiersDict[[sender name]]; + NSDictionary * recordDict = [NSNetService dictionaryFromTXTRecordData:data]; + NSString * identifier = [recordDict[TPRecordIDKey] stringValue]; + + if([identifier isEqualToString:previousIdentifier]) { + TPRemoteHost * host = [[TPHostsManager defaultManager] hostWithIdentifier:identifier]; + if(host != nil) { + [self _updateHost:host withRecordDict:recordDict]; + } + } + else { + TPRemoteHost * remoteHost = [[TPHostsManager defaultManager] hostWithIdentifier:previousIdentifier]; + + if(remoteHost != nil) { + TPRemoteHost * newRemoteHost = [[TPRemoteHost alloc] initWithIdentifier:identifier address:[remoteHost address] port:[remoteHost port]]; + + if([self _updateHost:newRemoteHost withRecordDict:recordDict]) { + [[TPHostsManager defaultManager] removeBonjourHost:remoteHost]; + + [[TPHostsManager defaultManager] addBonjourHost:newRemoteHost]; + _namesToIdentifiersDict[[sender name]] = identifier; + } + + } + } +} + +@end diff --git a/TPClientController.h b/TPClientController.h new file mode 100644 index 0000000..e836460 --- /dev/null +++ b/TPClientController.h @@ -0,0 +1,46 @@ +// +// TPClientController.h +// Teleport +// +// Created by JuL on Wed Dec 03 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +#import "TPConnectionController.h" +#import "TPEventsController.h" + +typedef NS_ENUM(NSInteger, TPClientState) { + TPClientIdleState, + TPClientConnectingState, + TPClientControllingState +} ; + +@class TPRemoteHost; + +@interface TPClientController : TPConnectionController +{ + TPClientState _state; + NSMutableDictionary * _hotBorders; + NSMutableDictionary * _hotKeys; + NSDictionary * _infoDict; + + io_object_t _sleepNotifier; + io_connect_t _sleepService; + IONotificationPortRef _sleepNotifyPortRef; +} + ++ (TPClientController*)defaultController; + +- (void)updateTriggersAndShowVisualHint:(BOOL)showVisualHint; + +- (void)requestStartControlOnHost:(TPRemoteHost*)remoteHost atLocation:(NSPoint)location withDraggingInfo:(id )draggingInfo; +- (void)sendStartControlRequestForConnection:(TPNetworkConnection*)connection withInfoDict:(NSDictionary*)infoDict; +- (void)startControl; + +- (void)requestedStopControlWithInfoDict:(NSDictionary*)infoDict; +- (void)stopControlWithDisconnect:(BOOL)disconnect; +@property (nonatomic, getter=isControlling, readonly) BOOL controlling; + +@end diff --git a/TPClientController.m b/TPClientController.m new file mode 100644 index 0000000..e0b1a6a --- /dev/null +++ b/TPClientController.m @@ -0,0 +1,674 @@ +// +// TPClientController.m +// Teleport +// +// Created by JuL on Wed Dec 03 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPClientController.h" +#import "TPMainController.h" +#import "TPAuthenticationManager.h" +#import "TPEventsController.h" +#import "TPBezelController.h" +#import "TPStatusItemController.h" +#import "TPNetworkConnection.h" +#import "TPTransfersManager.h" +#import "TPPreferencesManager.h" +#import "TPHostAnimationController.h" +#import "TPRemoteHost.h" +#import "TPLocalHost.h" +#import "TPHotBorder.h" +#import "TPMessage.h" + +#import "TPHostsManager.h" +#import "TPConnectionsManager.h" + +#import "PTHotKeyCenter.h" +#import "PTKeyBroadcaster.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define HOTBORDER_SAFE_MARGIN 3 + +static TPClientController * _defaultClientController = nil; + +@interface TPClientController (Internal) + +- (void)_startSleepPrevention; +- (void)_stopSleepPrevention; +- (NSDictionary*)_clientOptionsForRemoteHost:(TPRemoteHost*)remoteHost; + +@end + +@implementation TPClientController + ++ (TPClientController*)defaultController +{ + if(_defaultClientController == nil) + _defaultClientController = [[TPClientController alloc] init]; + + return _defaultClientController; +} + +- (instancetype) init +{ + self = [super init]; + + _hotBorders = [[NSMutableDictionary alloc] init]; + _hotKeys = [[NSMutableDictionary alloc] init]; + _sleepService = IO_OBJECT_NULL; + _state = TPClientIdleState; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateTriggers:) name:TPHostDidUpdateNotification object:nil]; + + return self; +} + + + +#pragma mark - +#pragma mark Hot borders + +- (TPHotBorder*)currentHotBorder +{ + TPNetworkConnection * currentConnection = [self currentConnection]; + if(currentConnection == nil) + return nil; + + TPRemoteHost * host = [currentConnection connectedHost]; + if(host == nil) + return nil; + + return _hotBorders[[host identifier]]; +} + +- (void)_updateTriggersForHost:(TPRemoteHost*)host showVisualHint:(BOOL)showVisualHint +{ + if(![host isKindOfClass:[TPRemoteHost class]]) + return; + + BOOL canControl = [(TPMainController*)[NSApp delegate] canControlHostWithIdentifier:[host identifier]]; + BOOL canWakeOnLAN = [host isInState:TPHostPeeredOfflineState] && [host hasValidMACAddress] && [[TPPreferencesManager sharedPreferencesManager] boolForPref:WAKE_ON_LAN]; + BOOL shouldEnableTriggers = canControl && (([host isInState:TPHostPeeredOnlineState | TPHostControlledState]) || canWakeOnLAN); + BOOL shouldActivateHotBorder = ![host isInState:TPHostControlledState]; + NSString * identifier = [host identifier]; + + TPHotBorder * hotBorder = _hotBorders[identifier]; + PTHotKey * hotKey = _hotKeys[identifier]; + + DebugLog(@"updating hot border %@ for host %@: show: %d activate: %d", hotBorder, host, shouldEnableTriggers, shouldActivateHotBorder); + + // Hot border + if(hotBorder != nil) { + if(shouldEnableTriggers) { + [hotBorder updateWithRepresentingRect:[host adjustedHostRect] inRect:[[host localScreen] frame]]; + + if(shouldActivateHotBorder && [hotBorder state] == TPHotBorderInactiveState) { + [hotBorder delayedActivate]; + } + else if(!shouldActivateHotBorder && [hotBorder state] != TPHotBorderInactiveState) { + [hotBorder deactivate]; + } + } + else { + DebugLog(@"remove hot border %@ with id %@", hotBorder, identifier); + [hotBorder deactivate]; + [self takeDownHotBorder:hotBorder]; + [hotBorder close]; + [_hotBorders removeObjectForKey:identifier]; + } + } + else if(shouldEnableTriggers) { + hotBorder = [TPHotBorder hotBorderRepresentingRect:[host adjustedHostRect] inRect:[[host localScreen] frame]]; + [hotBorder setDelegate:self]; + [hotBorder setIdentifier:identifier]; + [self setupHotBorder:hotBorder forHost:host]; + _hotBorders[identifier] = hotBorder; + + if(shouldActivateHotBorder) { + [hotBorder delayedActivate]; + } + + DebugLog(@"add hot border %@ with id %@", hotBorder, identifier); + } + + if(showVisualHint && shouldEnableTriggers && shouldActivateHotBorder && [host isInState:TPHostPeeredOnlineState] && [host previousHostState] != TPHostControlledState) { + [[TPHostAnimationController controller] showAppearanceAnimationForHost:host]; + } + + // Hot key + PTKeyCombo * keyCombo = [host keyCombo]; + BOOL isValidKeyCombo = (keyCombo != nil) && ![keyCombo isClearCombo] && [keyCombo isValidHotKeyCombo]; + BOOL enableKeyCombo = shouldEnableTriggers && isValidKeyCombo; + + if(enableKeyCombo) { + if(hotKey != nil && ![[hotKey keyCombo] isEqual:keyCombo]) { + [[PTHotKeyCenter sharedCenter] unregisterHotKey:hotKey]; + hotKey = nil; + } + + if(hotKey == nil) { + hotKey = [[PTHotKey alloc] initWithIdentifier:identifier keyCombo:keyCombo]; + [hotKey setTarget:self]; + [hotKey setAction:@selector(hotKeyPressed:)]; + [[PTHotKeyCenter sharedCenter] registerHotKey:hotKey]; + _hotKeys[identifier] = hotKey; + } + } + else if(hotKey != nil) { + [[PTHotKeyCenter sharedCenter] unregisterHotKey:hotKey]; + [_hotKeys removeObjectForKey:identifier]; + } +} + +- (void)updateTriggers:(NSNotification*)notification +{ + TPRemoteHost * host = [notification object]; + [self _updateTriggersForHost:host showVisualHint:YES]; +} + +- (void)updateTriggersAndShowVisualHint:(BOOL)showVisualHint +{ +#if DEBUG_GENERAL + DebugLog(@"client: startWaiting"); +#endif + + NSArray * hosts = [[TPHostsManager defaultManager] hostsWithState:TPHostAllStates]; + NSEnumerator * hostEnum = [hosts objectEnumerator]; + TPRemoteHost * host; + + while((host = [hostEnum nextObject]) != nil) { + [self _updateTriggersForHost:host showVisualHint:showVisualHint]; + } +} + +- (float)hotBorderSwitchDelay:(TPHotBorder*)hotBorder +{ + TPRemoteHost * remoteHost = [[TPHostsManager defaultManager] hostWithIdentifier:[hotBorder identifier]]; + if(![[remoteHost optionForKey:DELAYED_SWITCH] boolValue]) { + return 0.0; + } + else { + return [[remoteHost optionForKey:SWITCH_DELAY] floatValue]; + } +} + +- (BOOL)hotBorder:(TPHotBorder*)hotBorder canFireWithEvent:(NSEvent*)event +{ + if(![(TPMainController*)[NSApp delegate] canControlHostWithIdentifier:[hotBorder identifier]]) + return NO; + +#if ! LEGACY_BUILD + NSArray * applicationsIgnoringTeleport = [[TPPreferencesManager sharedPreferencesManager] valueForPref:APPLICATIONS_DISABLING_TELEPORT]; + if(applicationsIgnoringTeleport != nil) { + NSDictionary * activeApplication = [[NSWorkspace sharedWorkspace] activeApplication]; + NSString * currentApplicationIdentifier = activeApplication[@"NSApplicationBundleIdentifier"]; + if(currentApplicationIdentifier != nil) { + for(NSString * ignoringApp in applicationsIgnoringTeleport) { + if([ignoringApp isEqualToString:currentApplicationIdentifier]) { + DebugLog(@"Ignoring switch because of ignoring app: %@", ignoringApp); + return NO; + } + } + } + } +#endif + + TPRemoteHost * remoteHost = [[TPHostsManager defaultManager] hostWithIdentifier:[hotBorder identifier]]; + BOOL requireKey = [[remoteHost optionForKey:REQUIRE_KEY] boolValue]; + int keyTag = [[remoteHost optionForKey:SWITCH_KEY_TAG] intValue]; + return [[TPEventsController defaultController] event:event hasRequiredKeyIfNeeded:requireKey withTag:keyTag]; +} + +- (BOOL)hotBorder:(TPHotBorder*)hotBorder firedAtLocation:(NSPoint)location withDraggingInfo:(id )draggingInfo +{ + TPRemoteHost * remoteHost = [[TPHostsManager defaultManager] hostWithIdentifier:[hotBorder identifier]]; + + if([remoteHost hostState] == TPHostPeeredOnlineState) { +#if DEBUG_BUILD + BOOL isLocalHost = [remoteHost isEqual:[TPLocalHost localHost]]; + + if(!isLocalHost) { + [self requestStartControlOnHost:remoteHost atLocation:location withDraggingInfo:draggingInfo]; + } +#else + [self requestStartControlOnHost:remoteHost atLocation:location withDraggingInfo:draggingInfo]; +#endif + + return [super hotBorder:hotBorder firedAtLocation:location withDraggingInfo:draggingInfo]; + } + else if([remoteHost hostState] == TPHostPeeredOfflineState && [remoteHost hasValidMACAddress] && [[TPPreferencesManager sharedPreferencesManager] boolForPref:WAKE_ON_LAN]) { + if(![[TPConnectionsManager manager] wakeUpHost:remoteHost]) + [remoteHost invalidateMACAddress]; + else + [hotBorder activate]; + } + + return NO; +} + +- (void)hotKeyPressed:(PTHotKey*)hotKey +{ + NSString * identifier = [hotKey identifier]; + TPRemoteHost * remoteHost = [[TPHostsManager defaultManager] hostWithIdentifier:identifier]; + TPHotBorder * hotBorder = _hotBorders[identifier]; + + if([remoteHost hostState] == TPHostPeeredOnlineState) { + NSPoint location = NSMakePoint(-1.0, -1.0); +#if DEBUG_BUILD + BOOL isLocalHost = [remoteHost isEqual:[TPLocalHost localHost]]; + + if(!isLocalHost) { + [self requestStartControlOnHost:remoteHost atLocation:location withDraggingInfo:nil]; + } +#else + [self requestStartControlOnHost:remoteHost atLocation:location withDraggingInfo:nil]; +#endif + + if(hotBorder != nil) { + [super hotBorder:hotBorder firedAtLocation:NSZeroPoint withDraggingInfo:nil]; + } + } + else if([remoteHost hostState] == TPHostPeeredOfflineState && [remoteHost hasValidMACAddress] && [[TPPreferencesManager sharedPreferencesManager] boolForPref:WAKE_ON_LAN]) { + if(![[TPConnectionsManager manager] wakeUpHost:remoteHost]) + [remoteHost invalidateMACAddress]; + else if(hotBorder != nil) { + [hotBorder activate]; + } + } +} + + +#pragma mark - +#pragma mark Start control + +- (void)requestStartControlOnHost:(TPRemoteHost*)remoteHost atLocation:(NSPoint)location withDraggingInfo:(id )draggingInfo +{ + DebugLog(@"request start control on %@", remoteHost); + + if(remoteHost == nil || [remoteHost hostState] != TPHostPeeredOnlineState || _state != TPClientIdleState) + return; + + NSMutableDictionary * infoDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: + [NSValue valueWithPoint:location], TPMousePositionKey, + nil]; + + [self addDraggingInfo:draggingInfo toInfoDict:infoDict]; + + /* Do the fake mouse up */ + if(draggingInfo != nil) { + [[TPEventsController defaultController] mouseUpAtPosition:[NSEvent mouseLocation]]; + } + + /* Re-use existing connection if possible */ + BOOL shouldConnect = YES; + TPNetworkConnection * currentConnection = [self currentConnection]; + if(currentConnection != nil) { + if([currentConnection isValidForHost:remoteHost]) { + [self sendStartControlRequestForConnection:currentConnection withInfoDict:infoDict]; + shouldConnect = NO; + } + else { + [self setCurrentConnection:nil]; + } + } + + if(shouldConnect) { + _state = TPClientConnectingState; + [[TPConnectionsManager manager] connectToHost:remoteHost withDelegate:self infoDict:infoDict]; + } +} + +- (void)sendStartControlRequestForConnection:(TPNetworkConnection*)connection withInfoDict:(NSDictionary*)infoDict +{ + TPRemoteHost * controlledHost = [connection connectedHost]; + + NSPoint mousePosition = [infoDict[TPMousePositionKey] pointValue]; + + [self setCurrentConnection:connection]; + + NSRect clientScreenRect = [[controlledHost localScreen] frame]; + NSRect serverScreenRect = [controlledHost adjustedHostRect]; + + NSRect screenPlacement = clientScreenRect; + screenPlacement.origin.x -= NSMinX(serverScreenRect); + screenPlacement.origin.y -= NSMinY(serverScreenRect); + + NSRect hotRect = [TPHotBorder hotRectWithRepresentingRect:clientScreenRect inRect:serverScreenRect]; + + DebugLog(@"clientRect=%@ serverRect=%@ hotRect=%@ mousePosition=%@", NSStringFromRect(clientScreenRect), NSStringFromRect(serverScreenRect), NSStringFromRect(hotRect), NSStringFromPoint(mousePosition)); + +// NSPoint mousePosition; +// mousePosition.x = NSMinX(hotRect) + mouseLocation.x - NSMinX(serverScreenRect); +// mousePosition.y = NSHeight(serverScreenRect) - (NSMinY(hotRect) + mouseLocation.y - NSMinY(serverScreenRect)); + + _infoDict = infoDict; // XXX do this better + + /* Initiate connection */ + NSDictionary * clientOptions = [self _clientOptionsForRemoteHost:controlledHost]; + TPMessage * message = [TPMessage messageWithType:TPControlRequestMsgType + andInfoDict:@{TPScreenIndexKey: [NSData dataWithInt:[controlledHost sharedScreenIndex]], + TPScreenPlacementKey: [NSData dataWithRect:screenPlacement], + TPMousePositionKey: [NSData dataWithPoint:mousePosition], + TPSwitchOptionsKey: clientOptions}]; + + [[self currentConnection] sendMessage:message]; +} + +- (void)startControl +{ +#if DEBUG_GENERAL + DebugLog(@"client: startControl"); +#endif + + TPRemoteHost * controlledHost = [[self currentConnection] connectedHost]; + + /* Update status menu */ + [[TPStatusItemController defaultController] updateWithStatus:TPStatusControlling host:controlledHost]; + + /* Change state of controlled host */ + [controlledHost setHostState:TPHostControlledState]; + + /* Deactivate hot borders */ + [self updateTriggersAndShowVisualHint:NO]; + + /* Activate bezel */ + if(![[TPPreferencesManager sharedPreferencesManager] boolForPref:HIDE_CONTROL_BEZEL]) { + [[TPBezelController defaultController] showBezelWithControlledHost:controlledHost]; + } + + /* Play sound */ + if([[TPPreferencesManager sharedPreferencesManager] boolForPref:PLAY_SWITCH_SOUND]) { + [self playSwitchSound]; + } + + /* Begin transfers */ + [self beginTransfersWithInfoDict:_infoDict]; + _infoDict = nil; + + /* Activate events recorder */ + [self updateEventsController]; + [[self eventsController] startGettingEventsForListener:self onScreen:[controlledHost localScreen]]; + + /* Start preventing the Mac to go to sleep */ + [self _startSleepPrevention]; + + _state = TPClientControllingState; +} + +- (NSDictionary*)_clientOptionsForRemoteHost:(TPRemoteHost*)remoteHost +{ + NSString * optionKeys[] = { + REQUIRE_KEY, + SWITCH_WITH_DOUBLE_TAP, + SHARE_PASTEBOARD, + DELAYED_SWITCH, + SWITCH_DELAY, + LIMIT_PASTEBOARD_SIZE, + MAX_PASTEBOARD_SIZE, + SWITCH_KEY_TAG, + REQUIRE_PASTEBOARD_KEY, + PASTEBOARD_KEY_TAG, + SYNC_FIND_PASTEBOARD, + DOUBLE_TAP_INTERVAL, + COPY_FILES + }; + + int count = sizeof(optionKeys) / sizeof(NSString*); + int i; + + NSMutableDictionary * clientOptions = [NSMutableDictionary dictionaryWithCapacity:count]; + for(i=0; i_sleepService; + + switch(messageType) { + case kIOMessageCanSystemSleep: + DebugLog(@"Preventing sleep"); + IOCancelPowerChange(sleepService, (long)messageArgument); + break; + case kIOMessageSystemWillSleep: + IOAllowPowerChange(sleepService, (long)messageArgument); + break; + default: + break; + } +} + + +#pragma mark - +#pragma mark Events listener + +- (void)gotEventWithEventData:(NSData*)eventData +{ + //DebugLog(@"got event with event data (%d)", [eventData length]); + [[self currentConnection] sendMessage:[TPMessage messageWithType:TPEventMsgType andData:eventData]]; +} + +- (void)gotEmergencyStopEvent +{ + [self stopControl]; +} + +- (BOOL)shouldStopWithEvent:(NSEvent*)event +{ + if([event type] == NSKeyDown) { + TPRemoteHost * connectedHost = [[self currentConnection] connectedHost]; + PTKeyCombo * keyCombo = [connectedHost keyCombo]; + + if(keyCombo != nil) { + if(([keyCombo keyCode] == [event keyCode]) && ([keyCombo modifiers] == [PTKeyBroadcaster cocoaModifiersAsCarbonModifiers:[event modifierFlags]])) { + [self stopControlWithDisconnect:DISCONNECT_WHEN_STOP_CONTROL]; + return YES; + } + } + } + + return NO; +} + + +#pragma mark - +#pragma mark Connection + +- (void)connectionToServerSucceeded:(TPNetworkConnection*)connection infoDict:(NSDictionary*)infoDict +{ + [self sendStartControlRequestForConnection:connection withInfoDict:infoDict]; +} + +- (void)connectionToServerFailed:(TPRemoteHost*)host infoDict:(NSDictionary*)infoDict +{ + NSString * msgTitle = [NSString stringWithFormat:NSLocalizedString(@"Connection to \\U201C%@\\U201D failed.", @"Title for connection failure"), [host computerName]]; + NSAlert * alert = [NSAlert alertWithMessageText:msgTitle defaultButton:NSLocalizedString(@"OK", nil) alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(@"The server may be down, or an encryption problem may have occured. If encryption is enabled, please check that the certificate algorithms match.", nil)]; + [(TPMainController*)[NSApp delegate] presentAlert:alert]; + + [self updateTriggersAndShowVisualHint:NO]; + + _state = TPClientIdleState; +} + +- (void)connection:(TPNetworkConnection*)connection receivedMessage:(TPMessage*)message +{ + TPMsgType type = [message msgType]; + +#if DEBUG_GENERAL + DebugLog(@"master receive msg %ld", type); +#endif + switch(type) { + case TPControlSuccessMsgType: + [self startControl]; + break; + case TPControlFailureMsgType: + { + TPRemoteHost * controlledHost = [[self currentConnection] connectedHost]; + + [controlledHost setHostState:TPHostSharedState]; + + NSString * msgTitle = [NSString stringWithFormat:NSLocalizedString(@"Host \\U201C%@\\U201D rejected control.", @"Title for control failure"), [controlledHost computerName]]; + NSAlert * alert = [NSAlert alertWithMessageText:msgTitle defaultButton:NSLocalizedString(@"Yes", nil) alternateButton:NSLocalizedString(@"No", nil) otherButton:nil informativeTextWithFormat:NSLocalizedString(@"The host has probably removed this Mac from its trusted hosts list. Do you want to ask for trust again?", nil)]; + int result = [(TPMainController*)[NSApp delegate] presentAlert:alert]; + if(result == NSAlertDefaultReturn) { + controlledHost = [[TPHostsManager defaultManager] hostWithIdentifier:[controlledHost identifier]]; + [[TPAuthenticationManager defaultManager] requestAuthenticationOnHost:controlledHost]; + } + + break; + } + case TPControlStopMsgType: + { + [self requestedStopControlWithInfoDict:[message infoDict]]; + break; + } + default: + [super connection:connection receivedMessage:message]; + } +} + +@end diff --git a/TPClosingWindow.h b/TPClosingWindow.h new file mode 100644 index 0000000..601ed40 --- /dev/null +++ b/TPClosingWindow.h @@ -0,0 +1,16 @@ +// +// TPClosingWindow.h +// teleport +// +// Created by JuL on Mon Jan 26 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + + +@interface TPClosingWindow : NSWindow +{ +} + +@end diff --git a/TPClosingWindow.m b/TPClosingWindow.m new file mode 100644 index 0000000..cff30ae --- /dev/null +++ b/TPClosingWindow.m @@ -0,0 +1,25 @@ +// +// TPClosingWindow.m +// teleport +// +// Created by JuL on Mon Jan 26 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPClosingWindow.h" +#import "TPPreferencePane.h" + +@implementation TPClosingWindow + +- (void)keyDown:(NSEvent*)event +{ + //DebugLog(@"keyDown"); + [(id)[self delegate] closeAboutSheet]; +} + +- (void)mouseDown:(NSEvent*)event +{ + [(id)[self delegate] closeAboutSheet]; +} + +@end diff --git a/TPConnectionController.h b/TPConnectionController.h new file mode 100644 index 0000000..7df3788 --- /dev/null +++ b/TPConnectionController.h @@ -0,0 +1,51 @@ +// +// TPConnectionController.h +// teleport +// +// Created by JuL on Thu Jan 08 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +#define DISCONNECT_WHEN_STOP_CONTROL 0 + +@class TPNetworkConnection, TPMessage, TPEventsController, TPHotBorder, TPRemoteHost; + +extern NSString * TPScreenIndexKey; +extern NSString * TPScreenPlacementKey; +extern NSString * TPMousePositionKey; +extern NSString * TPSwitchOptionsKey; +extern NSString * TPDraggedPathsKey; +extern NSString * TPDragImageKey; +extern NSString * TPDragImageLocationKey; + +@interface TPConnectionController : NSObject +{ + IBOutlet id delegate; + TPNetworkConnection * _currentConnection; + TPEventsController * _eventsController; +} + +@property (nonatomic, strong) TPNetworkConnection *currentConnection; +- (void)updateEventsController; +@property (nonatomic, readonly, strong) TPEventsController *eventsController; +@property (nonatomic, readonly, strong) TPHotBorder *currentHotBorder; + +- (void)setupHotBorder:(TPHotBorder*)hotBorder forHost:(TPRemoteHost*)host; +- (void)takeDownHotBorder:(TPHotBorder*)hotBorder; +- (BOOL)hotBorder:(TPHotBorder*)hotBorder firedAtLocation:(NSPoint)location withDraggingInfo:(id )draggingInfo; + +- (void)playSwitchSound; + +- (void)stopControl; +- (void)stopControlWithDisconnect:(BOOL)disconnect; + +- (id)optionForRemoteHost:(TPRemoteHost*)remoteHost key:(NSString*)key; + +- (void)addDraggingInfo:(id)draggingInfo toInfoDict:(NSMutableDictionary*)infoDict; +- (void)beginTransfersWithInfoDict:(NSDictionary*)infoDict; + +- (void)connection:(TPNetworkConnection*)connection receivedMessage:(TPMessage*)message; + +@end diff --git a/TPConnectionController.m b/TPConnectionController.m new file mode 100644 index 0000000..1e2ca21 --- /dev/null +++ b/TPConnectionController.m @@ -0,0 +1,379 @@ +// +// TPConnectionController.m +// teleport +// +// Created by JuL on Thu Jan 08 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPConnectionController.h" +#import "TPPreferencesManager.h" +#import "TPAuthenticationManager.h" +#import "TPClientController.h" +#import "TPEventsController.h" +#import "TPHotBorder.h" +#import "TPHostAnimationController.h" + +#import "TPNetworkConnection.h" +#import "TPLocalHost.h" +#import "TPRemoteHost.h" +#import "TPMessage.h" + +#import "TPHostsManager.h" +#import "TPTransfersManager.h" +#import "TPTransfer.h" +#import "TPPasteboardTransfer.h" +#import "TPFileTransfer.h" + +NSString * TPScreenIndexKey = @"TPScreenIndex"; +NSString * TPScreenPlacementKey = @"TPScreenPlacement"; +NSString * TPMousePositionKey = @"TPMousePosition"; +NSString * TPSwitchOptionsKey = @"TPSwitchOptions"; +NSString * TPDraggedPathsKey = @"TPDraggedPaths"; +NSString * TPDragImageKey = @"TPDragImage"; +NSString * TPDragImageLocationKey = @"TPDragImageLocation"; + +typedef void* CoreDragRef; + +extern OSStatus CoreDragGetDragWindow(CoreDragRef drag, CGWindowID * wid); + +@interface NSDragDestination : NSObject +{ + NSWindow *_window; // non-retained window + void * trackingHandlerRef; + void * receiveHandlerRef; + NSString *_pasteboardName; + BOOL _finalSlide; + NSUInteger _lastDragDestinationOperation; + NSPoint _finalSlideLocation; // in screen coordinates + id _target; // retained pointer to last found target + CFRunLoopTimerRef _updateTimer; + CoreDragRef _drag; + NSMutableSet *_dragCompletionTargets; + CFRunLoopRef _runLoop; +} + +@end + +@interface NSDragDestination (HackHack) + +@property (nonatomic, readonly) CoreDragRef _getDragRef; + +@end + +@implementation NSDragDestination (HackHack) + +- (CoreDragRef)_getDragRef +{ + return _drag; +} + +@end + +@class TPClientController; + +@interface TPConnectionController (Internal) + +- (void)_preheatSwitchSoundIfNeeded; + +@end + +@implementation TPConnectionController + +- (instancetype) init +{ + self = [super init]; + + _currentConnection = nil; + + [self _preheatSwitchSoundIfNeeded]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_localHostDidChange:) name:TPHostDidUpdateNotification object:[TPLocalHost localHost]]; + + return self; +} + + + +#pragma mark - +#pragma mark Connection + +- (void)setCurrentConnection:(TPNetworkConnection*)networkConnection +{ + if(_currentConnection != networkConnection) { + [[TPTransfersManager manager] abortAllTransferRequests]; + + _currentConnection = networkConnection; + [_currentConnection setDelegate:self]; + } +} + +- (TPNetworkConnection*)currentConnection +{ + return _currentConnection; +} + +- (void)updateEventsController +{ + if(_currentConnection != nil) { + TPEventsController * eventsController = [TPEventsController eventsControllerForRemoteHost:[_currentConnection connectedHost]]; + if(_eventsController != eventsController) { + _eventsController = eventsController; + } + } + else { + _eventsController = nil; + } +} + +- (TPEventsController*)eventsController +{ + return _eventsController; +} + +- (void)stopControl +{ + [self stopControlWithDisconnect:YES]; +} + +- (void)stopControlWithDisconnect:(BOOL)disconnect +{ + if(disconnect) + [self setCurrentConnection:nil]; +} + +- (id)optionForRemoteHost:(TPRemoteHost*)remoteHost key:(NSString*)key +{ + return [remoteHost optionForKey:key]; +} + +- (void)_localHostDidChange:(NSNotification*)notification +{ + if(_currentConnection != nil && ([_currentConnection localHostCapabilities] != [[TPLocalHost localHost] capabilities])) { + DebugLog(@"localHost did change: disconnect"); + [self stopControl]; + } +} + + +#pragma mark - +#pragma mark Hot border + +- (TPHotBorder*)currentHotBorder +{ + return nil; +} + +- (void)setupHotBorder:(TPHotBorder*)hotBorder forHost:(TPRemoteHost*)host +{ + [hotBorder bind:@"doubleTap" toObject:host withKeyPath:@"options.switchWithDoubleTap" options:nil]; + [hotBorder bind:@"acceptDrags" toObject:host withKeyPath:@"options.copyFiles" options:nil]; +} + +- (void)takeDownHotBorder:(TPHotBorder*)hotBorder +{ + [hotBorder unbind:@"doubleTap"]; + [hotBorder unbind:@"acceptDrags"]; +} + +- (BOOL)hotBorder:(TPHotBorder*)hotBorder firedAtLocation:(NSPoint)location withDraggingInfo:(id )draggingInfo +{ + if([[TPPreferencesManager sharedPreferencesManager] boolForPref:SHOW_SWITCH_ANIMATION]) { + if([[TPLocalHost localHost] osVersion] >= TPHostOSVersion(5)) { + TPRemoteHost * remoteHost = [[TPHostsManager defaultManager] hostWithIdentifier:[hotBorder identifier]]; + + [[TPHostAnimationController controller] showFireAnimationForHost:remoteHost atPoint:[hotBorder screenPointFromLocalPoint:location flipped:NO] onScreen:[hotBorder screen] side:[hotBorder side]]; + +#if DEBUG_BUILD + if([remoteHost isEqual:[TPLocalHost localHost]]) { + [hotBorder activate]; + } +#endif + return NO; + } + else { + return YES; + } + } + else { + return NO; + } +} + +static NSString * _switchSoundPath = nil; +static NSSound * _switchSound = nil; ++ (NSSound*)_switchSound +{ + NSString * soundPath = [[TPPreferencesManager sharedPreferencesManager] valueForPref:SWITCH_SOUND_PATH]; + if(soundPath == nil || ![[NSFileManager defaultManager] fileExistsAtPath:soundPath]) { + soundPath = [[NSBundle bundleForClass:[self class]] pathForSoundResource:@"switch-sound"]; + } + + if((_switchSoundPath == nil) || ![_switchSoundPath isEqualToString:soundPath]) { + _switchSoundPath = [soundPath copy]; + + _switchSound = [[NSSound alloc] initWithContentsOfFile:soundPath byReference:NO]; + + if([_switchSound respondsToSelector:@selector(setVolume:)]) { + [_switchSound setVolume:0.3]; // play it soft + } + } + + return _switchSound; +} + +- (void)_preheatSwitchSoundIfNeeded +{ + if([[TPPreferencesManager sharedPreferencesManager] boolForPref:PLAY_SWITCH_SOUND]) { + [[self class] _switchSound]; + } +} + +- (void)playSwitchSound +{ + [[[self class] _switchSound] play]; +} + + +#pragma mark - +#pragma mark Transfers + +- (void)addDraggingInfo:(id)draggingInfo toInfoDict:(NSMutableDictionary*)infoDict +{ + if(draggingInfo != nil && infoDict != nil) { + NSPasteboard * dragPasteboard = [draggingInfo draggingPasteboard]; + + if([[dragPasteboard types] containsObject:NSFilenamesPboardType]) { + NSArray * draggedPaths = [dragPasteboard propertyListForType:NSFilenamesPboardType]; + NSImage * dragImage = [draggingInfo draggedImage]; + NSPoint dragImageLocation = [draggingInfo draggedImageLocation]; + +#if 0 + if(dragImage == nil) { + CoreDragRef dragRef = NULL; + @try { + dragRef = [(NSDragDestination*)draggingInfo _getDragRef]; + } + @catch (NSException * e) { + dragRef = NULL; + } + + if(dragRef != NULL) { + CGWindowID wid; + CoreDragGetDragWindow(dragRef, &wid); + + CGImageRef imageRef = CGWindowListCreateImage(CGRectZero, kCGWindowListOptionIncludingWindow, wid, kCGWindowImageDefault); + if(imageRef != NULL) { + NSBitmapImageRep * imageRep = [[NSBitmapImageRep alloc] initWithCGImage:imageRef]; + CFRelease(imageRef); + + if(imageRep != nil) { + dragImage = [[NSImage alloc] initWithSize:[imageRep size]]; + [dragImage addRepresentation:imageRep]; + [imageRep release]; + + [[dragImage TIFFRepresentation] writeToFile:@"/tmp/drag-image.tiff" atomically:YES]; + + [dragImage autorelease]; + } + } + } + } +#endif + +#if 0 + // fallback on NSWorkspace + if(dragImage == nil) { + if([draggedPaths count] == 1) { + dragImage = [[NSWorkspace sharedWorkspace] iconForFile:[draggedPaths lastObject]]; + dragImageLocation = NSZeroPoint; + } + } +#endif + + if(dragImage != nil) { + infoDict[TPDragImageKey] = dragImage; + infoDict[TPDragImageLocationKey] = [NSValue valueWithPoint:dragImageLocation]; + } + + infoDict[TPDraggedPathsKey] = draggedPaths; + } + } +} + +- (void)beginTransfersWithInfoDict:(NSDictionary*)infoDict +{ + /* First abort all pending transfer requests - real transfers will continue */ + [[TPTransfersManager manager] abortAllTransferRequests]; + + TPRemoteHost * remoteHost = [[self currentConnection] connectedHost]; + + BOOL sharePasteboard = [[self optionForRemoteHost:remoteHost key:SHARE_PASTEBOARD] boolValue]; + BOOL sharingPasteboardRequiresKey = [[self optionForRemoteHost:remoteHost key:REQUIRE_PASTEBOARD_KEY] boolValue]; + int sharingPastebardKeyTag = [[self optionForRemoteHost:remoteHost key:PASTEBOARD_KEY_TAG] intValue]; + + /* Send pasteboard */ + if(sharePasteboard && [[TPEventsController defaultController] event:[NSApp currentEvent] hasRequiredKeyIfNeeded:sharingPasteboardRequiresKey withTag:sharingPastebardKeyTag]) { + + /* General pasteboard */ + TPOutgoingPasteboardTransfer * generalPasteboardTransfer = (TPOutgoingPasteboardTransfer*)[TPOutgoingPasteboardTransfer transfer]; + if([[self optionForRemoteHost:remoteHost key:LIMIT_PASTEBOARD_SIZE] boolValue]) { + int maxKSize = [[self optionForRemoteHost:remoteHost key:MAX_PASTEBOARD_SIZE] intValue]; + unsigned long long maxSize = ((unsigned long long)maxKSize)*1024; + [generalPasteboardTransfer setMaxSize:maxSize]; + } + + [[TPTransfersManager manager] beginTransfer:generalPasteboardTransfer usingConnection:[self currentConnection]]; + + /* Optionnally the find pasteboard too */ + if([[self optionForRemoteHost:remoteHost key:SYNC_FIND_PASTEBOARD] boolValue]) { + TPOutgoingPasteboardTransfer * findPasteboardTransfer = (TPOutgoingPasteboardTransfer*)[TPOutgoingPasteboardTransfer transfer]; + [findPasteboardTransfer setPasteboardName:NSFindPboard]; + [[TPTransfersManager manager] beginTransfer:findPasteboardTransfer usingConnection:[self currentConnection]]; + } + } + + BOOL copyFiles = [[self optionForRemoteHost:remoteHost key:COPY_FILES] boolValue]; + + /* Send files */ + if(copyFiles && [[TPLocalHost localHost] pairWithHost:remoteHost hasCapability:TPHostDragNDropCapability]) { + NSArray * draggedFilePaths = infoDict[TPDraggedPathsKey]; + + if(draggedFilePaths != nil && [draggedFilePaths count] > 0) { + TPOutgoingFileTransfer * fileTransfer = (TPOutgoingFileTransfer*)[TPOutgoingFileTransfer transfer]; + [fileTransfer setFilePaths:draggedFilePaths dragImage:infoDict[TPDragImageKey] location:[infoDict[TPDragImageLocationKey] pointValue]]; + [[TPTransfersManager manager] beginTransfer:fileTransfer usingConnection:[self currentConnection]]; + } + } +} + + +#pragma mark - +#pragma mark Messages handling + +- (void)connection:(TPNetworkConnection*)connection receivedMessage:(TPMessage*)message +{ + TPMsgType type = [message msgType]; + + switch(type) { + case TPTransferRequestMsgType: + [[TPTransfersManager manager] receiveTransferRequestWithInfoDict:[message infoDict] onConnection:connection isClient:[self isKindOfClass:[TPClientController class]]]; + break; + case TPTransferSuccessMsgType: + [[TPTransfersManager manager] startTransferWithUID:[message infoDict][TPTransferUIDKey] usingConnection:connection onPort:[[message infoDict][TPTransferPortKey] intValue]]; + break; + case TPTransferFailureMsgType: + [[TPTransfersManager manager] abortTransferWithUID:[message string]]; + break; + default: + DebugLog(@"unknown message type: %ld", type); + } +} + +- (void)connectionDisconnected:(TPNetworkConnection*)connection +{ + DebugLog(@"connection broken - disconnecting"); + [self stopControl]; +} + +@end diff --git a/TPConnectionsManager.h b/TPConnectionsManager.h new file mode 100644 index 0000000..62cacee --- /dev/null +++ b/TPConnectionsManager.h @@ -0,0 +1,41 @@ +// +// TPConnectionsManager.h +// teleport +// +// Created by JuL on 24/02/06. +// Copyright 2006 abyssoft. All rights reserved. +// + +#import + +#import "TPNetworkConnection.h" + +@class TPTCPSecureSocket; + +@interface TPConnectionsManager : NSObject +{ + NSMutableSet * _connections; + NSMutableDictionary * _pendingConnections; + TPTCPSecureSocket * _listenSocket; + id _listenDelegate; +} + ++ (TPConnectionsManager*)manager; + +- (void)connectToHost:(TPRemoteHost*)host withDelegate:(id)delegate infoDict:(NSDictionary*)infoDict; + +- (BOOL)startListeningWithDelegate:(id)delegate onPort:(int*)port; +- (void)stopListening; + +- (BOOL)wakeUpHost:(TPRemoteHost*)host; + +@end + +@interface NSObject (TPConnectionsManager_delegate) + +- (void)connectionToServerSucceeded:(TPNetworkConnection*)networkConnection infoDict:(NSDictionary*)infoDict; +- (void)connectionToServerFailed:(TPRemoteHost*)host infoDict:(NSDictionary*)infoDict; + +- (void)connectionFromClientAccepted:(TPNetworkConnection*)networkConnection; + +@end diff --git a/TPConnectionsManager.m b/TPConnectionsManager.m new file mode 100644 index 0000000..148e6f1 --- /dev/null +++ b/TPConnectionsManager.m @@ -0,0 +1,330 @@ +// +// TPConnectionsManager.m +// teleport +// +// Created by JuL on 24/02/06. +// Copyright 2006 abyssoft. All rights reserved. +// + +#import "TPConnectionsManager.h" +//#import "TPNetworkConnection_Private.h" +#import "TPPreferencesManager.h" +#import "TPMainController.h" +#import "TPHostsManager.h" +#import "TPTCPSecureSocket.h" +#import "TPLocalHost.h" +#import "TPRemoteHost.h" +#import "TPMessage.h" + +static NSString * TPMsgProtocolVersionKey = @"proto-version"; +static NSString * TPMsgHostDataKey = @"host-data"; + +static TPConnectionsManager * _connectionsManager = nil; + +@class _TPSocketConnection; + +@interface TPConnectionsManager (Internal) + ++ (TPMessage*)_identificationMessage; + +- (void)_socketConnectionFailed:(_TPSocketConnection*)socketConnection; +- (void)_socketConnection:(_TPSocketConnection*)socketConnection succeededWithNetworkConnection:(TPNetworkConnection*)networkConnection; + +@end + +@interface _TPSocketConnection : NSObject +{ + @public + TPTCPSecureSocket * _socket; + TPRemoteHost * _host; + NSDictionary * _infoDict; + TPNetworkConnection * _connection; + id _delegate; +} + +- (instancetype) initWithConnectionToHost:(TPRemoteHost*)remoteHost delegate:(id)delegate infoDict:(NSDictionary*)infoDict NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readonly, strong) TPRemoteHost *host; +@property (nonatomic, readonly, copy) NSDictionary *infoDict; +@property (nonatomic, readonly, unsafe_unretained) id delegate; + +@end + +@implementation _TPSocketConnection + +- (instancetype) initWithConnectionToHost:(TPRemoteHost*)remoteHost delegate:(id)delegate infoDict:(NSDictionary*)infoDict +{ + self = [super init]; + + PRINT_ME; + + _host = remoteHost; + _infoDict = infoDict; + _delegate = delegate; + _socket = [[TPTCPSecureSocket alloc] initWithDelegate:self]; + + BOOL enableEncryption = [[TPLocalHost localHost] pairWithHost:remoteHost hasCapability:TPHostEncryptionCapability]; + [_socket setEnableEncryption:enableEncryption]; + [_socket connectToHost:[remoteHost address] onPort:[remoteHost port]]; + [_socket setNoDelay:YES]; + + return self; +} + + +- (TPRemoteHost*)host +{ + return _host; +} + +- (id)delegate +{ + return _delegate; +} + +- (NSDictionary*)infoDict +{ + return _infoDict; +} + +- (SecIdentityRef)tcpSecureSocketCopyIdentity:(TPTCPSecureSocket*)tcpSocket +{ + return [[TPConnectionsManager manager] tcpSecureSocketCopyIdentity:tcpSocket]; +} + +- (void)tcpSocketConnectionSucceeded:(TPTCPSocket*)tcpSocket +{ + PRINT_ME; + _connection = [[TPNetworkConnection alloc] initWithSocket:tcpSocket]; + + [_connection setDelegate:self]; + [_connection sendMessage:[TPConnectionsManager _identificationMessage]]; +} + +- (void)tcpSocketSecureConnectionSucceeded:(TPTCPSecureSocket*)tcpSocket +{ + SecCertificateRef certRef = [tcpSocket copyPeerCertificate]; + [_host setCertificate:certRef]; + CFRelease(certRef); + + [self tcpSocketConnectionSucceeded:tcpSocket]; +} + +- (void)tcpSocketConnectionFailed:(TPTCPSocket*)tcpSocket +{ + PRINT_ME; + [[TPConnectionsManager manager] _socketConnectionFailed:self]; +} + +- (void)tcpSocketSecureConnectionFailed:(TPTCPSecureSocket*)tcpSocket +{ + PRINT_ME; + [[TPConnectionsManager manager] _socketConnectionFailed:self]; +} + +- (void)connection:(TPNetworkConnection*)connection receivedMessage:(TPMessage*)message +{ + PRINT_ME; + if([message msgType] == TPIdentificationMsgType) { + NSDictionary * infoDict = [message infoDict]; + int protocolVersion = [infoDict[TPMsgProtocolVersionKey] intValue]; + + if(protocolVersion == PROTOCOL_VERSION) { + NSData * hostData = infoDict[TPMsgHostDataKey]; + TPRemoteHost * remoteHost = [[TPHostsManager defaultManager] updatedHostFromData:hostData]; + + [connection setConnectedHost:remoteHost]; + + [[TPConnectionsManager manager] _socketConnection:self succeededWithNetworkConnection:connection]; + } + else { + NSAlert * alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Wrong protocol version", @"Invalid protocol error title") defaultButton:NSLocalizedString(@"OK", nil) alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(@"The Mac you tried to control is using teleport protocol v%d, but you're using v%d.", @"Invalid protocol error message"), protocolVersion, PROTOCOL_VERSION]; + [(TPMainController*)[NSApp delegate] presentAlert:alert]; + + [[TPConnectionsManager manager] _socketConnectionFailed:self]; + } + } + else { + NSLog(@"received invalid msg type: %ld (expected identification - %ld)", [message msgType], (long)TPIdentificationMsgType); + + [[TPConnectionsManager manager] _socketConnectionFailed:self]; + } +} + +@end + +@implementation TPConnectionsManager + ++ (TPConnectionsManager*)manager +{ + if(_connectionsManager == nil) + _connectionsManager = [[TPConnectionsManager alloc] init]; + return _connectionsManager; +} + ++ (TPMessage*)_identificationMessage +{ + NSDictionary * infoDict = @{TPMsgProtocolVersionKey: @PROTOCOL_VERSION, + TPMsgHostDataKey: [[TPLocalHost localHost] hostData]}; + + return [TPMessage messageWithType:TPIdentificationMsgType andInfoDict:infoDict]; +} + +- (instancetype) init +{ + self = [super init]; + + _connections = [[NSMutableSet alloc] init]; + _pendingConnections = [[NSMutableDictionary alloc] init]; + + return self; +} + + +- (void)connectToHost:(TPRemoteHost*)host withDelegate:(id)delegate infoDict:(NSDictionary*)infoDict +{ + PRINT_ME; + + _TPSocketConnection * connection = _pendingConnections[[host identifier]]; + if(connection == nil) { + connection = [[_TPSocketConnection alloc] initWithConnectionToHost:host delegate:delegate infoDict:infoDict]; + _pendingConnections[[host identifier]] = connection; + } +} + +- (BOOL)startListeningWithDelegate:(id)delegate onPort:(int*)port +{ + if(_listenSocket != nil) + return YES; + + _listenDelegate = delegate; + + BOOL enableEncryption = [[TPLocalHost localHost] hasCapability:TPHostEncryptionCapability]; + _listenSocket = [[TPTCPSecureSocket alloc] initWithDelegate:self]; + [_listenSocket setEnableEncryption:enableEncryption]; + + return [_listenSocket listenOnPort:port tries:50]; +} + +- (void)stopListening +{ + [_listenSocket close]; + _listenSocket = nil; +} + + +#pragma mark - +#pragma mark Socket connection + +- (void)_socketConnectionFailed:(_TPSocketConnection*)socketConnection +{ + id delegate = [socketConnection delegate]; + TPRemoteHost * host = [socketConnection host]; + + if(delegate && [delegate respondsToSelector:@selector(connectionToServerFailed:infoDict:)]) + [delegate connectionToServerFailed:host infoDict:[socketConnection infoDict]]; + + [_pendingConnections removeObjectForKey:[host identifier]]; +} + +- (void)_socketConnection:(_TPSocketConnection*)socketConnection succeededWithNetworkConnection:(TPNetworkConnection*)networkConnection +{ + id delegate = [socketConnection delegate]; + TPRemoteHost * host = [socketConnection host]; + + if(delegate && [delegate respondsToSelector:@selector(connectionToServerSucceeded:infoDict:)]) + [delegate connectionToServerSucceeded:networkConnection infoDict:[socketConnection infoDict]]; + + [_pendingConnections removeObjectForKey:[host identifier]]; +} + + +#pragma mark - +#pragma mark Wake on LAN + +- (BOOL)wakeUpHost:(TPRemoteHost*)host +{ + if([host hasValidMACAddress]) { + DebugLog(@"trying to wake up host %@", host); + return [TPTCPSocket wakeUpHostWithMACAddress:[host MACAddress]]; + } + else + return NO; +} + + +#pragma mark - +#pragma mark Listen socket delegate + +- (void)tcpSocket:(TPTCPSocket*)listenSocket connectionAccepted:(TPTCPSocket*)childSocket +{ + DebugLog(@"connection accepted from %@ on %@", listenSocket, childSocket); + + [childSocket setNoDelay:YES]; + + TPNetworkConnection * connection = [[TPNetworkConnection alloc] initWithSocket:childSocket]; + + [connection setDelegate:self]; + [connection sendMessage:[TPConnectionsManager _identificationMessage]]; + [_connections addObject:connection]; +} + +- (BOOL)tcpSecureSocketShouldEnableEncryption:(TPTCPSecureSocket*)tcpSocket +{ + NSString * remoteAddress = [tcpSocket remoteAddress]; + TPRemoteHost * remoteHost = [[TPHostsManager defaultManager] hostWithAddress:remoteAddress]; + if(remoteHost == nil) { + NSLog(@"could not determine which host has IP %@, will use encrypted connection by default", remoteAddress); + return YES; // encrypted by default + } + else { + return [[TPLocalHost localHost] pairWithHost:remoteHost hasCapability:TPHostEncryptionCapability]; + } +} + +- (void)tcpSocket:(TPTCPSecureSocket*)listenSocket secureConnectionAccepted:(TPTCPSecureSocket*)childSocket +{ + [self tcpSocket:listenSocket connectionAccepted:childSocket]; +} + +- (SecIdentityRef)tcpSecureSocketCopyIdentity:(TPTCPSecureSocket*)tcpSocket +{ + SecIdentityRef identity = [[TPLocalHost localHost] identity]; + if(identity == NULL) { + return NULL; + } + else { + return (SecIdentityRef)CFRetain(identity); + } +} + +- (void)connection:(TPNetworkConnection*)connection receivedMessage:(TPMessage*)message +{ + if([message msgType] == TPIdentificationMsgType) { + NSDictionary * infoDict = [message infoDict]; + int protocolVersion = [infoDict[TPMsgProtocolVersionKey] intValue]; + + if(protocolVersion == PROTOCOL_VERSION) { + NSData * hostData = infoDict[TPMsgHostDataKey]; + TPRemoteHost * remoteHost = [[TPHostsManager defaultManager] updatedHostFromData:hostData]; + + [connection setConnectedHost:remoteHost]; + [[TPHostsManager defaultManager] addClientHost:remoteHost]; + + if(_listenDelegate && [_listenDelegate respondsToSelector:@selector(connectionFromClientAccepted:)]) + [_listenDelegate connectionFromClientAccepted:connection]; + } + else + NSLog(@"received message with invalid protocol (%d vs %d)", protocolVersion, PROTOCOL_VERSION); + } + else { + NSLog(@"received invalid msg type: %ld (excepted identification - %ld)", [message msgType], (long)TPIdentificationMsgType); + } +} + +- (void)connectionDisconnected:(TPNetworkConnection *)connection +{ + [_connections removeObject:connection]; +} + +@end diff --git a/TPDirectEventTapsController.h b/TPDirectEventTapsController.h new file mode 100644 index 0000000..30664a7 --- /dev/null +++ b/TPDirectEventTapsController.h @@ -0,0 +1,17 @@ +// +// TPDirectEventTapsController.h +// teleport +// +// Created by Julien Robert on 01/08/07. +// Copyright 2007 abyssoft. All rights reserved. +// + +#import +#import "TPEventTapsController.h" + +@interface TPDirectEventTapsController : TPEventTapsController +{ + +} + +@end diff --git a/TPDirectEventTapsController.m b/TPDirectEventTapsController.m new file mode 100644 index 0000000..c9bc455 --- /dev/null +++ b/TPDirectEventTapsController.m @@ -0,0 +1,126 @@ +// +// TPDirectEventTapsController.m +// teleport +// +// Created by Julien Robert on 01/08/07. +// Copyright 2007 abyssoft. All rights reserved. +// + +#import "TPDirectEventTapsController.h" +#import "TPEventTapsController_Internal.h" + +//typedef uint16_t CGSKeyCode; +//typedef uint16_t CGSCharCode; +//typedef uint32_t CGSEventMask; +//typedef uint32_t CGSEventTime; /* ticks, or about 1/60 second */ +//typedef uint64_t CGSEventRecordTime; /* nanosecond timer */ +//typedef uint16_t CGSEventRecordVersion; +//typedef uint32_t CGSEventType; +//typedef uint32_t CGSByteCount; /* Should be `size_t'. */ +//typedef uint32_t CGSEventFlag; +//typedef uint32_t CGSConnectionID; +//typedef uint32_t CGSWindowID; +//typedef unsigned char CGSBoolean; +// +// +///* Finally: The event record! */ +//struct _CGSEventRecord { +// CGSEventRecordVersion major; +// CGSEventRecordVersion minor; +// CGSByteCount length; /* Length of complete event record */ +// CGSEventType type; /* An event type from above */ +// CGPoint location; /* Base coordinates (global), from upper-left */ +// CGPoint windowLocation; /* Coordinates relative to window */ +// CGSEventRecordTime time; /* nanoseconds since startup */ +// CGSEventFlag flags; /* key state flags */ +// CGSWindowID window; /* window number of assigned window */ +// CGSConnectionID connection; /* connection the event came from, or that owns the window */ +// /* New for 10.4 */ +// +// UInt8 pad1[40]; +// UInt8 pad2[20]; +// UInt8 pad3[72]; +//#ifdef __LP64__ +// void * ioEventData; /* TEMPORARY; used in server while converting to new format; gone in 10.6 */ +//#else +// void * ioEventData; /* TEMPORARY; used in server while converting to new format; gone in 10.6 */ +// uint32_t _padding __attribute__ ((deprecated)); /* Preserve alignment, reserved for future use */ +//#endif +// /* New for 10.5: Values used to map global and window coordinates between Quadrant I and III */ +// uint16_t windowHeight; /* Height of window in 'window' field */ +// uint16_t mainDisplayHeight; /* Height of main display */ +// uint16_t *unicodePayload; /* TEMPORARY: Unicode payload, valid only while CGEventRef container exists; gone in 10.6 */ +// +// /* New for 10.7: connection that gets events for window (as opposed to that owns the window). +// * They are different in the case of cross-process window hosting. +// */ +// CGSConnectionID eventOwner; +// /* Also new for 10.7: a flag to indicate whether a click passed through an event-transparent window */ +// CGSBoolean passedThrough; +//}; +// +//typedef struct _CGSEventRecord CGSEventRecord; +// +//extern uint32_t CGEventGetEventRecordSize(CGEventRef event); +//extern CGError CGEventGetEventRecord(CGEventRef event, CGSEventRecord * eventRecord, uint32_t eventRecordSize); +//extern CGError CGEventSetEventRecord(CGEventRef event, CGSEventRecord * eventRecord, uint32_t eventRecordSize); + +#define CGIsMouseEventType(event) (event == kCGEventMouseMoved) || (event == kCGEventLeftMouseDragged) || (event == kCGEventRightMouseDragged) || (event == kCGEventOtherMouseDragged) + +static TPDirectEventTapsController * _eventTapsController = nil; + +@implementation TPDirectEventTapsController + ++ (TPEventsController*)defaultController +{ + if(_eventTapsController == nil) + _eventTapsController = [[TPDirectEventTapsController alloc] init]; + return _eventTapsController; +} + +- (NSData*)_eventDataFromEvent:(id)e +{ + CGEventRef event = (__bridge CGEventRef)e; + + +// CGSEventRecord eventRecord; +// +// uint32_t size = CGEventGetEventRecordSize(event); +// +// CGEventGetEventRecord(event, &eventRecord, size); +// eventRecord.ioEventData = NULL; +// +// CGEventSetEventRecord(event, &eventRecord, size); +// + NSData * eventData = (NSData*)CFBridgingRelease(CGEventCreateData(NULL, event)); + + return eventData; +} + +- (void)_postEventWithEventData:(NSData*)eventData +{ + if([eventData length] == 0) return; + CGEventRef event = CGEventCreateFromData(NULL, (__bridge CFDataRef)eventData); + CGEventType eventType = CGEventGetType(event); + + //DebugLog(@"post event %@", [TPEventTapsController _eventNameFromType:eventType]); + + if(CGIsMouseEventType(eventType)) { + int64_t deltaX = CGEventGetIntegerValueField(event, kCGMouseEventDeltaX); + int64_t deltaY = CGEventGetIntegerValueField(event, kCGMouseEventDeltaY); + + if(deltaX != 0 || deltaY != 0) { + TPMouseDelta mouseDelta = {deltaX, deltaY}; + [self _updateMouseLocationWithMouseDelta:mouseDelta]; + + //DebugLog(@"mouseDelta=%f %f mouseLocation=%f %f", mouseDelta.x, mouseDelta.y, _currentMouseLocation.x, _currentMouseLocation.y); + } + } + + CGEventSetLocation(event, _currentMouseLocation); + + [self _postEvent:event]; + CFRelease(event); +} + +@end diff --git a/TPEventTapsController.h b/TPEventTapsController.h new file mode 100644 index 0000000..968d43d --- /dev/null +++ b/TPEventTapsController.h @@ -0,0 +1,19 @@ +// +// TPEventTapsController.h +// teleport +// +// Created by JuL on 09/11/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import + +#import "TPEventsController.h" + +@interface TPEventTapsController : TPEventsController +{ + CFMachPortRef _eventPort; + CGEventSourceRef _eventSource; +} + +@end diff --git a/TPEventTapsController.m b/TPEventTapsController.m new file mode 100644 index 0000000..aa8ed72 --- /dev/null +++ b/TPEventTapsController.m @@ -0,0 +1,613 @@ +// +// TPEventTapsController.m +// teleport +// +// Created by JuL on 09/11/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import "TPEventTapsController.h" +#import "TPEventTapsController_Internal.h" + +#import "TPPreferencesManager.h" +#import "TPMainController.h" + +#include + +#define DEBUG_EVENTTAP 0 +#define MAX_STRING 20 + +void CGSSetConnectionProperty(int, int, CFStringRef, CFBooleanRef); +int _CGSDefaultConnection(); + +static TPEventTapsController * _eventTapsController = nil; + +static const char * eventTypeName[] = +{ + "NullEvent", "LMouseDown", "LMouseUp", "RMouseDown", "RMouseUp", + "MouseMoved", "LMouseDragged", "RMouseDragged", "MouseEntered", + "MouseExited", "KeyDown", "KeyUp", "FlagsChanged", "Kitdefined", + "SysDefined", "AppDefined", "Timer", "CursorUpdate", + "Journaling", "Suspend", "Resume", "Notification", + "ScrollWheel", "TabletPointer", "TabletProximity", + "OtherMouseDown", "OtherMouseUp", "OtherMouseDragged", "Zoom" +}; + +@interface TPEventTapsController (Internal) + +- (void)_startGettingsEvents; +- (void)_postEvent:(CGEventRef)event; + +- (BOOL)_shouldSkipFirstEvent:(id)e; +- (BOOL)_isEmergencyStopEvent:(id)e; + +@end + +@implementation TPEventTapsController + ++ (TPEventsController*)defaultController +{ + if(_eventTapsController == nil) + _eventTapsController = [[TPEventTapsController alloc] init]; + return _eventTapsController; +} + +static CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) +{ + TPEventTapsController * self = (__bridge TPEventTapsController*)refcon; + +#if DEBUG_EVENTTAP + NSLog(@"Callback: %@", [TPEventTapsController _eventNameFromType:type]); +#endif + + if(type == kCGEventTapDisabledByTimeout) { + [self _startGettingsEvents]; + } + else { + [self _sendEventToListener:(__bridge id)event]; + } + + return NULL; +} + ++ (NSString*)_eventNameFromType:(CGEventType)type +{ + if(type < (sizeof eventTypeName / sizeof eventTypeName[0])) { + return @(eventTypeName[type]); + } + else { + return [NSString stringWithFormat:@"Event Type 0x%x", type]; + } +} + + +#pragma mark - +#pragma mark Getting events + +- (void)_startGettingEventsOnScreen:(NSScreen*)screen +{ +#if DISABLE_CONTROL + return; +#endif + + if(IsSecureEventInputEnabled()) { + [(TPMainController*)[NSApp delegate] goFrontmost]; + } + + // Hack to make background cursor setting work + CFStringRef propertyString = CFStringCreateWithCString(NULL, "SetsCursorInBackground", kCFStringEncodingUTF8); + CGSSetConnectionProperty(_CGSDefaultConnection(), _CGSDefaultConnection(), propertyString, kCFBooleanTrue); + CFRelease(propertyString); + + CGDisplayHideCursor(kCGDirectMainDisplay); + + if(_eventPort == NULL) { + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + + _eventPort = CGEventTapCreate(kCGSessionEventTap, + kCGHeadInsertEventTap, + 0, +#if 0 + CGEventMaskBit(kCGEventLeftMouseDown) | + CGEventMaskBit(kCGEventLeftMouseUp) | + CGEventMaskBit(kCGEventRightMouseDown) | + CGEventMaskBit(kCGEventRightMouseUp) | + CGEventMaskBit(kCGEventMouseMoved) | + CGEventMaskBit(kCGEventLeftMouseDragged) | + CGEventMaskBit(kCGEventRightMouseDragged) | + CGEventMaskBit(kCGEventKeyDown) | + CGEventMaskBit(kCGEventKeyUp) | + CGEventMaskBit(kCGEventFlagsChanged) | + CGEventMaskBit(kCGEventScrollWheel) | + CGEventMaskBit(kCGEventTabletPointer) | + CGEventMaskBit(kCGEventTabletProximity) | + CGEventMaskBit(kCGEventOtherMouseDown) | + CGEventMaskBit(kCGEventOtherMouseUp) | + CGEventMaskBit(kCGEventOtherMouseDragged) | + CGEventMaskBit(NX_ZOOM), +#else + kCGEventMaskForAllEvents & ~(NX_KITDEFINEDMASK | + NX_APPDEFINEDMASK), +#endif + eventCallback, + (__bridge void *)(self)); + if(_eventPort == NULL) { + NSLog(@"Can't create event port"); + return; + } + + CFRunLoopSourceRef eventSrc = CFMachPortCreateRunLoopSource(NULL, _eventPort, 0); + if(eventSrc == NULL) { + NSLog(@"Can't create event src"); + } + else { + CFRunLoopAddSource(runLoop, eventSrc, kCFRunLoopDefaultMode); + CFRelease(eventSrc); + } + } + else { + [self _startGettingsEvents]; + } +} + +- (void)_sendEventToListener:(id)event +{ + if(CGEventTapIsEnabled(_eventPort)) { + [super _sendEventToListener:event]; + } +} + +- (void)_startGettingsEvents +{ + if(_eventPort != NULL) { + CGEventTapEnable(_eventPort, true); + } +} + +- (void)_stopGettingEvents +{ + if(_eventPort != NULL) { + CGEventTapEnable(_eventPort, false); + } + + [(TPMainController*)[NSApp delegate] leaveFrontmost]; +} + +- (void)cleanupGettingEvents +{ + [super cleanupGettingEvents]; + + CGDisplayShowCursor(kCGDirectMainDisplay); +} + + +#pragma mark - +#pragma mark Posting events + +- (void)startPostingEvents +{ + _eventSource = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); + +// CGEventSourceSetLocalEventsFilterDuringSuppressionState(_eventSource, 0, kCGEventSuppressionStateSuppressionInterval); + +// _eventSource = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); +// _eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); +} + +- (void)stopPostingEvents +{ + if(_eventSource != NULL) { + CFRelease(_eventSource); + _eventSource = NULL; + } +} + +- (void)_postEvent:(CGEventRef)event +{ + CGEventSetTimestamp(event, CGSCurrentEventTimestamp()); +// CGEventSourceStateID sourceState = CGEventSourceGetSourceStateID(_eventSource); +// CGEventFlags flags = CGEventSourceFlagsState(sourceState); +// DebugLog(@"event flags=%lld", CGEventGetFlags(event)); +// DebugLog(@"before source flags=%lld", flags); +// CGEventSetFlags(event, flags); +// DebugLog(@"post event %s", eventTypeName[eventType]); + + if(_eventSource != NULL) { + CGEventSetSource(event, _eventSource); + } + + CGEventPost(kCGSessionEventTap, event); +// DebugLog(@"after source flags=%lld", CGEventSourceFlagsState(sourceState)); +} + + +#pragma mark - +#pragma mark Conversions + +#define APPEND_INTEGER(key) \ +{ \ + int64_t value = CGEventGetIntegerValueField(event, key); \ + int64_t swappedValue = NSSwapHostLongLongToBig(value); \ + [eventData appendData:[NSData dataWithBytes:&swappedValue length:sizeof(int64_t)]]; \ +} + +#define APPEND_DOUBLE(key) \ +{ \ + double value = CGEventGetDoubleValueField(event, key); \ + NSSwappedDouble swappedValue = NSSwapHostDoubleToBig(value); \ + [eventData appendData:[NSData dataWithBytes:&swappedValue length:sizeof(NSSwappedDouble)]]; \ +} + +#define READ_INTEGER(key) \ +{ \ + int64_t swappedValue; \ + [eventData _readBytes:&swappedValue withSize:sizeof(int64_t) atPos:&pos]; \ + int64_t value = NSSwapBigLongLongToHost(swappedValue); \ + CGEventSetIntegerValueField(event, key, value); \ +} + +#define READ_DOUBLE(key) \ +{ \ + NSSwappedDouble swappedValue; \ + [eventData _readBytes:&swappedValue withSize:sizeof(NSSwappedDouble) atPos:&pos]; \ + double value = NSSwapBigDoubleToHost(swappedValue); \ + CGEventSetDoubleValueField(event, key, value); \ +} + +- (NSData*)_eventDataFromEvent:(id)e +{ + CGEventRef event = (__bridge CGEventRef)e; + + NSMutableData * eventData = [[NSMutableData alloc] init]; + + CGEventType eventType = CGEventGetType(event); + CGEventType swappedEventType = NSSwapHostIntToBig(eventType); + +// DebugLog(@"postEvent: %d", eventType); + + /* Write event type */ + [eventData appendData:[NSData dataWithBytes:&swappedEventType length:sizeof(CGEventType)]]; + + /* Write event data */ + switch(eventType) { + case kCGEventMouseMoved: + case kCGEventLeftMouseDragged: + case kCGEventRightMouseDragged: + case kCGEventOtherMouseDragged: + { + APPEND_INTEGER(kCGMouseEventNumber) + APPEND_INTEGER(kCGMouseEventDeltaX) + APPEND_INTEGER(kCGMouseEventDeltaY) + APPEND_INTEGER(kCGMouseEventButtonNumber) + break; + } + case kCGEventScrollWheel: + case NX_ZOOM: + { + APPEND_INTEGER(kCGScrollWheelEventDeltaAxis1) + APPEND_INTEGER(kCGScrollWheelEventDeltaAxis2) + APPEND_INTEGER(kCGScrollWheelEventDeltaAxis3) + + if(1) { + APPEND_INTEGER(kCGScrollWheelEventScrollPhase) + APPEND_INTEGER(kCGScrollWheelEventIsContinuous) + + APPEND_DOUBLE(kCGScrollWheelEventFixedPtDeltaAxis1) + APPEND_DOUBLE(kCGScrollWheelEventFixedPtDeltaAxis2) + APPEND_DOUBLE(kCGScrollWheelEventFixedPtDeltaAxis3) + APPEND_DOUBLE(kCGScrollWheelEventPointDeltaAxis1) + APPEND_DOUBLE(kCGScrollWheelEventPointDeltaAxis2) + APPEND_DOUBLE(kCGScrollWheelEventPointDeltaAxis3) + } + break; + } + case kCGEventLeftMouseDown: + case kCGEventLeftMouseUp: + case kCGEventRightMouseDown: + case kCGEventRightMouseUp: + case kCGEventOtherMouseDown: + case kCGEventOtherMouseUp: + { + APPEND_INTEGER(kCGMouseEventNumber) + APPEND_INTEGER(kCGMouseEventButtonNumber) + APPEND_INTEGER(kCGMouseEventClickState) + APPEND_INTEGER(kCGMouseEventInstantMouser) + APPEND_INTEGER(kCGMouseEventSubtype) + APPEND_DOUBLE(kCGMouseEventPressure) + break; + } + case kCGEventKeyUp: + case kCGEventKeyDown: + { + CGKeyCode keyCode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode); + CGKeyCode swappedKeyCode = NSSwapHostShortToBig(keyCode); + [eventData appendData:[NSData dataWithBytes:&swappedKeyCode length:sizeof(CGKeyCode)]]; + + APPEND_INTEGER(kCGKeyboardEventKeyboardType) + + UniChar string[MAX_STRING]; + UniCharCount length; + CGEventKeyboardGetUnicodeString(event, MAX_STRING, &length, string); + +// DebugLog(@"OUT string=%@ eventkbt: %d", [NSString stringWithCharacters:string length:length], keyboardType); + + UniCharCount swappedLength = NSSwapHostIntToBig(length); + [eventData appendData:[NSData dataWithBytes:&swappedLength length:sizeof(UniCharCount)]]; + + UniCharCount i; + for(i=0; i + +#define ESC_KEYCODE 53 +#define DISABLE_CONTROL 0 + +@class TPRemoteHost; + +@protocol TPEventsListener + +- (void)gotEventWithEventData:(NSData*)eventData; +- (void)gotEmergencyStopEvent; +- (BOOL)shouldStopWithEvent:(NSEvent*)event; + +@end + +@interface TPEventsController : NSObject +{ + id _eventsListener; + + int _currentScreenIndex; + CGPoint _currentMouseLocation; + + NSTimeInterval _lastPostedEventDate; +} + ++ (TPEventsController*)defaultController; ++ (TPEventsController*)eventsControllerForRemoteHost:(TPRemoteHost*)remoteHost; + +- (void)startGettingEventsForListener:(id)eventsListener onScreen:(NSScreen*)screen; +- (void)stopGettingEvents; +- (void)cleanupGettingEvents; +@property (nonatomic, readonly, strong) id eventsListener; + +- (void)startPostingEvents; +- (void)stopPostingEvents; +- (void)postEventWithEventData:(NSData*)eventData; + +- (void)warpMouseToPosition:(NSPoint)position; +- (void)mouseDownAtPosition:(NSPoint)position; +- (void)mouseUpAtPosition:(NSPoint)position; + +- (BOOL)event:(NSEvent*)event hasRequiredKeyIfNeeded:(BOOL)needed withTag:(NSEventType)tag; + +@end + +@interface TPEventsController (Internal) + +- (void)_startGettingEventsOnScreen:(NSScreen*)screen; +- (void)_sendEventToListener:(id)event; +- (void)_stopGettingEvents; + +- (BOOL)_shouldSkipFirstEvent:(id)event; +- (BOOL)_isEmergencyStopEvent:(id)event; +- (NSEvent*)_nsEventFromEvent:(id)event; +- (NSData*)_eventDataFromEvent:(id)event; +- (void)_postEventWithEventData:(NSData*)eventData; + +- (void)_updateMouseLocationWithMouseDelta:(TPMouseDelta)mouseDelta; + +@end diff --git a/TPEventsController.m b/TPEventsController.m new file mode 100644 index 0000000..ed5430a --- /dev/null +++ b/TPEventsController.m @@ -0,0 +1,272 @@ +// +// TPEventsController.m +// teleport +// +// Created by JuL on 09/11/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import "TPEventsController.h" + +#import "TPRemoteOperationsController.h" +#import "TPDirectEventTapsController.h" +#import "TPEventTapsController.h" +#import "TPPreferencesManager.h" + +#import "TPRemoteHost.h" +#import "TPLocalHost.h" + +#define THREADED_EVENT_POSTING 0 + +@implementation TPEventsController + ++ (TPEventsController*)defaultController +{ + if([[TPLocalHost localHost] hasCapability:TPHostEventTapsCapability]) + return [TPEventTapsController defaultController]; + else + return [TPRemoteOperationsController defaultController]; +} + ++ (TPEventsController*)eventsControllerForRemoteHost:(TPRemoteHost*)remoteHost +{ + if([[TPLocalHost localHost] pairWithHost:remoteHost hasCapability:TPHostDirectEventTapsCapability]) { + SInt32 localMajorVersion = [[TPLocalHost localHost] osVersion] & 0xFFF0; + SInt32 remoteMajorVersion = [remoteHost osVersion] & 0xFFF0; + // only allow direct event taps between same major OS versions + if(localMajorVersion == remoteMajorVersion) { + DebugLog(@"Using TPDirectEventTapsController"); + return [TPDirectEventTapsController defaultController]; + } + else { + DebugLog(@"Using TPEventTapsController"); + return [TPEventTapsController defaultController]; + } + } + else if([[TPLocalHost localHost] pairWithHost:remoteHost hasCapability:TPHostEventTapsCapability]) { + DebugLog(@"Using TPEventTapsController"); + return [TPEventTapsController defaultController]; + } + else { + DebugLog(@"Using TPRemoteOperationsController"); + return [TPRemoteOperationsController defaultController]; + } +} + +- (void)startGettingEventsForListener:(id)eventsListener onScreen:(NSScreen*)screen +{ + _lastPostedEventDate = 0.0; + _eventsListener = eventsListener; + + [self _startGettingEventsOnScreen:screen]; +} + +- (void)_sendEventToListener:(id)event +{ + id eventsListener = [self eventsListener]; + + if((_lastPostedEventDate == 0.0) && [self _shouldSkipFirstEvent:event]) { + DebugLog(@"skipping first event"); + } + else if(eventsListener != nil) { + if([self _isEmergencyStopEvent:event]) { + NSLog(@"emergency stop key sequence detected, disconnecting."); + [eventsListener gotEmergencyStopEvent]; + } + else { + NSEvent * nsEvent = [self _nsEventFromEvent:event]; + if(![eventsListener shouldStopWithEvent:nsEvent]) { + NSData * eventData = [self _eventDataFromEvent:event]; + [eventsListener gotEventWithEventData:eventData]; + } + } + } + + _lastPostedEventDate = [NSDate timeIntervalSinceReferenceDate]; +} + +- (void)stopGettingEvents +{ + [self _stopGettingEvents]; + _eventsListener = nil; +} + +- (void)cleanupGettingEvents +{ + +} + +- (id)eventsListener +{ + return _eventsListener; +} + +- (void)_startGettingEventsOnScreen:(NSScreen*)screen +{ + // overloaded +} + +- (void)_stopGettingEvents +{ + // overloaded +} + +- (void)startPostingEvents +{ + _lastPostedEventDate = 0.0; +} + +- (void)stopPostingEvents +{ + +} + +- (void)_threadedPostEventWithEventData:(NSData*)eventData +{ + @autoreleasepool { + [self _postEventWithEventData:eventData]; + } +} + +- (void)postEventWithEventData:(NSData*)eventData +{ + NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; + + if (now - _lastPostedEventDate > 60.0) { // if last event occurred more than a minute ago + [[TPLocalHost localHost] wakeUpScreen]; + } + + _lastPostedEventDate = now; + +#if THREADED_EVENT_POSTING + [NSThread detachNewThreadSelector:@selector(_threadedPostEventWithEventData:) toTarget:self withObject:eventData]; +#else + [self _postEventWithEventData:eventData]; +#endif +} + +- (void)_postEventWithEventData:(NSData*)eventData +{ + // overloaded +} + +- (void)warpMouseToPosition:(NSPoint)position +{ + _currentMouseLocation = *(CGPoint*)&position; +} + +- (void)mouseDownAtPosition:(NSPoint)position +{ + _currentMouseLocation = *(CGPoint*)&position; +} + +- (void)mouseUpAtPosition:(NSPoint)position +{ + _currentMouseLocation = *(CGPoint*)&position; +} + +- (BOOL)_shouldSkipFirstEvent:(id)event +{ + return NO; +} + +- (BOOL)_isEmergencyStopEvent:(id)event +{ + return NO; +} + +- (NSEvent*)_nsEventFromEvent:(id)event +{ + return (NSEvent*)event; +} + +- (NSData*)_eventDataFromEvent:(id)event +{ + return nil; +} + +- (void)_updateMouseLocationWithMouseDelta:(TPMouseDelta)mouseDelta +{ + NSArray * screens = [[TPLocalHost localHost] screens]; + NSRect mainScreenRect = [screens[0] frame]; + + _currentMouseLocation = [NSEvent mouseLocation]; + _currentMouseLocation.x += mouseDelta.x; + _currentMouseLocation.y = (NSHeight(mainScreenRect) - _currentMouseLocation.y); + _currentMouseLocation.y += mouseDelta.y; + + NSEnumerator * screenEnum = [screens objectEnumerator]; + NSScreen * screen; + unsigned i = 0; + + while((screen = [screenEnum nextObject])) { + NSRect screenFrame = [screen frame]; + // DebugLog(@"point=(%d,%d), screenFrame=%@", point.x, point.y, NSStringFromRect(screenFrame)); + CGPoint relativePoint = _currentMouseLocation; + relativePoint.x -= NSMinX(screenFrame); + relativePoint.y -= (NSHeight(mainScreenRect) - NSMaxY(screenFrame)); + + if(relativePoint.x >= 0 && relativePoint.y >= 0 && relativePoint.x < NSWidth(screenFrame) && relativePoint.y < NSHeight(screenFrame)) { + _currentScreenIndex = i; + return; + } + i++; + } + + /* Mouse cursor is outside */ + screen = screens[_currentScreenIndex]; + NSRect screenFrame = [screen frame]; + CGPoint relativePoint = _currentMouseLocation; + relativePoint.x -= NSMinX(screenFrame); + relativePoint.y -= (NSHeight(mainScreenRect) - NSMaxY(screenFrame)); + + relativePoint.x = MAX(0, MIN(relativePoint.x, NSWidth(screenFrame) - 1.0)); + relativePoint.y = MAX(0, MIN(relativePoint.y, NSHeight(screenFrame) - 1.0)); + + _currentMouseLocation = relativePoint; + _currentMouseLocation.x += NSMinX(screenFrame); + _currentMouseLocation.y += (NSHeight(mainScreenRect) - NSMaxY(screenFrame)); +} + +- (BOOL)event:(NSEvent*)event hasRequiredKeyIfNeeded:(BOOL)needed withTag:(NSEventType)tag +{ + if(!needed) + return YES; + + unsigned int keyMask = NSEventMaskFromType(tag); + DebugLog(@"keyMask: %d modifiersFlags: %d", keyMask, (int)[event modifierFlags]); + if(([event modifierFlags] & keyMask) != 0) + return YES; + + NSEventModifierFlags flags = [NSEvent modifierFlags]; + DebugLog(@"flags: %lu", flags); + switch(tag) { + case COMMAND_KEY_TAG: + if((flags & NSCommandKeyMask) != 0) + return YES; + break; + case ALT_KEY_TAG: + if((flags & NSAlternateKeyMask) != 0) + return YES; + break; + case CTRL_KEY_TAG: + if((flags & NSControlKeyMask) != 0) + return YES; + break; + case SHIFT_KEY_TAG: + if((flags & NSShiftKeyMask) != 0) + return YES; + break; + case CAPSLOCK_KEY_TAG: + if((flags & NSAlphaShiftKeyMask) != 0) + return YES; + break; + default: + break; + + } + + return NO; +} + +@end diff --git a/TPEventsReceiver.h b/TPEventsReceiver.h new file mode 100644 index 0000000..de097e0 --- /dev/null +++ b/TPEventsReceiver.h @@ -0,0 +1,27 @@ +// +// TPEventsReceiver.h +// Teleport +// +// Created by JuL on Thu Dec 04 2003. +// Copyright (c) 2003 abyssoft. All rights reserved. +// + +#import +#import "TPNetworkConnection.h" + +@class TPUDPSocket, TPEventsPlayer; + +@interface TPEventsReceiver : TPCommunicationHandler +{ + //TPUDPSocket * listenSocket; + TPEventsPlayer * eventsPlayer; +} + +- init; + +- (BOOL)listenOnPort:(int)port; +- (void)stopListening; + +- (void)disconnect; + +@end diff --git a/TPEventsReceiver.m b/TPEventsReceiver.m new file mode 100644 index 0000000..988ba14 --- /dev/null +++ b/TPEventsReceiver.m @@ -0,0 +1,47 @@ +// +// TPEventsReceiver.m +// Teleport +// +// Created by JuL on Thu Dec 04 2003. +// Copyright (c) 2003 abyssoft. All rights reserved. +// + +#import "TPEventsReceiver.h" +#import "TPUDPSocket.h" +#import "TPEventsPlayer.h" +#import "common.h" + +@implementation TPEventsReceiver + +- init +{ + self = [super init]; + //udpSocket = nil; + return self; +} + +- (BOOL)listenOnPort:(int)port +{ + udpSocket = [TPUDPSocket UDPSocketListeningOnPort:port]; + if(!udpSocket) + return FALSE; + [udpSocket retain]; + [udpSocket setDelegate:self]; + [self connect]; + //NSLog(@"listening"); + return TRUE; +} + +- (void)stopListening +{ + [super disconnect]; +} + +- (void)disconnect +{ + //[self sendMessage:[TPMessage messageWithType:STOP_CONTROLLING]]; + [self stopListening]; + [super disconnect]; +} + +@end diff --git a/TPEventsTransmitter.h b/TPEventsTransmitter.h new file mode 100644 index 0000000..23ce4f0 --- /dev/null +++ b/TPEventsTransmitter.h @@ -0,0 +1,18 @@ +// +// TPEventsTransmitter.h +// Teleport +// +// Created by JuL on Thu Dec 04 2003. +// Copyright (c) 2003 abyssoft. All rights reserved. +// + +#import +#import "TPCommunicationHandler.h" + +@interface TPEventsTransmitter : TPCommunicationHandler +{ +} + +- (BOOL)connectToHost:(NSString*)host onPort:(int)port; + +@end diff --git a/TPEventsTransmitter.m b/TPEventsTransmitter.m new file mode 100644 index 0000000..8bad2ca --- /dev/null +++ b/TPEventsTransmitter.m @@ -0,0 +1,28 @@ +// +// TPEventsTransmitter.m +// Teleport +// +// Created by JuL on Thu Dec 04 2003. +// Copyright (c) 2003 abyssoft. All rights reserved. +// + +#import "TPEventsTransmitter.h" +#import "TPMasterController.h" +#import "TPUDPSocket.h" +#import "TPMessage.h" +#import "common.h" +#include + +@implementation TPEventsTransmitter + +- (BOOL)connectToHost:(NSString*)host onPort:(int)port +{ + [super connect]; + udpSocket = [[TPUDPSocket UDPSocketConnectedToHost:host port:port] retain]; + if(!udpSocket) + return FALSE; + [udpSocket setDelegate:self]; + return TRUE; +} + +@end diff --git a/TPFakeHostsGenerator.h b/TPFakeHostsGenerator.h new file mode 100644 index 0000000..2be81cb --- /dev/null +++ b/TPFakeHostsGenerator.h @@ -0,0 +1,20 @@ +// +// TPFakeHostsGenerator.h +// teleport +// +// Created by Julien Robert on 01/05/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import + + +@interface TPFakeHostsGenerator : NSObject +{ + NSTimer * _timer; + NSMutableArray * _hosts; +} + +- (void)run; + +@end diff --git a/TPFakeHostsGenerator.m b/TPFakeHostsGenerator.m new file mode 100644 index 0000000..d527c9f --- /dev/null +++ b/TPFakeHostsGenerator.m @@ -0,0 +1,101 @@ +// +// TPFakeHostsGenerator.m +// teleport +// +// Created by Julien Robert on 01/05/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "TPFakeHostsGenerator.h" +#import "TPHostsManager.h" + +#define SCREEN_SIZES (sizeof(TPScreenSizes) / sizeof(NSSize)) + +static NSSize TPScreenSizes[] = { + {640.0, 480.0}, + {800.0, 600.0}, + {1024.0, 768.0}, + {1280.0, 1024.0}, + {1600.0, 1200.0}, + {1680.0, 1050.0}, + {1920.0, 1200.0}, +}; + +@implementation TPFakeHostsGenerator + +- (instancetype) init +{ + self = [super init]; + + _hosts = [[NSMutableArray alloc] init]; + + return self; +} + + +- (void)_addFakeHost +{ + static int number = 1; + TPRemoteHost * host = [[TPRemoteHost alloc] initWithIdentifier:[[NSProcessInfo processInfo] globallyUniqueString] address:[NSString stringWithFormat:@"%ld:%ld:%ld:%ld", random()%255, random()%255, random()%255, random()%255] port:44186]; + + [host setCapabilities:TPHostEncryptionCapability]; + + NSRect screenFrame = NSZeroRect; + screenFrame.size = TPScreenSizes[random() % SCREEN_SIZES]; + + TPScreen * screen = [[TPScreen alloc] init]; + [screen setFrame:screenFrame]; + NSArray * screens = @[screen]; + [host setScreens:screens]; + + NSString * computerName = [NSString stringWithFormat:@"Fake Mac %d", number++]; + [host setComputerName:computerName]; + + [host setHostState:TPHostSharedState]; + + [[TPHostsManager defaultManager] addBonjourHost:host]; + [_hosts addObject:host]; + + +// if(number == 10) { +// [_timer invalidate]; +// } +} + +- (void)_removeRandomFakeHost +{ + if([_hosts count] > 0) { + TPRemoteHost * host = _hosts[random() % [_hosts count]]; + [[TPHostsManager defaultManager] removeBonjourHost:host]; + [_hosts removeObject:host]; + } +} + +- (void)run +{ +#if 1 + int i; + for(i=0; i<10; i++) { + [self _addFakeHost]; + } +#else + _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(_addOrRemoveHost) userInfo:nil repeats:YES]; +#endif +} + +- (void)_addOrRemoveHost +{ +#if 0 + [self _addFakeHost]; +#else + if(random() % 2 == 0) { + [self _removeRandomFakeHost]; + } + else { + [self _addFakeHost]; + } +#endif +} + + +@end diff --git a/TPFileSerialization.h b/TPFileSerialization.h new file mode 100644 index 0000000..f398656 --- /dev/null +++ b/TPFileSerialization.h @@ -0,0 +1,55 @@ +// +// TPFileSerialization.h +// Streams +// +// Created by Julien Robert on 09/10/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import + +extern NSString * TPFileArchiverNameKey; +extern NSString * TPFileArchiverTypeKey; + +@class TPFileArchiver; + +@protocol TPFileArchiverDelegate + +- (void)fileArchiver:(TPFileArchiver*)archiver hasDataAvailable:(NSData*)data; +- (void)fileArchiverDidCompleteReading:(TPFileArchiver*)archiver; + +@end + +@interface TPFileArchiver : NSObject +{ + id _delegate; + NSArray * _paths; + NSTask * _readTask; + NSPipe * _readPipe; + NSFileHandle * _readFileHandle; +} + +- (instancetype) initForReadingFilesAtPaths:(NSArray*)paths delegate:(id)delegate NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readonly, copy) NSArray *representedFiles; + +- (void)startReading; +- (void)continueReading; + +@end + +@interface TPFileUnarchiver : NSObject +{ + NSTask * _writeTask; + NSPipe * _writePipe; + NSFileHandle * _writeFileHandle; +} + +- (instancetype) initForWritingAtPath:(NSString*)path NS_DESIGNATED_INITIALIZER; + +- (void)startWriting; +- (void)close; + +- (void)writeData:(NSData*)data; + +@end diff --git a/TPFileSerialization.m b/TPFileSerialization.m new file mode 100644 index 0000000..31705a7 --- /dev/null +++ b/TPFileSerialization.m @@ -0,0 +1,141 @@ +// +// TPFileSerialization.m +// Streams +// +// Created by Julien Robert on 09/10/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import "TPFileSerialization.h" + +NSString * TPFileArchiverNameKey = @"TPFileArchiverName"; +NSString * TPFileArchiverTypeKey = @"TPFileArchiverType"; + +@implementation TPFileArchiver + ++ (NSDictionary*)environment +{ + return @{@"LANG": @"en_US.UTF-8", + @"LC_CTYPE": @"en_US.UTF-8"}; +} + +- (instancetype) initForReadingFilesAtPaths:(NSArray*)paths delegate:(id)delegate +{ + self = [super init]; + + _delegate = delegate; + _paths = paths; + + _readPipe = [[NSPipe alloc] init]; + + _readTask = [[NSTask alloc] init]; + [_readTask setLaunchPath:@"/usr/bin/tar"]; + + NSMutableArray * arguments = [NSMutableArray arrayWithObjects:@"cz", @"-", nil]; + NSEnumerator * pathsEnum = [paths objectEnumerator]; + NSString * commonParentPath = nil; + NSString * path; + + while((path = [pathsEnum nextObject]) != nil) { + if(commonParentPath == nil) { + commonParentPath = [path stringByDeletingLastPathComponent]; + } + else if(![commonParentPath isEqual:[path stringByDeletingLastPathComponent]]) { + continue; + } + + [arguments addObject:[path lastPathComponent]]; + } + + [_readTask setArguments:arguments]; + [_readTask setEnvironment:[TPFileArchiver environment]]; + [_readTask setCurrentDirectoryPath:commonParentPath]; + [_readTask setStandardOutput:_readPipe]; + + _readFileHandle = [_readPipe fileHandleForReading]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fileHandleDidReadData:) name:NSFileHandleReadCompletionNotification object:_readFileHandle]; + + return self; +} + + +- (NSArray*)representedFiles +{ + NSMutableArray * representedFiles = [NSMutableArray array]; + + NSEnumerator * pathsEnum = [_paths objectEnumerator]; + NSString * path; + + while((path = [pathsEnum nextObject]) != nil) { + NSDictionary * fileDict = [[NSDictionary alloc] initWithObjectsAndKeys: + [path lastPathComponent], TPFileArchiverNameKey, + [[NSWorkspace sharedWorkspace] typeOfFile:path error:NULL], TPFileArchiverTypeKey, + nil]; + [representedFiles addObject:fileDict]; + } + + return representedFiles; +} + +- (void)startReading +{ + [_readTask launch]; + [_readFileHandle readInBackgroundAndNotify]; +} + +- (void)continueReading +{ + [_readFileHandle readInBackgroundAndNotify]; +} + +- (void)fileHandleDidReadData:(NSNotification*)notification +{ + NSData * data = [notification userInfo][NSFileHandleNotificationDataItem]; + if([data length] > 0) { + [_delegate fileArchiver:self hasDataAvailable:data]; + } + else { + [_delegate fileArchiverDidCompleteReading:self]; + } +} + +@end + +@implementation TPFileUnarchiver + +- (instancetype) initForWritingAtPath:(NSString*)path +{ + self = [super init]; + + _writePipe = [[NSPipe alloc] init]; + + _writeTask = [[NSTask alloc] init]; + [_writeTask setLaunchPath:@"/usr/bin/tar"]; + [_writeTask setArguments:@[@"xz", @"-C", path]]; + [_writeTask setEnvironment:[TPFileArchiver environment]]; + [_writeTask setStandardInput:_writePipe]; + + _writeFileHandle = [_writePipe fileHandleForWriting]; + + return self; +} + + +- (void)startWriting +{ + [_writeTask launch]; +} + +- (void)close +{ + [_writeFileHandle closeFile]; + [_writeTask waitUntilExit]; +} + +- (void)writeData:(NSData*)data +{ + [_writeFileHandle writeData:data]; +} + +@end \ No newline at end of file diff --git a/TPFileTransfer.h b/TPFileTransfer.h new file mode 100644 index 0000000..22dd35d --- /dev/null +++ b/TPFileTransfer.h @@ -0,0 +1,44 @@ +// +// TPFileTransfer.h +// teleport +// +// Created by JuL on 14/02/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import +#import + +#import "TPTransfer.h" +#import "TPFileSerialization.h" + +typedef NS_ENUM(NSInteger, TPFileTransferPhase) { + TPFileTransferPromisePhase, + TPFileTransferDataPhase +} ; + +@class NSFilePromiseDragSource; + +@interface TPOutgoingFileTransfer : TPOutgoingTransfer +{ + TPFileArchiver * _archiver; + NSImage * _dragImage; + NSPoint _dragImageLocation; +} + +- (void)setFilePaths:(NSArray*)filePaths dragImage:(NSImage*)image location:(NSPoint)point; + +@end + +@interface TPIncomingFileTransfer : TPIncomingTransfer +{ + NSArray * _representedFiles; + NSImage * _dragImage; + NSPoint _dragImageLocation; + NSMutableDictionary * _destinationPaths; + float _progress; + TPFileUnarchiver * _unarchiver; +} + +@end + diff --git a/TPFileTransfer.m b/TPFileTransfer.m new file mode 100644 index 0000000..f28357c --- /dev/null +++ b/TPFileTransfer.m @@ -0,0 +1,504 @@ +// +// TPFileTransfer.m +// teleport +// +// Created by JuL on 14/02/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import "TPTransfer_Private.h" +#import "TPFileTransfer.h" + +#import "TPMainController.h" +#import "TPEventsController.h" +#import "TPConnectionController.h" +#import "TPNetworkConnection.h" +#import "TPTCPSecureSocket.h" +#import "TPHotBorder.h" +#import "TPMessage.h" + +NSString * TPFileTransferRepresentedFilesKey = @"TPFileTransferRepresentedFiles"; +NSString * TPFileTransferDragImageDataKey = @"TPFileTransferDragImageData"; +NSString * TPFileTransferDragImageLocationKey = @"TPFileTransferDragImageLocation"; + +@interface TPIncomingFileTransfer (Internal) + +- (NSArray*)_dragEndedAtPath:(NSString*)path; + +@end + +@implementation TPOutgoingFileTransfer + + +- (NSString*)type +{ + return @"TPIncomingFileTransfer"; +} + +- (BOOL)shouldBeEncrypted +{ + return YES; +} + +- (BOOL)requireTrustedHost +{ + return YES; +} + +- (TPTransferPriority)priority +{ + return TPTransferMediumPriority; +} + +- (BOOL)hasFeedback +{ + return YES; +} + +- (NSString*)completionMessage +{ + NSString * sizeString = [NSString sizeStringForSize:[self totalDataLength]]; + return [NSString stringWithFormat:NSLocalizedString(@"File sent (%@)", nil), sizeString]; +} + +- (NSString*)errorMessage +{ + return NSLocalizedString(@"File not sent", nil); +} + +- (void)setFilePaths:(NSArray*)filePaths dragImage:(NSImage*)image location:(NSPoint)point +{ + _archiver = [[TPFileArchiver alloc] initForReadingFilesAtPaths:filePaths delegate:self]; + + if(image != nil) { + _dragImage = image; + _dragImageLocation = point; + } +} + +- (NSDictionary*)infoDict +{ + NSMutableDictionary * infoDict = [[NSMutableDictionary alloc] initWithDictionary:[super infoDict]]; + + infoDict[TPFileTransferRepresentedFilesKey] = [_archiver representedFiles]; + + if(_dragImage != nil) { + NSData * dragImageData = [_dragImage TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:1.0]; + infoDict[TPFileTransferDragImageDataKey] = dragImageData; + infoDict[TPFileTransferDragImageLocationKey] = NSStringFromPoint(_dragImageLocation); + } + + return infoDict; +} + +- (void)_beginTransfer +{ + [super _beginTransfer]; + [_manager transfer:self didProgress:-1.0]; + [_archiver startReading]; +} + +- (void)fileArchiver:(TPFileArchiver*)archiver hasDataAvailable:(NSData*)data +{ + DebugLog(@"sending data: %d", (int)[data length]); + _totalDataLength += [data length]; + [_socket sendData:data]; +} + +- (void)tcpSocketDidSendData:(TPTCPSocket*)tcpSocket +{ + DebugLog(@"did send"); + [_archiver continueReading]; +} + +- (void)fileArchiverDidCompleteReading:(TPFileArchiver*)archiver +{ + DebugLog(@"fileArchiverDidCompleteReading: %@", archiver); + [self _senderDataTransferCompleted]; +} + +@end + +#pragma mark - + +@interface TPIncomingFileTransfer () + +@property (nonatomic, weak) TPHotBorder * hotBorder; + +@end + +@implementation TPIncomingFileTransfer + +- (BOOL)shouldBeEncrypted +{ + return YES; +} + +- (BOOL)requireTrustedHost +{ + return YES; +} + +- (TPTransferPriority)priority +{ + return TPTransferMediumPriority; +} + +- (BOOL)hasFeedback +{ + return YES; +} + +- (NSString*)completionMessage +{ + NSString * sizeString = [NSString sizeStringForSize:_totalDataLength]; + return [NSString stringWithFormat:NSLocalizedString(@"File received (%@)", nil), sizeString]; +} + +- (NSString*)errorMessage +{ + return NSLocalizedString(@"File not received", nil); +} + +- (NSString*)_temporaryDestinationPath +{ + return [[NSTemporaryDirectory() stringByAppendingPathComponent:@"teleport-transfers"] stringByAppendingPathComponent:[self uid]]; +} + +#define THREADED_DRAG 0 +- (BOOL)prepareToReceiveDataWithInfoDict:(NSDictionary*)infoDict fromHost:(TPRemoteHost*)host onPort:(int*)port delegate:(id)delegate +{ + _representedFiles = infoDict[TPFileTransferRepresentedFilesKey]; + + NSData * dragImageData = infoDict[TPFileTransferDragImageDataKey]; + if(dragImageData != nil) { + _dragImage = [[NSImage alloc] initWithData:dragImageData]; + _dragImageLocation = NSPointFromString(infoDict[TPFileTransferDragImageLocationKey]); + } + + NSString * temporaryDestinationPath = [self _temporaryDestinationPath]; + + if(![[NSFileManager defaultManager] fileExistsAtPath:temporaryDestinationPath isDirectory:NULL]) { + if(![[NSFileManager defaultManager] createDirectoryAtPath:temporaryDestinationPath withIntermediateDirectories:YES attributes:nil error:NULL]) { + return NO; + } + } + + _unarchiver = [[TPFileUnarchiver alloc] initForWritingAtPath:temporaryDestinationPath]; + [_unarchiver startWriting]; + + if([delegate respondsToSelector:@selector(currentHotBorder)]) { + TPHotBorder * hotBorder = [delegate currentHotBorder]; + if(hotBorder == nil) { + DebugLog(@"currentHotBorder is nil!"); + return NO; + } + + if([delegate respondsToSelector:@selector(eventsController)]) { + TPEventsController * eventsController = [delegate eventsController]; + if(eventsController == nil) { + DebugLog(@"eventsController is nil!"); + return NO; + } + + [hotBorder setOpaqueToMouseEvents:YES]; + + self.hotBorder = hotBorder; + + NSPoint currentPoint = [NSEvent mouseLocation]; + NSPoint centerPoint = [hotBorder convertBaseToScreen:NSMakePoint(NSMidX([[hotBorder contentView] bounds]), NSMidY([[hotBorder contentView] bounds]))]; + DebugLog(@"current=%@ center=%@", NSStringFromPoint(currentPoint), NSStringFromPoint(centerPoint)); + centerPoint.y = NSMaxY([[hotBorder screen] frame]) - centerPoint.y; + currentPoint.y = NSMaxY([[hotBorder screen] frame]) - currentPoint.y; + + [eventsController mouseDownAtPosition:centerPoint]; + [eventsController warpMouseToPosition:currentPoint]; + // CGPostMouseEvent(*(CGPoint*)¢erPoint, TRUE, 1, YES); + // CGPostMouseEvent(*(CGPoint*)¤tPoint, TRUE, 1, YES); + + + DebugLog(@"Start drag of files: %@", _representedFiles); + +#if THREADED_DRAG + [NSThread detachNewThreadSelector:@selector(startDragUsingHotBorder:) toTarget:self withObject:hotBorder]; +#else + [self performSelector:@selector(startDragUsingHotBorder:) withObject:hotBorder]; +#endif + + return [super prepareToReceiveDataWithInfoDict:infoDict fromHost:host onPort:port delegate:delegate]; + } + else { + DebugLog(@"delegate does not responds to eventsController!"); + return NO; + } + } + else { + DebugLog(@"delegate does not responds to currentHotBorder!"); + return NO; + } +} + +- (void)startDragUsingHotBorder:(TPHotBorder*)hotBorder +{ + @autoreleasepool { + NSPasteboard * pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + + NSMutableArray * fileTypes = [[NSMutableArray alloc] init]; + NSEnumerator * representedFilesEnum = [_representedFiles objectEnumerator]; + NSDictionary * representedFile; + while((representedFile = [representedFilesEnum nextObject])) { + NSString * fileType = representedFile[TPFileArchiverTypeKey]; + [fileTypes addObject:fileType]; + } + + NSImage * image; + NSSize offset = NSZeroSize; + if(_dragImage == nil) { + if([_representedFiles count] == 1) { + NSString * fileType = fileTypes[0]; + image = [[[NSWorkspace sharedWorkspace] iconForFileType:fileType] copy]; + [image setScalesWhenResized:YES]; + [image setSize:NSMakeSize(64, 64)]; + } + else { + image = [[NSImage imageNamed:@"MultipleFiles.icns"] copy]; + [image setScalesWhenResized:YES]; + [image setSize:NSMakeSize(64, 64)]; + } + } + else { + image = _dragImage; + offset.width = _dragImageLocation.x; + offset.height = _dragImageLocation.y; + } + + NSPoint point = [hotBorder mouseLocationOutsideOfEventStream]; + NSEvent * fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDown location:point modifierFlags:0 timestamp:0 windowNumber:[hotBorder windowNumber] context:[NSGraphicsContext currentContext] eventNumber:0 clickCount:1 pressure:0.0]; + + [[hotBorder contentView] dragPromisedFilesOfTypes:fileTypes fromRect:NSMakeRect(point.x - 16.0, point.y - 16.0, 0.0, 0.0) source:self slideBack:NO event:fakeEvent]; + } +} + +#if LEGACY_BUILD +- (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)isLocal +#else +- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal +#endif +{ + return NSDragOperationCopy; +} + +- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination +{ + DebugLog(@"Drop at location: %@", dropDestination); + + return [self _dragEndedAtPath:[dropDestination path]]; +} + +- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation +{ + if(operation == NSDragOperationNone) { + DebugLog(@"Drop aborted"); + [self _dragEndedAtPath:nil]; + } +} + +- (NSString*)_filePathFromDestinationPath:(NSString*)destinationPath andFileName:(NSString*)fileName +{ + NSString * filePath = [destinationPath stringByAppendingPathComponent:fileName]; + + if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { + NSAlert * alert = [NSAlert alertWithMessageText:NSLocalizedString(@"A file already exists with this name.", @"Title of the dialog that appears when dragging to a file that already exists in the Finder") defaultButton:NSLocalizedString(@"Unique", @"Unique button in dialog, to make a unique name of an existing filename") alternateButton:NSLocalizedString(@"Cancel", @"Generic cancel button in dialog") otherButton:NSLocalizedString(@"Replace", @"Replace button to replace an existing file.") informativeTextWithFormat:NSLocalizedString(@"A file named \\U201C%@\\U201D already exists in \\U201C%@\\U201D. You can choose to use a unique name for the new file, replace the old file, or cancel the drop.", @"Explanation text for the dialog that appears when dragging a file to a location where a file with the same name already exists."), fileName, [destinationPath lastPathComponent]]; + + int returnCode = [(TPMainController*)[NSApp delegate] presentAlert:alert]; + switch(returnCode) { + case NSAlertDefaultReturn: + { + NSString * name = [fileName stringByDeletingPathExtension]; + NSString * extension = [fileName pathExtension]; + int count = 1; + do { + fileName = [name stringByAppendingFormat:@" %d", count++]; + if([extension length] > 0) + fileName = [fileName stringByAppendingPathExtension:extension]; + filePath = [destinationPath stringByAppendingPathComponent:fileName]; + } while([[NSFileManager defaultManager] fileExistsAtPath:filePath]); + break; + } + case NSAlertAlternateReturn: + return nil; + case NSAlertOtherReturn: + default: + if(![[NSFileManager defaultManager] removeFileAtPath:filePath handler:nil]) { + DebugLog(@"couldn't remove file at %@", filePath); + NSBeep(); + return nil; + } + } + } + + return filePath; +} + +- (void)_putItemsInPlace +{ + NSString * tempDestinationPath = [self _temporaryDestinationPath]; + NSEnumerator * representedFilesEnum = [_representedFiles objectEnumerator]; + NSDictionary * representedFile; + while((representedFile = [representedFilesEnum nextObject])) { + NSString * filename = representedFile[TPFileArchiverNameKey]; + NSString * destPath = _destinationPaths[filename]; + + if(destPath == nil) continue; + + NSString * tempPath = [tempDestinationPath stringByAppendingPathComponent:filename]; + DebugLog(@"move %@ to %@", tempPath, destPath); + [[NSFileManager defaultManager] removeItemAtPath:destPath error:NULL]; // remove placeholder + [[NSFileManager defaultManager] moveItemAtPath:tempPath toPath:destPath error:NULL]; + } + + [[NSFileManager defaultManager] removeItemAtPath:tempDestinationPath error:NULL]; // remove temp folder +} + +- (BOOL)_completeTransferIfPossible +{ + DebugLog(@"_completeTransferIfPossible"); + if(_destinationPaths != nil && _unarchiver == nil) { + DebugLog(@"YUP!"); + [NSThread detachNewThreadSelector:@selector(_putItemsInPlace) toTarget:self withObject:nil]; + return YES; + } + else { + return NO; + } +} + +- (NSArray*)_dragEndedAtPath:(NSString*)destinationPath +{ + if(destinationPath == nil) { + [self _fileDropped]; + [self _receiverDataTransferAborted]; // cancel transfer if drop aborted + return nil; + } + + DebugLog(@"drag ended at %@", destinationPath); + _destinationPaths = [[NSMutableDictionary alloc] init]; + + NSMutableArray * filenames = [NSMutableArray array]; + NSEnumerator * representedFilesEnum = [_representedFiles objectEnumerator]; + NSDictionary * representedFile; + while((representedFile = [representedFilesEnum nextObject])) { + NSString * filename = representedFile[TPFileArchiverNameKey]; + NSString * path = [self _filePathFromDestinationPath:destinationPath andFileName:filename]; + + if(path != nil) { + _destinationPaths[filename] = path; + [filenames addObject:[path lastPathComponent]]; + } + else { + [_destinationPaths removeObjectForKey:filename]; + } + } + + if([_destinationPaths count] == 0) { + _destinationPaths = nil; + } + + DebugLog(@"destinationPaths: %@", _destinationPaths); + + // If transfer is not complete yet, create placeholder files + if(![self _completeTransferIfPossible]) { + NSEnumerator * representedFilesEnum = [_representedFiles objectEnumerator]; + NSDictionary * representedFile; + while((representedFile = [representedFilesEnum nextObject])) { + NSString * filename = representedFile[TPFileArchiverNameKey]; + NSString * fileType = representedFile[TPFileArchiverTypeKey]; + NSString * path = _destinationPaths[filename]; + + if(path == nil) continue; + + DebugLog(@"create placeholder at: %@", path); + + [[NSFileManager defaultManager] createFileAtPath:path contents:[NSData data] attributes:nil]; + + NSImage * tempImage = [[NSImage alloc] initWithSize:NSMakeSize(128, 128)]; + NSImage * haloImage = [NSImage imageNamed:@"halo.png"]; + NSImage * fileTypeImage = [[[NSWorkspace sharedWorkspace] iconForFileType:fileType] copy]; + [fileTypeImage setSize:NSMakeSize(128, 128)]; + + NSSize haloSize = [haloImage size]; + float halfHaloHeight = floorf(haloSize.height / 2.0); + + // dim icon + [fileTypeImage lockFocus]; + [[NSColor colorWithCalibratedWhite:1.0 alpha:0.5] set]; + NSRectFillUsingOperation(NSMakeRect(0, 0, 128, 128), NSCompositeSourceAtop); + [fileTypeImage unlockFocus]; + + [tempImage lockFocus]; + + // draw halos + NSRect haloFromRect = NSMakeRect(0.0, halfHaloHeight, haloSize.width, haloSize.height - halfHaloHeight); + NSRect haloRect = NSMakeRect(0.0, 0.0, haloSize.width, haloSize.height - halfHaloHeight); + + haloRect.origin.y = 32.0; + [haloImage drawInRect:haloRect fromRect:haloFromRect operation:NSCompositeSourceOver fraction:0.8]; + haloRect.origin.y = 42.0; + [haloImage drawInRect:haloRect fromRect:haloFromRect operation:NSCompositeSourceOver fraction:0.6]; + haloRect.origin.y = 64.0; + [haloImage drawInRect:haloRect fromRect:haloFromRect operation:NSCompositeSourceOver fraction:0.4]; + haloRect.origin.y = 96.0; + [haloImage drawInRect:haloRect fromRect:haloFromRect operation:NSCompositeSourceOver fraction:0.2]; + + [fileTypeImage drawInRect:NSMakeRect(0, 0, 128, 128) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + + haloFromRect.origin.y = 0.0; + haloFromRect.size.height = halfHaloHeight; + haloRect.size.height = halfHaloHeight; + + haloRect.origin.y = 32.0 - halfHaloHeight; + [haloImage drawInRect:haloRect fromRect:haloFromRect operation:NSCompositeSourceOver fraction:0.8]; + haloRect.origin.y = 42.0 - halfHaloHeight; + [haloImage drawInRect:haloRect fromRect:haloFromRect operation:NSCompositeSourceOver fraction:0.6]; + haloRect.origin.y = 64.0 - halfHaloHeight; + [haloImage drawInRect:haloRect fromRect:haloFromRect operation:NSCompositeSourceOver fraction:0.4]; + haloRect.origin.y = 96.0 - halfHaloHeight; + [haloImage drawInRect:haloRect fromRect:haloFromRect operation:NSCompositeSourceOver fraction:0.2]; + + [tempImage unlockFocus]; + + [[NSWorkspace sharedWorkspace] setIcon:tempImage forFile:path options:NSExcludeQuickDrawElementsIconCreationOption]; + } + } + + [self _fileDropped]; + + return filenames; +} + +- (void)_fileDropped +{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self.hotBorder setOpaqueToMouseEvents:NO]; + self.hotBorder = nil; + }); +} + +- (void)_receivedData:(NSData*)data +{ + DebugLog(@"received data %ld", [data length]); + _totalDataLength += [data length]; + [_unarchiver writeData:data]; +} + +- (void)_receiverDataTransferCompleted +{ + DebugLog(@"file transfer completed"); + [_unarchiver close]; + _unarchiver = nil; + [self _completeTransferIfPossible]; + [super _receiverDataTransferCompleted]; +} + +@end + diff --git a/TPHost.h b/TPHost.h new file mode 100644 index 0000000..c34fbf0 --- /dev/null +++ b/TPHost.h @@ -0,0 +1,85 @@ +// +// TPHost.h +// Teleport +// +// Created by JuL on Sun Dec 07 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +#import "TPTCPSocket.h" +#import "TPScreen.h" + +#define MAX_CAPABILITIES 3 +typedef NS_OPTIONS(NSUInteger, TPHostCapability) { + TPHostNoCapability = 0, + TPHostEncryptionCapability = 1 << 0, + TPHostEventTapsCapability = 1 << 1, + TPHostDragNDropCapability = TPHostEventTapsCapability, + TPHostDirectEventTapsCapability = 1 << 2 +} ; + +/* + 13.x.x OS X 10.9.x Mavericks + 12.x.x OS X 10.8.x Mountain Lion + 11.x.x OS X 10.7.x Lion + 10.x.x OS X 10.6.x Snow Leopard + 9.x.x OS X 10.5.x Leopard + 8.x.x OS X 10.4.x Tiger + 7.x.x OS X 10.3.x Panther + 6.x.x OS X 10.2.x Jaguar + 5.x OS X 10.1.x Puma + */ + +static inline int TPHostOSVersion(int version) +{ + return version + 4; +} + +extern NSString * TPHostDidUpdateNotification; + +extern NSString * TPHostIdentifierKey; +extern NSString * TPHostComputerNameKey; +extern NSString * TPHostBackgroundImageDataKey; +extern NSString * TPHostMacAddressKey; +extern NSString * TPHostPortKey; +extern NSString * TPHostCapabilitiesKey; +extern NSString * TPHostOSVersionKey; + +@interface TPHost : NSObject +{ + NSString * _computerName; + IOEthernetAddress _macAddress; +} + +@property (nonatomic, readonly, copy) NSString *identifier; +@property (nonatomic, readonly) SInt32 osVersion; + +@property (nonatomic, readonly, copy) NSString *address; + +@property (nonatomic, copy) NSString *computerName; + +@property (nonatomic, readonly) BOOL hasValidMACAddress; +- (void)invalidateMACAddress; +@property (nonatomic) IOEthernetAddress MACAddress; + +@property (nonatomic, readonly, copy) NSArray *screens; +- (NSScreen*)screenAtIndex:(unsigned)screenIndex; + ++ (NSImage*)backgroundImageFromDesktopPicture:(NSImage*)desktopPicture; +- (NSImage*)defaultBackgroundImage; +@property (nonatomic, readonly, copy) NSImage *backgroundImage; +@property (nonatomic, readonly, copy) NSData *backgroundImageData; +@property (nonatomic, readonly) BOOL hasCustomBackgroundImage; + +@property (nonatomic, readonly) TPHostCapability capabilities; +- (BOOL)hasCapability:(TPHostCapability)capability; +- (BOOL)pairWithHost:(TPHost*)host hasCapability:(TPHostCapability)capability; + ++ (TPHost*)hostFromHostData:(NSData*)hostData; +@property (nonatomic, readonly, copy) NSData *hostData; + +- (void)notifyChange; + +@end diff --git a/TPHost.m b/TPHost.m new file mode 100644 index 0000000..43c3c1f --- /dev/null +++ b/TPHost.m @@ -0,0 +1,303 @@ +// +// TPHost.m +// Teleport +// +// Created by JuL on Sun Dec 07 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPHost.h" +#import "TPPreferencesManager.h" + +#define MAX_BACKGROUND_IMAGE_WIDTH 240 + +NSString * TPHostDidUpdateNotification = @"TPHostDidUpdateNotification"; + +NSString * TPHostIdentifierKey = @"identifier"; +NSString * TPHostComputerNameKey = @"computerName"; +NSString * TPHostBackgroundImageDataKey = @"backgroundImage"; +NSString * TPHostMacAddressKey = @"macAddress"; +NSString * TPHostCapabilitiesKey = @"capabilities"; +NSString * TPHostOSVersionKey = @"osVersion"; + +@implementation TPHost + +- (instancetype) init +{ + self = [super init]; + + _computerName = nil; + [self invalidateMACAddress]; + + return self; +} + +- (BOOL)isEqual:(id)object +{ + if(![object isKindOfClass:[TPHost class]]) + return NO; + + TPHost * otherObject = (TPHost*)object; + + return [[self identifier] isEqualToString:[otherObject identifier]]; +} + +#if LEGACY_BUILD +- (unsigned)hash +#else +- (NSUInteger)hash +#endif +{ + return [[self identifier] hash]; +} + +- (instancetype) initWithCoder:(NSCoder *)coder +{ + self = [super init]; + + _computerName = [[coder decodeObjectForKey:TPHostComputerNameKey] copy]; + + if([coder containsValueForKey:TPHostMacAddressKey]) { + const uint8_t * tempBuffer = [coder decodeBytesForKey:TPHostMacAddressKey returnedLength:NULL]; + memcpy(_macAddress.bytes, tempBuffer, kIOEthernetAddressSize); + } + else + [self invalidateMACAddress]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [coder encodeObject:[self identifier] forKey:TPHostIdentifierKey]; + [coder encodeObject:[self computerName] forKey:TPHostComputerNameKey]; + IOEthernetAddress address = [self MACAddress]; + [coder encodeBytes:address.bytes length:kIOEthernetAddressSize forKey:TPHostMacAddressKey]; + [coder encodeInt:[self capabilities] forKey:TPHostCapabilitiesKey]; + [coder encodeInt32:[self osVersion] forKey:TPHostOSVersionKey]; + +#if 0 // sent with TPBackgroundImageTransfer + if([self hasCustomBackgroundImage]) { + NSData * backgroundImageData = [self backgroundImageData]; + if(backgroundImageData != nil) + [coder encodeBytes:[backgroundImageData bytes] length:[backgroundImageData length] forKey:TPHostBackgroundImageDataKey]; + } +#endif +} + +- (NSString*)identifier +{ + return nil; +} + +- (SInt32)osVersion +{ + return 0; +} + +- (NSString*)address +{ + return nil; +} + +- (NSString*)computerName +{ + if(_computerName == nil) + return NSLocalizedString(@"unnamed", @"Name for unamed server"); + + return _computerName; +} + +- (void)setComputerName:(NSString*)computerName +{ + if(computerName != _computerName) { + _computerName = [computerName copy]; + [self notifyChange]; + } +} + +- (BOOL)hasValidMACAddress +{ + int i; + for(i=0; i + +#import "TPHostSnapping.h" +#import "TPRemoteHost.h" + +@class TPEffectsWindow; + +@interface TPHostPlacementIndicator : NSObject +{ + TPEffectsWindow * _window; + CALayer * _layer; + TPRemoteHost * _remoteHost; + NSRect _hostRect; + TPSide _currentSide; + unsigned _currentScreenIndex; +} + +- (instancetype) initWithHost:(TPRemoteHost*)remoteHost NS_DESIGNATED_INITIALIZER; +- (void)setHostRect:(NSRect)hostRect localScreenIndex:(unsigned)localScreenIndex; + +- (void)show; +- (void)close; + +@end + +@interface TPHostAnimationController : NSObject + ++ (TPHostAnimationController*)controller; + +- (void)showFireAnimationForHost:(TPRemoteHost*)host atPoint:(NSPoint)point onScreen:(NSScreen*)screen side:(TPSide)side; +- (void)showAppearanceAnimationForHost:(TPRemoteHost*)host; + +@end diff --git a/TPHostAnimationController.m b/TPHostAnimationController.m new file mode 100644 index 0000000..a24e860 --- /dev/null +++ b/TPHostAnimationController.m @@ -0,0 +1,491 @@ +// +// TPHostAnimationController.m +// teleport +// +// Created by Julien Robert on 23/02/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import "TPHostAnimationController.h" + +#import + +#import "TPLocalHost.h" + +@interface NSObject (TeleportLayerSupport) + +- (void)setWantsLayer:(BOOL)flag; +@property (nonatomic, readonly, strong) id layer; + +- (void)addSublayer:(id)layer; +- (void)setContents:(id)contents; +- (void)setFrame:(CGRect)frame; +- (void)setBounds:(CGRect)bounds; +- (void)setPosition:(CGPoint)position; +- (void)setOpacity:(float)opacity; +- (void)setAffineTransform:(CGAffineTransform)m; +- (void)addAnimation:(id)anim forKey:(NSString *)key; + +- (void)begin; +- (void)commit; + +- (id)animationWithKeyPath:(NSString *)path; +- (void)setToValue:(id)value; +- (void)setRemovedOnCompletion:(BOOL)flag; +- (void)setDuration:(CFTimeInterval)duration; +- (void)setFillMode:(NSString*)fillMode; + +@end + +@interface TPEffectsWindow : NSWindow + +- (instancetype) initWithFrame:(NSRect)frame NS_DESIGNATED_INITIALIZER; +@property (nonatomic, readonly, strong) CALayer *effectsLayer; + +@end + +@implementation TPEffectsWindow + +- (instancetype) initWithFrame:(NSRect)frame +{ + self = [super initWithContentRect:frame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + + [self setOpaque:NO]; + [self setIgnoresMouseEvents:YES]; + [self setLevel:kCGDraggingWindowLevel-1]; + [self setReleasedWhenClosed:NO]; + [self setBackgroundColor:[NSColor clearColor]]; + + NSView * view = [self contentView]; + [view setWantsLayer:YES]; + + if ([view respondsToSelector:@selector(setLayerUsesCoreImageFilters:)]) { + [view setLayerUsesCoreImageFilters:YES]; + } + + return self; +} + +- (void)close +{ + [[self contentView] setWantsLayer:NO]; + [super close]; +} + +- (CALayer*)effectsLayer +{ + return [[self contentView] layer]; +} + +@end + +static TPHostAnimationController* _controller = nil; + +@interface TPHostAnimationController () + +@property (nonatomic, strong) NSMutableSet *effectWindows; + +@end + +@implementation TPHostAnimationController + ++ (TPHostAnimationController*)controller +{ + if(_controller == nil) { + _controller = [[TPHostAnimationController alloc] init]; + } + + return _controller; +} + +- (instancetype)init +{ + self = [super init]; + + self.effectWindows = [[NSMutableSet alloc] init]; + + return self; +} + +#pragma mark - +#pragma mark Fire + +#define FIRE_HALO_COUNT 4 +#define FIRE_DURATION 0.8 +- (void)showFireAnimationForHost:(TPRemoteHost*)host atPoint:(NSPoint)point onScreen:(NSScreen*)screen side:(TPSide)side +{ + static CGImageRef _haloImageRef = NULL; + + if(_haloImageRef == NULL) { + NSString * imagePath = [[NSBundle mainBundle] pathForResource:@"halo" ofType:@"png"]; + CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)[NSURL fileURLWithPath:imagePath], NULL); + if(imageSource != NULL) { + _haloImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); + CFRelease(imageSource); + } + } + + TPEffectsWindow * effectWindow = [[TPEffectsWindow alloc] initWithFrame:[screen frame]]; + [_effectWindows addObject:effectWindow]; + id rootLayer = [effectWindow effectsLayer]; + + CGRect imageBounds = CGRectMake(0.0, 0.0, CGImageGetWidth(_haloImageRef), CGImageGetHeight(_haloImageRef)); + id layers[FIRE_HALO_COUNT]; + + id layerClass = NSClassFromString(@"CALayer"); + id transactionClass = NSClassFromString(@"CATransaction"); + + float xDirection = 0.0; + float yDirection = 0.0; + CGAffineTransform baseTransform = CGAffineTransformIdentity; + point = [effectWindow convertScreenToBase:point]; + + if(side & TPLeftSide) { + if(side & TPTopSide) { + baseTransform = CGAffineTransformMakeRotation(M_PI/4.0); + xDirection = +1/sqrtf(2); + } + else if(side & TPBottomSide) { + baseTransform = CGAffineTransformMakeRotation(-M_PI/4.0); + xDirection = +1/sqrtf(2); + } + else { + baseTransform = CGAffineTransformMakeRotation(M_PI/2.0); + xDirection = +1; + } + } + + if(side & TPRightSide) { + if(side & TPTopSide) { + baseTransform = CGAffineTransformMakeRotation(-M_PI/4.0); + xDirection = -1/sqrtf(2); + } + else if(side & TPBottomSide) { + baseTransform = CGAffineTransformMakeRotation(M_PI/4.0); + xDirection = -1/sqrtf(2); + } + else { + baseTransform = CGAffineTransformMakeRotation(M_PI/2.0); + xDirection = -1; + } + } + + if(side & TPTopSide) { + if(side & (TPRightSide|TPLeftSide)) { + yDirection = -1/sqrtf(2); + } + else { + yDirection = -1; + } + } + + if(side & TPBottomSide) { + if(side & (TPRightSide|TPLeftSide)) { + yDirection = +1/sqrtf(2); + } + else { + yDirection = +1; + } + } + + [transactionClass begin]; + + int i; + for(i=0; i)actionForLayer:(CALayer *)layer forKey:(NSString *)event +{ + return (id)[NSNull null]; +} + +@end diff --git a/TPHostSnapping.h b/TPHostSnapping.h new file mode 100644 index 0000000..8dd842d --- /dev/null +++ b/TPHostSnapping.h @@ -0,0 +1,20 @@ +/* + * TPHostSnapping.h + * teleport + * + * Created by JuL on 11/05/05. + * Copyright 2005 abyssoft. All rights reserved. + * + */ + +typedef NS_OPTIONS(NSUInteger, TPSide) { + TPUndefSide = 0, + TPRightSide = 1 << 0, + TPBottomSide = 1 << 1, + TPLeftSide = 1 << 2, + TPTopSide = 1 << 3 +} ; + +float TPSquaredDistance(NSRect rect1, NSRect rect2); +float TPGluedRect(NSRect * outGluedRect, TPSide * outSide, NSRect fixedRect, NSRect mobileRect, TPSide excludedSides); +NSRect TPRectShiftedOnSide(NSRect rect, TPSide side, float amount); diff --git a/TPHostSnapping.m b/TPHostSnapping.m new file mode 100644 index 0000000..daefb1f --- /dev/null +++ b/TPHostSnapping.m @@ -0,0 +1,116 @@ +/* + * TPHostSnapping.c + * teleport + * + * Created by JuL on 11/05/05. + * Copyright 2005 abyssoft. All rights reserved. + * + */ + +#include "TPHostSnapping.h" + +float TPSquaredDistance(NSRect rect1, NSRect rect2) { + NSPoint point1 = rect1.origin; + NSPoint point2 = rect2.origin; + float dx = point1.x - point2.x; + float dy = point1.y - point2.y; + return dx*dx+dy*dy; +} + +float TPGluedRect(NSRect * outGluedRect, TPSide * outSide, NSRect fixedRect, NSRect mobileRect, TPSide excludedSides) +{ + int s = 0; + NSRect gluedRect[4]; + TPSide side[4]; + + /* Right */ + side[s] = TPRightSide; + gluedRect[s] = mobileRect; + gluedRect[s].origin.x = NSMaxX(fixedRect); + gluedRect[s].origin.y = MIN(NSMaxY(fixedRect), MAX(NSMinY(fixedRect) - NSHeight(mobileRect), gluedRect[s].origin.y)); + + /* Left */ + side[++s] = TPLeftSide; + gluedRect[s] = mobileRect; + gluedRect[s].origin.x = NSMinX(fixedRect) - NSWidth(mobileRect); + gluedRect[s].origin.y = gluedRect[s-1].origin.y; + + /* Top */ + side[++s] = TPTopSide; + gluedRect[s] = mobileRect; + gluedRect[s].origin.y = NSMaxY(fixedRect); + gluedRect[s].origin.x = MIN(NSMaxX(fixedRect), MAX(NSMinX(fixedRect) - NSWidth(mobileRect), gluedRect[s].origin.x)); + + /* Bottom */ + side[++s] = TPBottomSide; + gluedRect[s] = mobileRect; + gluedRect[s].origin.y = NSMinY(fixedRect) - NSHeight(mobileRect); + gluedRect[s].origin.x = gluedRect[s-1].origin.x; + + NSRect minGluedRect = NSZeroRect; + float minDist = INFINITY; + TPSide minSide = TPLeftSide; + + for(s=0; s<4; s++) { + if((side[s] & excludedSides) == 0) { + float dist = TPSquaredDistance(mobileRect, gluedRect[s]); + if(dist < minDist) { + minDist = dist; + minSide = side[s]; + minGluedRect = gluedRect[s]; + } + } + } + + if(outGluedRect != NULL) + *outGluedRect = minGluedRect; + if(outSide != NULL) { + switch(minSide) { + case TPRightSide: + case TPLeftSide: + if(NSMinY(mobileRect) >= NSMaxY(fixedRect)) + minSide |= TPTopSide; + else if(NSMaxY(mobileRect) <= NSMinY(fixedRect)) + minSide |= TPBottomSide; + break; + case TPTopSide: + case TPBottomSide: + if(NSMinX(mobileRect) >= NSMaxX(fixedRect)) + minSide |= TPRightSide; + else if(NSMaxX(mobileRect) <= NSMinX(fixedRect)) + minSide |= TPLeftSide; + break; + default: + break; + } + + *outSide = minSide; + } + + if(NSIntersectsRect(fixedRect, mobileRect)) + return -minDist; + else + return minDist; +} + +NSRect TPRectShiftedOnSide(NSRect rect, TPSide side, float amount) +{ + NSRect shiftedRect = rect; + switch(side) { + case TPRightSide: + shiftedRect.origin.x += amount; + break; + case TPLeftSide: + shiftedRect.origin.x -= amount; + break; + case TPTopSide: + shiftedRect.origin.y += amount; + break; + case TPBottomSide: + shiftedRect.origin.y -= amount; + break; + default: + break; + } + return shiftedRect; +} diff --git a/TPHostsManager.h b/TPHostsManager.h new file mode 100644 index 0000000..4b6bf59 --- /dev/null +++ b/TPHostsManager.h @@ -0,0 +1,47 @@ +// +// TPHostsManager.h +// teleport +// +// Created by JuL on 30/01/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import + +#import "TPRemoteHost.h" + +extern NSString * TPHostsKey; +extern NSString * TPHostsConfigurationDidChangeNotification; + +@interface TPHostsManager : NSObject +{ + NSMutableDictionary * _hosts; + NSMutableDictionary * _clientHosts; + + TPRemoteHost * _pendingAuthenticationHost; +} + ++ (TPHostsManager*)defaultManager; + +/* Persistence */ +- (void)loadHosts; +- (void)saveHosts; + +/* Add/remove */ +//- (void)addRemoteHost:(TPRemoteHost*)host; +//- (void)removeRemoteHost:(TPRemoteHost*)host; +- (void)addBonjourHost:(TPRemoteHost*)bonjourHost; +- (void)removeBonjourHost:(TPRemoteHost*)bonjourHost; +- (TPRemoteHost*)updatedHostFromData:(NSData*)hostData; + +- (void)addClientHost:(TPRemoteHost*)clientHost; + +/* Queries */ +- (NSArray*)hostsWithState:(TPHostState)state; +- (TPRemoteHost*)hostWithIdentifier:(NSString*)identifier; +- (TPRemoteHost*)hostWithAddress:(NSString*)address; + +/* Misc */ +- (void)notifyChanges; + +@end diff --git a/TPHostsManager.m b/TPHostsManager.m new file mode 100644 index 0000000..b6fcb38 --- /dev/null +++ b/TPHostsManager.m @@ -0,0 +1,327 @@ +// +// TPHostsManager.m +// teleport +// +// Created by JuL on 30/01/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import "TPHostsManager.h" + +#import "TPLocalHost.h" +#import "TPClientController.h" +#import "TPNetworkConnection.h" +#import "TPAuthenticationManager.h" +#import "TPPreferencesManager.h" +#import "TPMessage.h" + +#define DUPLICATE 0 +#define HOSTS_VERSION 6 + +NSString * TPHostsVersionKey = @"TPHostsVersion"; +NSString * TPHostsKey = @"TPHosts"; +NSString *TPHostsConfigurationDidChangeNotification = @"TPHostsConfigurationDidChangeNotification"; + +static TPHostsManager * _defaultHostsManager = nil; + +@implementation TPHostsManager + ++ (TPHostsManager*)defaultManager +{ + if(_defaultHostsManager == nil) + _defaultHostsManager = [[TPHostsManager alloc] init]; + + return _defaultHostsManager; +} + +- (instancetype) init +{ + self = [super init]; + + _hosts = [[NSMutableDictionary alloc] init]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifyChanges) name:TPHostDidUpdateNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveHosts) name:TPHostDidUpdateNotification object:nil]; + + return self; +} + + + +#pragma mark - +#pragma mark Persistence + +- (void)loadHosts +{ + int hostsVersion = [[TPPreferencesManager sharedPreferencesManager] intForPref:TPHostsVersionKey]; + if(hostsVersion != HOSTS_VERSION) + return; + + NSData * archivedData = [[TPPreferencesManager sharedPreferencesManager] valueForPref:TPHostsKey]; + if(archivedData != nil) { + [_hosts removeAllObjects]; + NSArray * newHosts = nil; + @try { + newHosts = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData]; + } + @catch(NSException * e) { + newHosts = nil; + } + + if(newHosts != nil) { + NSEnumerator * newHostsEnum = [newHosts objectEnumerator]; + TPRemoteHost * newHost; + + while((newHost = [newHostsEnum nextObject])) { + TPHostState state = [newHost hostState]; + + switch(state) { + case TPHostSharedState: + case TPHostIncompatibleState: + break; + case TPHostUndefState: + case TPHostPeeredOfflineState: + _hosts[[newHost identifier]] = newHost; + break; + case TPHostPeeredOnlineState: + case TPHostControlledState: + [newHost setHostState:TPHostPeeredOfflineState]; + _hosts[[newHost identifier]] = newHost; + break; + default: + break; + } + } + } + } +} + +- (void)saveHosts +{ + NSMutableDictionary * hostsToSave = [_hosts mutableCopy]; + NSEnumerator * hostsEnum = [_hosts objectEnumerator]; + TPRemoteHost * host; + + while((host = [hostsEnum nextObject])) { + TPHostState state = [host hostState]; + + switch(state) { + case TPHostSharedState: + case TPHostIncompatibleState: + case TPHostUndefState: + [hostsToSave removeObjectForKey:[host identifier]]; + break; + default: + break; + } + } + + [[TPPreferencesManager sharedPreferencesManager] setValue:[NSKeyedArchiver archivedDataWithRootObject:hostsToSave] forKey:TPHostsKey]; + [[TPPreferencesManager sharedPreferencesManager] setValue:@HOSTS_VERSION forKey:TPHostsVersionKey]; + +} + + +#pragma mark - +#pragma mark Setters + +//- (void)addRemoteHost:(TPRemoteHost*)host +//{ +// TPRemoteHost * knownHost = [self hostWithIdentifier:[host identifier]]; +// BOOL shouldNotify = YES; +// +// DebugLog(@"new host: %@%@", host, knownHost?@" (known)":@""); +// +// if(knownHost != nil) { +// if([host address] != nil && [knownHost address] == nil) { +// [knownHost setAddress:[host address]]; +// [knownHost setScreenSize:[host screenSize]]; +// } +// } +// else +// [_hosts setObject:host forKey:[host identifier]]; +// +// if(shouldNotify) +// [self notifyChanges]; +//} + +- (void)removeRemoteHost:(TPRemoteHost*)host +{ + TPRemoteHost * knownHost = [self hostWithIdentifier:[host identifier]]; + BOOL shouldNotify = YES; + + DebugLog(@"remove host: %@", knownHost); + + if(knownHost != nil) { + TPHostState state = [knownHost hostState]; + + switch(state) { + case TPHostUndefState: + shouldNotify = NO; + break; + case TPHostSharedState: + case TPHostIncompatibleState: + [knownHost setHostState:TPHostUndefState]; + break; + case TPHostPeeredOfflineState: + shouldNotify = NO; + DebugLog(@"error: offline peered host disappeared on rdv"); + break; + case TPHostPeeredOnlineState: + [knownHost setHostState:TPHostPeeredOfflineState]; + break; + case TPHostControlledState: + [knownHost setHostState:TPHostPeeredOfflineState]; + [[TPClientController defaultController] stopControl]; + break; + default: + break; + } + } + else + shouldNotify = NO; + + if(shouldNotify) + [self notifyChanges]; +} + +- (void)addBonjourHost:(TPRemoteHost*)bonjourHost +{ + TPRemoteHost * knownHost = [self hostWithIdentifier:[bonjourHost identifier]]; + BOOL shouldNotify = YES; + + DebugLog(@"new bonjour host: %@%@", bonjourHost, knownHost?@" (known)":@""); + + if(knownHost != nil) { + if([bonjourHost address] != nil) { + TPHostState state = [knownHost hostState]; + + [knownHost setAddress:[bonjourHost address]]; + [knownHost setPort:[bonjourHost port]]; + [knownHost setScreens:[bonjourHost screens]]; + + switch(state) { + case TPHostUndefState: + [knownHost setHostState:TPHostSharedState]; + break; + case TPHostSharedState: + shouldNotify = NO; + break; + case TPHostPeeredOfflineState: + [knownHost setHostState:TPHostPeeredOnlineState]; + break; + case TPHostPeeredOnlineState: + break; + case TPHostControlledState: + break; + default: + break; + } + } + else { + [knownHost setHostState:TPHostIncompatibleState]; + } + } + else { + _hosts[[bonjourHost identifier]] = bonjourHost; +#if DUPLICATE + int i; + for(i=0; i +#import "TPHostSnapping.h" + +#define DEBUG_HOTBORDER 0 + +#if DEBUG_HOTBORDER +#define BORDER_SIZE 4.0 +#else +#define BORDER_SIZE 1.0 +#endif + +@class TPHotBorderView; + +typedef NS_ENUM(NSInteger, TPHotBorderState) { + TPHotBorderInactiveState, + TPHotBorderActivatingState, + TPHotBorderActiveState +} ; + +@interface TPHotBorder : NSWindow +{ + TPHotBorderState _state; + BOOL _doubleTap; + BOOL _acceptDrags; + id _hotDelegate; + NSString * identifier; + NSTrackingRectTag _trackingRectTag; + NSTimer * _fireTimer; + NSTimer * _tapTimer; + TPSide _side; +} + ++ (TPHotBorder*)hotBorderRepresentingRect:(NSRect)representedRect inRect:(NSRect)parentRect; ++ (NSRect)hotRectWithRepresentingRect:(NSRect)representedRect inRect:(NSRect)parentRect; + +- (instancetype) initWithRepresentingRect:(NSRect)representedRect inRect:(NSRect)parentRect NS_DESIGNATED_INITIALIZER; + +- (void)updateWithRepresentingRect:(NSRect)representedRect inRect:(NSRect)parentRect; + +@property (nonatomic) BOOL doubleTap; +@property (nonatomic) BOOL acceptDrags; +- (void)setDelegate:(id)delegate; +@property (nonatomic, copy) NSString *identifier; +- (void)setOpaqueToMouseEvents:(BOOL)opaque; +@property (nonatomic, readonly) NSRect hotRect; +@property (nonatomic, readonly) TPSide side; + +- (void)activate; +- (void)delayedActivate; +- (void)deactivate; +@property (nonatomic, readonly) TPHotBorderState state; + +- (void)fireAtLocation:(NSPoint)location withDraggingInfo:(id )draggingInfo; + +- (NSPoint)screenPointFromLocalPoint:(NSPoint)localPoint flipped:(BOOL)flipped; + +@end + +@interface TPHotBorder (Private) + +- (void)_fireAtLocation:(NSPoint)location withDraggingInfo:(id )draggingInfo; + +@end + +@interface NSObject (TPHotBorder_delegate) + +- (float)hotBorderSwitchDelay:(TPHotBorder*)hotBorder; +- (BOOL)hotBorder:(TPHotBorder*)hotBorder canFireWithEvent:(NSEvent*)event; +- (BOOL)hotBorder:(TPHotBorder*)hotBorder firedAtLocation:(NSPoint)location withDraggingInfo:(id )draggingInfo; + +@end \ No newline at end of file diff --git a/TPHotBorder.m b/TPHotBorder.m new file mode 100644 index 0000000..d39f745 --- /dev/null +++ b/TPHotBorder.m @@ -0,0 +1,400 @@ +// +// TPHotBorder.m +// Teleport +// +// Created by JuL on Sun Dec 07 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPHotBorder.h" +#import "TPHotBorderView.h" +#import "TPPreferencesManager.h" + + +static NSString * TPHotBorderLocationKey = @"TPHotBorderLocation"; +static NSString * TPHotBorderDraggingInfoKey = @"TPHotBorderDraggingInfo"; + +@interface TPHotBorder () +{ + BOOL _ignoringEvents; +} + +- (void)_discardFireAttempt; + +@end + +@implementation TPHotBorder + ++ (TPHotBorder*)hotBorderRepresentingRect:(NSRect)representedRect inRect:(NSRect)parentRect +{ + TPHotBorder * hotBorder = [[TPHotBorder alloc] initWithRepresentingRect:representedRect inRect:parentRect]; + return hotBorder; +} + ++ (NSRect)hotRectWithRepresentingRect:(NSRect)representedRect inRect:(NSRect)parentRect +{ + NSRect hotRect; + NSRect enlargedRect; + +#if 1 + enlargedRect = NSInsetRect(representedRect, -BORDER_SIZE, -BORDER_SIZE); + hotRect = NSIntersectionRect(parentRect, enlargedRect); +#else + enlargedRect = representedRect; + enlargedRect.origin.x -= BORDER_SIZE; + enlargedRect.size.width += 2*BORDER_SIZE; + + hotRect = NSIntersectionRect(parentRect, enlargedRect); + +// DebugLog(@"1representedRect=%@, parentRect=%@, BORDER_SIZE=%d, offSetRect=%@, hotRect=%@", NSStringFromRect(representedRect), NSStringFromRect(parentRect), BORDER_SIZE, NSStringFromRect(NSOffsetRect(representedRect,BORDER_SIZE,BORDER_SIZE)), NSStringFromRect(hotRect)); + + if(!NSEqualRects(hotRect, NSZeroRect)) + return hotRect; + + enlargedRect = representedRect; + enlargedRect.origin.y -= BORDER_SIZE; + enlargedRect.size.height += 2*BORDER_SIZE; + + hotRect = NSIntersectionRect(parentRect, enlargedRect); +#endif + +// DebugLog(@"2representedRect=%@, parentRect=%@, BORDER_SIZE=%d, offSetRect=%@, hotRect=%@", NSStringFromRect(representedRect), NSStringFromRect(parentRect), BORDER_SIZE, NSStringFromRect(NSOffsetRect(representedRect,BORDER_SIZE,BORDER_SIZE)), NSStringFromRect(hotRect)); + + return hotRect; +} + +- (instancetype) initWithRepresentingRect:(NSRect)representedRect inRect:(NSRect)parentRect +{ + self = [super initWithContentRect:representedRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + + [self setAlphaValue:1.0]; + [self setOpaque:NO]; + [self setHasShadow:NO]; + [self setLevel:kCGDraggingWindowLevel-1]; + + [self setReleasedWhenClosed:NO]; + [self setDelegate:self]; + + /* Setup view */ + TPHotBorderView * hotView = [[TPHotBorderView alloc] initWithFrame:NSZeroRect]; + [self setContentView:hotView]; + + _state = TPHotBorderInactiveState; + _hotDelegate = nil; + identifier = nil; + _fireTimer = nil; + _trackingRectTag = -1; + + [self updateWithRepresentingRect:representedRect inRect:parentRect]; + + [self orderFront:self]; + + if([self respondsToSelector:@selector(setCollectionBehavior:)]) { + [self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorStationary]; + } + + return self; +} + +- (void)dealloc +{ + [_tapTimer invalidate]; + _tapTimer = nil; + [self _discardFireAttempt]; +} + +- (void)updateWithRepresentingRect:(NSRect)representedRect inRect:(NSRect)parentRect +{ + NSRect hotRect = [TPHotBorder hotRectWithRepresentingRect:representedRect inRect:parentRect]; + TPGluedRect(NULL, &_side, parentRect, representedRect, TPUndefSide); + + [self setFrame:hotRect display:YES]; + + NSView * hotView = [self contentView]; + + if(_trackingRectTag != -1) { + [hotView removeTrackingRect:_trackingRectTag]; + } + + _trackingRectTag = [hotView addTrackingRect:[hotView bounds] owner:self userData:NULL assumeInside:NO]; +} + +- (void)activate +{ + if(NSPointInRect([self mouseLocationOutsideOfEventStream], [[self contentView] bounds])) { + [self performSelector:@selector(activate) withObject:nil afterDelay:0.1]; // post-pone activation until mouse is not already in the hot border + } + else { + _state = TPHotBorderActiveState; + } +} + +- (void)deactivate +{ + _state = TPHotBorderInactiveState; +} + +- (TPHotBorderState)state +{ + return _state; +} + +- (void)setDoubleTap:(BOOL)doubleTap +{ + _doubleTap = doubleTap; +} + +- (BOOL)doubleTap +{ + return _doubleTap; +} + +- (void)setAcceptDrags:(BOOL)acceptDrags +{ + _acceptDrags = acceptDrags; + + [self setIgnoresMouseEvents:!acceptDrags]; + + if(acceptDrags) { + [self setLevel:kCGDraggingWindowLevel-1]; + [[self contentView] registerForDraggedTypes:@[NSFilenamesPboardType]]; + } + else { + [self setLevel:kCGMaximumWindowLevel]; + [[self contentView] unregisterDraggedTypes]; + } +} + +- (BOOL)acceptDrags +{ + return _acceptDrags; +} + +- (void)setDelegate:(id)delegate +{ + _hotDelegate = delegate; +} + +- (void)setIdentifier:(NSString*)inIdentifier +{ + identifier = [inIdentifier copy]; +} + +- (NSString*)identifier +{ + return identifier; +} + +- (void)setOpaqueToMouseEvents:(BOOL)opaque +{ + TPHotBorderView * hotView = (TPHotBorderView*)[self contentView]; + + if(opaque) { + _ignoringEvents = YES; + [self setIgnoresMouseEvents:NO]; + [hotView setColor:[NSColor colorWithCalibratedWhite:0.5 alpha:0.2]]; + [hotView display]; + } + else { + _ignoringEvents = NO; + + [self setIgnoresMouseEvents:!_acceptDrags]; + [hotView setColor:[hotView normalColor]]; + [hotView display]; + } +} + +- (NSRect)hotRect +{ + return [self frame]; +} + +- (TPSide)side +{ + return _side; +} + +- (void)delayedActivate +{ + float inhibitionPeriod = [[TPPreferencesManager sharedPreferencesManager] floatForPref:INHIBITION_PERIOD]; + if(inhibitionPeriod > 0.0) { + _state = TPHotBorderActivatingState; + [NSTimer scheduledTimerWithTimeInterval:inhibitionPeriod target:self selector:@selector(activate) userInfo:nil repeats:NO]; + } + else { + [self activate]; + } +} + +- (NSPoint)screenPointFromLocalPoint:(NSPoint)localPoint flipped:(BOOL)flipped +{ + NSPoint point = [self convertBaseToScreen:localPoint]; + if(flipped) { + point.y = NSMaxY([[NSScreen screens][0] frame]) - point.y; + } + return point; +} + +- (void)mouseDown:(NSEvent*)event +{ + DebugLog(@"mouseDown"); + [super mouseDown:event]; +} + +- (void)mouseUp:(NSEvent*)event +{ + DebugLog(@"mouseUp"); + [super mouseUp:event]; +} + + +#pragma mark - +#pragma mark Fire methods + +- (void)_fireAttemptWithEvent:(NSEvent*)event atLocation:(NSPoint)location withDraggingInfo:(id )draggingInfo +{ + if(_state != TPHotBorderActiveState) { + DebugLog(@"not active"); + return; + } + if(_hotDelegate == nil) { + DebugLog(@"nil _hotDelegate!"); + return; + } + if (_ignoringEvents) { + DebugLog(@"ignoring events"); + return; + } + if([_hotDelegate respondsToSelector:@selector(hotBorder:canFireWithEvent:)]) { + if(![_hotDelegate hotBorder:self canFireWithEvent:event]) { + DebugLog(@"can't activate!"); + return; + } + } + + if(_doubleTap) { + if(_tapTimer == nil) { + float tapInterval = [[TPPreferencesManager sharedPreferencesManager] floatForPref:DOUBLE_TAP_INTERVAL]; + _tapTimer = [NSTimer scheduledTimerWithTimeInterval:tapInterval target:self selector:@selector(tapIntervalExpired:) userInfo:nil repeats:NO]; + return; + } + else { + [_tapTimer invalidate]; + _tapTimer = nil; + } + } + + float delay = [_delegate hotBorderSwitchDelay:self]; + if(delay > 0.0) { + NSMutableDictionary * fireDict = [NSMutableDictionary dictionary]; + + fireDict[TPHotBorderLocationKey] = [NSValue valueWithPoint:location]; + + if (draggingInfo != nil) { + fireDict[TPHotBorderDraggingInfoKey] = draggingInfo; + } + + _fireTimer = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:@selector(_fireWithTimer:) userInfo:fireDict repeats:NO]; + } + else { + [self fireAtLocation:location withDraggingInfo:draggingInfo]; + } +} + +- (void)tapIntervalExpired:(NSTimer*)tapTimer +{ + _tapTimer = nil; +} + +- (void)_discardFireAttempt +{ + [_fireTimer invalidate]; + _fireTimer = nil; +} + +- (void)mouseEntered:(NSEvent *)event +{ + DebugLog(@"mouseEntered %@", [[NSPasteboard pasteboardWithName:NSDragPboard] types]); + + [self _discardFireAttempt]; + + NSUInteger buttonState = [NSEvent pressedMouseButtons]; + + if(buttonState == 0) { // no mouse button down + [self _fireAttemptWithEvent:event atLocation:[event locationInWindow] withDraggingInfo:nil]; + } +} + +- (void)mouseExited:(NSEvent *)event +{ + DebugLog(@"mouseExited"); + + [self _discardFireAttempt]; +} + +- (NSDragOperation)draggingEntered:(id )sender +{ + DebugLog(@"drag entered\ntypes=%@", [[sender draggingPasteboard] types]); + + [self _discardFireAttempt]; + + NSPoint point = [sender draggingLocation]; + [self _fireAttemptWithEvent:[NSApp currentEvent] atLocation:point withDraggingInfo:sender]; + + return NSDragOperationCopy; +} + +- (void)draggingExited:(id )sender +{ + DebugLog(@"drag exited"); + + [self _discardFireAttempt]; +} + +- (BOOL)prepareForDragOperation:(id )sender +{ + DebugLog(@"prepareForDragOperation"); + return YES; +} + +- (BOOL)performDragOperation:(id )sender +{ + DebugLog(@"performDragOperation"); + return YES; +} + +- (void)concludeDragOperation:(id )sender +{ + DebugLog(@"concludeDragOperation"); +} + +- (void)_fireWithTimer:(NSTimer*)timer +{ + NSDictionary * fireDict = [timer userInfo]; + NSPoint location = [fireDict[TPHotBorderLocationKey] pointValue]; + + [self fireAtLocation:location withDraggingInfo:fireDict[TPHotBorderDraggingInfoKey]]; +} + +- (void)fireAtLocation:(NSPoint)location withDraggingInfo:(id )draggingInfo +{ + [self _discardFireAttempt]; + + if(![_hotDelegate respondsToSelector:@selector(hotBorder:firedAtLocation:withDraggingInfo:)]) { + DebugLog(@"delegate does not respond to selector!"); + return; + } + + DebugLog(@"Fire %@ at position=%@", self, NSStringFromPoint(location)); + + [self deactivate]; + + BOOL doFireAnimation = [_hotDelegate hotBorder:self firedAtLocation:location withDraggingInfo:draggingInfo]; + + if(doFireAnimation) + [(TPHotBorderView*)[self contentView] fireAtLocation:location]; + else + ; +} + +@end diff --git a/TPHotBorderView.h b/TPHotBorderView.h new file mode 100644 index 0000000..e115fec --- /dev/null +++ b/TPHotBorderView.h @@ -0,0 +1,24 @@ +// +// TPHotBorderView.h +// teleport +// +// Created by JuL on 08/02/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import + +@interface TPHotBorderView : NSView +{ + NSTimer * _animationTimer; + NSColor * _color; +} + +- (void)fireAtLocation:(NSPoint)location; + +@property (nonatomic, readonly, copy) NSColor *normalColor; +- (void)setColor:(NSColor*)color; + +- (void)stopAnimation; + +@end diff --git a/TPHotBorderView.m b/TPHotBorderView.m new file mode 100644 index 0000000..190afc1 --- /dev/null +++ b/TPHotBorderView.m @@ -0,0 +1,137 @@ +// +// TPHotBorderView.m +// teleport +// +// Created by JuL on 08/02/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import "TPHotBorderView.h" + +#import "TPPreferencesManager.h" +#import "TPHotBorder.h" + +NSColor * _highlightColor = nil; +NSColor * _transparentColor = nil; +NSColor * _visibleColor = nil; + +#define ANIMATION_TIME 2.0 + +@implementation TPHotBorderView + ++ (void)initialize +{ + if(self == [TPHotBorderView class]) { + _highlightColor = [NSColor redColor]; + _visibleColor = [NSColor blueColor]; + _transparentColor = [NSColor clearColor]; + } +} + +- (NSColor*)normalColor +{ + static NSColor * _normalColor = nil; + if(_normalColor == nil) { + if(DEBUG_HOTBORDER || [[TPPreferencesManager sharedPreferencesManager] boolForPref:@"visibleBorders"]) + _normalColor = _visibleColor; + else + _normalColor = _transparentColor; + } + + return _normalColor; +} + +- (instancetype)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self setColor:[self normalColor]]; + _animationTimer = nil; + } + return self; +} + +- (void)dealloc +{ + if(_animationTimer) { + [_animationTimer invalidate]; + } +} + +- (NSDragOperation)draggingEntered:(id )sender +{ + return [(id)[self window] draggingEntered:sender]; +} + +- (void)draggingExited:(id )sender +{ + [(id)[self window] draggingExited:sender]; +} + +- (BOOL)prepareForDragOperation:(id )sender +{ + PRINT_ME; + return YES; +} +- (BOOL)performDragOperation:(id )sender +{ + PRINT_ME; + return YES; +} + +- (void)concludeDragOperation:(id )sender +{ + PRINT_ME; +} + +- (void)fireAtLocation:(NSPoint)location +{ + if(_animationTimer) { + [_animationTimer invalidate]; + _animationTimer = nil; + } + + NSDate * now = [[NSDate alloc] init]; + _animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(_animationStep) userInfo:now repeats:YES]; +} + +- (void)_animationStep +{ + NSDate * start = [_animationTimer userInfo]; + NSTimeInterval elapsed = - [start timeIntervalSinceNow]; + float progress = elapsed / ANIMATION_TIME; + + + if(progress >= 1.0) { + [self stopAnimation]; + } + else { + [self setColor:[_highlightColor blendedColorWithFraction:progress ofColor:[self normalColor]]]; + } + + [self display]; +} + +- (void)setColor:(NSColor*)color +{ + if(color != _color) { + _color = color; + } +} + +- (void)stopAnimation +{ + [_animationTimer invalidate]; + _animationTimer = nil; + [self setColor:[self normalColor]]; + + [self window]; +} + +- (void)drawRect:(NSRect)rect +{ + [_color set]; + NSRectFill(rect); +} + +@end diff --git a/TPKeyComboView.h b/TPKeyComboView.h new file mode 100644 index 0000000..4dbc9e2 --- /dev/null +++ b/TPKeyComboView.h @@ -0,0 +1,28 @@ +// +// TPKeyComboView.h +// teleport +// +// Created by Julien Robert on 08/04/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import +#import "PTKeyCombo.h" + +@interface TPKeyComboView : NSView +{ + id _delegate; + PTKeyCombo * _keyCombo; +} + +@property (nonatomic, unsafe_unretained) id delegate; + +@property (nonatomic, copy) PTKeyCombo *keyCombo; + +@end + +@interface NSObject (TPKeyComboTextFieldDelegate) + +- (void)keyComboDidChange:(PTKeyCombo*)keyCombo; + +@end diff --git a/TPKeyComboView.m b/TPKeyComboView.m new file mode 100644 index 0000000..fd6c2ce --- /dev/null +++ b/TPKeyComboView.m @@ -0,0 +1,137 @@ +// +// TPKeyComboView.m +// teleport +// +// Created by Julien Robert on 08/04/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import "TPKeyComboView.h" +#import "PTKeyBroadcaster.h" + +@implementation TPKeyComboView + + +- (void)drawRect:(NSRect)rect +{ + BOOL hasFocus = ([[self window] firstResponder] == self); + + [[NSColor whiteColor] set]; + NSRectFill(rect); + + if(hasFocus) { + [[NSColor keyboardFocusIndicatorColor] set]; + NSFrameRectWithWidthUsingOperation([self bounds], 2.0, NSCompositeSourceOver); + } + else { + [[NSColor lightGrayColor] set]; + NSFrameRect([self bounds]); + } + + NSString * string = nil; + NSColor * color = nil; + + if(_keyCombo == nil) { + color = [NSColor grayColor]; + + if(hasFocus) { + string = NSLocalizedStringFromTableInBundle(@"Type key combo", nil, [NSBundle bundleForClass:[self class]], @"Displayed in the key combo field, should be short"); + } + else { + string = NSLocalizedStringFromTableInBundle(@"Click to set", nil, [NSBundle bundleForClass:[self class]], @"Displayed in the key combo field, should be short"); + } + } + else { + color = [NSColor blackColor]; + string = [_keyCombo description]; + } + + NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + [paragraphStyle setAlignment:NSCenterTextAlignment]; + NSFont * font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]; + NSDictionary * attributes = [[NSDictionary alloc] initWithObjectsAndKeys: + paragraphStyle, NSParagraphStyleAttributeName, + font, NSFontAttributeName, + color, NSForegroundColorAttributeName, + nil]; + [string drawInRect:NSInsetRect([self bounds], 2.0, 2.0) withAttributes:attributes]; +} + +- (void)setDelegate:(id)delegate +{ + _delegate = delegate; +} + +- (id)delegate +{ + return _delegate; +} + +- (void)setKeyCombo:(PTKeyCombo*)keyCombo +{ + if(keyCombo != _keyCombo) { + _keyCombo = keyCombo; + + [self setNeedsDisplay:YES]; + } +} + +- (PTKeyCombo*)keyCombo +{ + return _keyCombo; +} + +- (void)_updateKeyComboWithEvent:(NSEvent*)event +{ + BOOL updateKeyCombo = YES; + PTKeyCombo * keyCombo = nil; + unsigned short keyCode = [event keyCode]; + + switch(keyCode) { + case 51: // backspace: clear + break; + case 53: // escape: exit + updateKeyCombo = NO; + break; + default: + keyCombo = [PTKeyCombo keyComboWithKeyCode:keyCode modifiers:[PTKeyBroadcaster cocoaModifiersAsCarbonModifiers:[event modifierFlags]]]; + + if(![keyCombo isValidHotKeyCombo]) { + NSBeep(); + return; + } + } + + if(updateKeyCombo) { + [self setKeyCombo:keyCombo]; + + if(_delegate != nil && [_delegate respondsToSelector:@selector(keyComboDidChange:)]) + [_delegate keyComboDidChange:keyCombo]; + } + + [[self window] makeFirstResponder:[self superview]]; +} + +- (void)keyDown:(NSEvent*)event +{ + [self _updateKeyComboWithEvent:event]; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (BOOL)becomeFirstResponder +{ + [self setNeedsDisplay:YES]; + return YES; +} + +- (BOOL)resignFirstResponder +{ + [self setNeedsDisplay:YES]; + return YES; +} + +@end diff --git a/TPLayoutHostView.h b/TPLayoutHostView.h new file mode 100644 index 0000000..48322be --- /dev/null +++ b/TPLayoutHostView.h @@ -0,0 +1,84 @@ +// +// TPLayoutHost.h +// teleport +// +// Created by JuL on Sun Feb 29 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import +#import "TPLayoutView.h" + +#define ROUND_RECT 1 +#define FLIPPED_VIEW 0 + +#if ROUND_RECT +#define FORDRAW_ORIGIN(x) (round(x)) +#define FORDRAW_SIZE(x) (round(x)) +#else +#define FORDRAW_ORIGIN(x) (x) +#define FORDRAW_SIZE(x) (x) +#endif +#define MIN_SCALE 0.01 +#define MAX_SCALE 0.2 + +typedef NS_ENUM(NSInteger, TPDrawMode) {NORMAL_MODE, DISABLED_MODE, SELECTED_MODE} ; + +@class TPHost, TPLayoutHostView; + +@interface NSBezierPath (TeleportAdditions) + ++ (void)drawRect:(NSRect)rect withGradientFrom:(NSColor*)colorStart to:(NSColor*)colorEnd; + +@end + +@interface TPLayoutScreenView : NSView +{ + TPLayoutHostView * _hostView; + unsigned _screenIndex; +} + +- (instancetype) initWithHostView:(TPLayoutHostView*)hostView screenIndex:(unsigned)screenIndex NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readonly, strong) TPLayoutHostView *hostView; +@property (nonatomic, readonly, strong) NSScreen *screen; +@property (nonatomic, readonly) unsigned int screenIndex; +@property (nonatomic, getter=isMainScreen, readonly) BOOL mainScreen; + +- (void)update; + +- (void)drawHostTitleInRect:(NSRect)rect dimmed:(BOOL)dimmed; + +@end + +@interface TPLayoutHostView : NSView +{ + NSString * _hostIdentifier; + TPLayoutView * _layoutView; + NSPoint _origin; + TPDrawMode drawMode; +} + +//- (void)setRealFrame:(NSRect)frame; + ++ (Class)screenViewClass; +- (instancetype) initWithHost:(TPHost*)host layoutView:(TPLayoutView*)layoutView NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, copy) NSString *hostIdentifier; +@property (nonatomic, readonly, strong) TPHost *host; + +@property (nonatomic, readonly) NSRect totalFrame; +@property (nonatomic, readonly, copy) NSArray *screenViews; +- (unsigned)indexOfScreenAtPoint:(NSPoint)point; +- (TPLayoutScreenView*)screenViewAtIndex:(unsigned)index; +- (NSRect)screenFrameAtIndex:(unsigned)index; +- (NSRect)hostFrameFromScreenFrame:(NSRect)frame atIndex:(unsigned)index; + +- (void)updateLayoutWithScaleFactor:(float)scaleFactor; + +#if 0 +- (void)setDrawMode:(TPDrawMode)drawMode; +- (void)setOrigin:(NSPoint)origin; +#endif + +@end diff --git a/TPLayoutHostView.m b/TPLayoutHostView.m new file mode 100644 index 0000000..9407eba --- /dev/null +++ b/TPLayoutHostView.m @@ -0,0 +1,306 @@ +// +// TPLayoutHost.m +// teleport +// +// Created by JuL on Sun Feb 29 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPLayoutHostView.h" +#import "TPHost.h" +#import "TPHostsManager.h" + +#define TEXT_MARGIN 6 + +@implementation NSBezierPath (TeleportAdditions) + ++ (void)drawRect:(NSRect)rect withGradientFrom:(NSColor*)colorStart to:(NSColor*)colorEnd +{ + float fraction = 0; + float height = rect.size.height - 1; + float width = rect.size.width; + float step = 1/height; + int i; + + NSRect gradientRect = NSMakeRect(rect.origin.x, rect.origin.y, width, 1.0); + [colorEnd set]; + [NSBezierPath fillRect:gradientRect]; + + for(i = 0; i < height; i++) + { + gradientRect.origin.y++; + NSColor * gradientColor = [colorStart blendedColorWithFraction:fraction ofColor:colorEnd]; + [gradientColor set]; + [NSBezierPath fillRect:gradientRect]; + fraction += step; + } +} + +@end + +@implementation TPLayoutScreenView + +- (instancetype) initWithHostView:(TPLayoutHostView*)hostView screenIndex:(unsigned)screenIndex +{ + self = [super initWithFrame:NSZeroRect]; + + _hostView = hostView; + _screenIndex = screenIndex; + + return self; +} + + +- (TPLayoutHostView*)hostView +{ + return _hostView; +} + +- (NSScreen*)screen +{ + NSArray * screens = [[_hostView host] screens]; + if(_screenIndex < [screens count]) { + return screens[_screenIndex]; + } + else { + return nil; + } +} + +- (unsigned)screenIndex +{ + return _screenIndex; +} + +- (BOOL)isMainScreen +{ + return ([self screenIndex] == 0); +} + +- (void)update +{ + +} + +- (void)drawHostTitleInRect:(NSRect)rect dimmed:(BOOL)dimmed +{ + if(NSWidth(rect) < 1.0 || NSHeight(rect) < 1.0) + return; + + NSImage * image = [[NSImage alloc] initWithSize:rect.size]; + [image setFlipped:YES]; + [image lockFocus]; + + /* Create attributed string */ + NSColor * fontColor; + if(dimmed) + fontColor = [[NSColor whiteColor] colorWithAlphaComponent:0.5]; + else + fontColor = [NSColor whiteColor]; + + NSShadow * shadow = [[NSShadow alloc] init]; + [shadow setShadowBlurRadius:3.0]; + [shadow setShadowColor:[NSColor blackColor]]; + [shadow setShadowOffset:NSMakeSize(1.0,-1.0)]; + + NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + [paragraphStyle setAlignment:NSCenterTextAlignment]; + // [paragraphStyle setLineSpacing:-20.0]; + + NSDictionary * attributes = @{NSFontAttributeName: [NSFont boldSystemFontOfSize:[NSFont systemFontSize]], + NSShadowAttributeName: shadow, + NSForegroundColorAttributeName: fontColor, + NSParagraphStyleAttributeName: paragraphStyle}; + + NSAttributedString * attString = [[NSAttributedString alloc] initWithString:[[_hostView host] computerName] attributes:attributes]; + + /* Layout */ + NSLayoutManager * layoutManager = [[NSLayoutManager alloc] init]; + NSTextContainer * textContainer = [[NSTextContainer alloc] initWithContainerSize:rect.size]; + NSTextStorage * textStorage = [[NSTextStorage alloc] initWithAttributedString:attString]; + + [layoutManager addTextContainer:textContainer]; + + [textStorage addLayoutManager:layoutManager]; + + + NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer]; + + NSRect usedRect = [layoutManager usedRectForTextContainer:textContainer]; + + [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:NSMakePoint(1.0, roundf((NSHeight(rect) - NSHeight(usedRect))/2.0) - 4.0)]; + + + [image unlockFocus]; + [[NSGraphicsContext currentContext] saveGraphicsState]; + [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.5]]; + [shadow set]; + [image drawAtPoint:rect.origin fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + [[NSGraphicsContext currentContext] restoreGraphicsState]; +} + +@end + +@implementation TPLayoutHostView + ++ (Class)screenViewClass +{ + return [TPLayoutScreenView class]; +} + ++ (id)defaultAnimationForKey:(NSString *)key +{ + if([key isEqualToString:NSAnimationTriggerOrderIn] || [key isEqualToString:NSAnimationTriggerOrderOut]) { + return nil; + } + else if([key isEqualToString:@"frameOrigin"]) { + CAAnimation * animation = [CABasicAnimation animation]; + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + return animation; + } + else { + return [super defaultAnimationForKey:key]; + } +} + +- (instancetype) initWithHost:(TPHost*)host layoutView:(TPLayoutView*)layoutView +{ + self = [super initWithFrame:NSZeroRect]; + + _hostIdentifier = [[host identifier] copy]; + _layoutView = layoutView; + + NSArray * screens = [host screens]; + NSEnumerator * screensEnum = [screens objectEnumerator]; + TPScreen * screen; + Class screenViewClass = [[self class] screenViewClass]; + + unsigned screenIndex = 0; + while((screen = [screensEnum nextObject])) { + NSView * screenView = [(TPLayoutScreenView*)[screenViewClass alloc] initWithHostView:self screenIndex:screenIndex]; + [self addSubview:screenView]; + screenIndex++; + } + + NSShadow * shadow = [[NSShadow alloc] init]; + [shadow setShadowBlurRadius:2.0]; + [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.5]]; + [shadow setShadowOffset:NSMakeSize(0,-1)]; + [self setShadow:shadow]; + + + return self; +} + + +- (void)setHostIdentifier:(NSString*)hostIdentifier +{ + if(hostIdentifier != _hostIdentifier) { + _hostIdentifier = hostIdentifier; + } +} + +- (NSString*)hostIdentifier +{ + return _hostIdentifier; +} + +- (TPHost*)host +{ + return [[TPHostsManager defaultManager] hostWithIdentifier:_hostIdentifier]; +} + +- (NSRect)totalFrame +{ + NSArray * screens = [[self host] screens]; + NSRect enclosingRect = NSZeroRect; + NSEnumerator * screensEnum = [screens objectEnumerator]; + TPScreen * screen; + + while((screen = [screensEnum nextObject])) { + NSRect screenRect = [screen frame]; + enclosingRect = NSUnionRect(enclosingRect, screenRect); + } + + return enclosingRect; +} + +- (NSArray*)screenViews +{ + return [self subviews]; +} + +- (unsigned)indexOfScreenAtPoint:(NSPoint)point +{ + NSEnumerator * screenViewsEnum = [[self screenViews] objectEnumerator]; + TPLayoutScreenView * screenView; + while((screenView = [screenViewsEnum nextObject]) != nil) { + if(NSPointInRect(point, [screenView frame])) { + return [screenView screenIndex]; + } + } + + return -1; +} + +- (TPLayoutScreenView*)screenViewAtIndex:(unsigned)index +{ + NSScreen * screen = [[self host] screens][index]; + NSEnumerator * screenViewsEnum = [[self screenViews] objectEnumerator]; + TPLayoutScreenView * screenView; + while((screenView = [screenViewsEnum nextObject]) != nil) { + if([[screenView screen] isEqual:screen]) { + return screenView; + } + } + return nil; +} + +- (NSRect)screenFrameAtIndex:(unsigned)index +{ + TPLayoutScreenView * screenView = [self screenViewAtIndex:index]; + NSRect frame = [screenView frame]; + frame = [self convertRect:frame toView:[self superview]]; + return frame; +} + +- (NSRect)hostFrameFromScreenFrame:(NSRect)frame atIndex:(unsigned)index +{ + TPLayoutScreenView * screenView = [self screenViewAtIndex:index]; + NSRect hostFrame = [self frame]; + NSRect screenFrame = [screenView frame]; + hostFrame.origin.x = NSMinX(frame) - NSMinX(screenFrame); + hostFrame.origin.y = NSMinY(frame) - NSMinY(screenFrame); + return hostFrame; +} + +- (void)updateLayoutWithScaleFactor:(float)scaleFactor +{ + NSRect totalFrame = [self totalFrame]; + + NSRect scaledTotalFrame = TPScaledRect(totalFrame, scaleFactor); + [self setFrameSize:scaledTotalFrame.size]; + + [self layer].masksToBounds = NO; + + NSArray * screenViews = [self screenViews]; + NSEnumerator * screensEnum = [screenViews objectEnumerator]; + TPLayoutScreenView * screenView; + + while((screenView = [screensEnum nextObject])) { + NSScreen * screen = [screenView screen]; + NSRect screenFrame = [screen frame]; + NSRect scaledScreenFrame = TPScaledRect(screenFrame, scaleFactor); + scaledScreenFrame.origin.x -= NSMinX(scaledTotalFrame); + scaledScreenFrame.origin.y -= NSMinY(scaledTotalFrame); + + scaledScreenFrame.origin.x = floor(NSMinX(scaledScreenFrame)); + scaledScreenFrame.origin.y = floor(NSMinY(scaledScreenFrame)); + scaledScreenFrame.size.width = floor(NSWidth(scaledScreenFrame)); + scaledScreenFrame.size.height = floor(NSHeight(scaledScreenFrame)); + + [screenView setFrame:scaledScreenFrame]; + } +} + +@end diff --git a/TPLayoutLocalHostView.h b/TPLayoutLocalHostView.h new file mode 100644 index 0000000..7f07309 --- /dev/null +++ b/TPLayoutLocalHostView.h @@ -0,0 +1,31 @@ +// +// TPLayoutLocalHost.h +// teleport +// +// Created by JuL on Fri Feb 27 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import +#import "TPLayoutHostView.h" +#import "TPHostSnapping.h" + +@class TPLocalHost; + +@interface TPLayoutLocalHostView : TPLayoutHostView +{ + NSScreen * _insideScreen; +} + +- (unsigned)nearestScreenIndexForFrame:(NSRect)frame distance:(float*)distance position:(NSPoint*)hostPosition side:(TPSide*)side; + +#if 0 +- (NSRect)rectForScreen:(NSScreen*)screen; +- (NSRect)drawRectForScreen:(NSScreen*)screen; +- (NSRect)shareScreenRectFromDrawRect:(NSRect)drawRect; +- (NSRect)totalRect; +- (NSSize)totalSize; + +- (NSScreen*)screenAtPoint:(NSPoint)point; +#endif +@end diff --git a/TPLayoutLocalHostView.m b/TPLayoutLocalHostView.m new file mode 100644 index 0000000..82fcb3d --- /dev/null +++ b/TPLayoutLocalHostView.m @@ -0,0 +1,341 @@ +// +// TPLayoutLocalHost.m +// teleport +// +// Created by JuL on Fri Feb 27 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPLayoutLocalHostView.h" +#import "TPLocalHost.h" +#import "TPPreferencesManager.h" + +#define MENU_HEIGHT 5.0 + +//static NSImage * _shareScreenImage = nil; +//static NSImage * _shareScreenOnImage = nil; + +@interface TPLayoutLocalScreenView : TPLayoutScreenView +@end + +@implementation TPLayoutLocalScreenView + +- (void)drawRect:(NSRect)rect +{ + TPLocalHost * localHost = (TPLocalHost*)[_hostView host]; + NSRect drawRect = [self bounds]; + NSScreen * screen = [self screen]; + NSImage * backgroundImage = [localHost backgroundImageForScreen:screen]; + if(backgroundImage != nil) { +#if FLIPPED_VIEW + [backgroundImage setFlipped:YES]; +#endif + [backgroundImage drawInRect:drawRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; +#if FLIPPED_VIEW + [backgroundImage setFlipped:NO]; +#endif + } + else { + [[NSColor colorWithCalibratedRed:30.0/255.0 green:70.0/255.0 blue:140.0/255.0 alpha:1.0] set]; + [[NSBezierPath bezierPathWithRect:drawRect] fill]; + } + + if([self isMainScreen]) { // main screen + NSRect menuRect = drawRect; +#if FLIPPEDVIEW +#else + menuRect.origin.y += (menuRect.size.height - MENU_HEIGHT); +#endif + menuRect.size.height = MENU_HEIGHT; + [[NSGraphicsContext currentContext] saveGraphicsState]; + + CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort]; + CGRect cgRect = *(CGRect*)(&drawRect); + CGContextClipToRect(ctx, cgRect); + NSShadow * shadow = [[NSShadow alloc] init]; + [shadow setShadowBlurRadius:2.0]; + [shadow setShadowColor:[NSColor blackColor]]; + [shadow setShadowOffset:NSMakeSize(0,-1)]; + [shadow set]; + + float alpha; + if([[_hostView host] osVersion] >= TPHostOSVersion(5)) { + alpha = 0.8; + } + else { + alpha = 1.0; + } + + [[NSColor colorWithCalibratedWhite:1.0 alpha:alpha] set]; + [[NSBezierPath bezierPathWithRect:menuRect] fill]; + [[NSGraphicsContext currentContext] restoreGraphicsState]; + + NSRect titleRect = NSInsetRect(drawRect, 2.0, 2.0); + [self drawHostTitleInRect:titleRect dimmed:NO/*(drawMode == DISABLED_MODE)*/]; + } + + [[NSColor colorWithCalibratedWhite:0.2 alpha:1.0] set]; + NSFrameRect(drawRect); +} + +@end + +@implementation TPLayoutLocalHostView + ++ (Class)screenViewClass +{ + return [TPLayoutLocalScreenView class]; +} + +- (TPHost*)host +{ + return [TPLocalHost localHost]; +} + +- (unsigned)nearestScreenIndexForFrame:(NSRect)frame distance:(float*)distance position:(NSPoint*)hostPosition side:(TPSide*)side +{ + unsigned nearestScreenIndex = 0; + float minDist = INFINITY; + NSRect minGluedRect = NSZeroRect; + TPSide minSide = TPUndefSide; + + unsigned screenIndex = 0; + NSArray * screenViews = [self screenViews]; + NSEnumerator * screenViewsEnum = [screenViews objectEnumerator]; + TPLayoutLocalScreenView * localScreenView; + + while((localScreenView = [screenViewsEnum nextObject]) != nil) { + NSRect screenFrame = [[localScreenView screen] frame]; + NSRect localRect = [self convertRect:[localScreenView frame] toView:[self superview]]; + + // Calculate excluded sides + TPSide excludedSides = TPUndefSide; + if(NSIntersectsRect(frame, localRect)) { + NSEnumerator * screenViewsEnum2 = [screenViews objectEnumerator]; + TPLayoutLocalScreenView * localScreenView2; + while((localScreenView2 = [screenViewsEnum2 nextObject]) != nil) { + NSRect screenFrame2 = [[localScreenView2 screen] frame]; + if(NSMinX(screenFrame2) == NSMaxX(screenFrame)) { + excludedSides |= TPRightSide; + } + if(NSMaxX(screenFrame2) == NSMinX(screenFrame)) { + excludedSides |= TPLeftSide; + } + if(NSMinY(screenFrame2) == NSMaxY(screenFrame)) { + excludedSides |= TPTopSide; + } + if(NSMaxY(screenFrame2) == NSMinY(screenFrame)) { + excludedSides |= TPBottomSide; + } + } + } + + NSRect gluedRect; + TPSide side; + float dist = TPGluedRect(&gluedRect, &side, localRect, frame, excludedSides); + + if(dist < minDist) { + minDist = dist; + nearestScreenIndex = screenIndex; + minGluedRect = gluedRect; + minSide = side; + } + + screenIndex++; + } + + if(distance != NULL) { + *distance = minDist; + *hostPosition = minGluedRect.origin; + *side = minSide; + } + + return nearestScreenIndex; +} + +#if 0 ++ (void)initialize +{ + if(self == [TPLayoutLocalHostView class]) { + NSString * imagePath = [[NSBundle bundleForClass:[self class]] pathForImageResource:@"sharedscreen.tiff"]; + _shareScreenImage = [[NSImage alloc] initWithContentsOfFile:imagePath]; + + imagePath = [[NSBundle bundleForClass:[self class]] pathForImageResource:@"sharedscreen-on.tiff"]; + _shareScreenOnImage = [[NSImage alloc] initWithContentsOfFile:imagePath]; + } +} + +- (BOOL)_canChooseSharedScreen +{ + NSArray * screens = [[TPLocalHost localHost] screens]; + return ([_layoutView isActive] && [screens count] > 1 && [[TPPreferencesManager sharedPreferencesManager] boolForPref:ALLOW_CONTROL]); +} + +- (void)setScaleFactor:(float)inScaleFactor +{ + scaleFactor = inScaleFactor; +} + +- (NSRect)rectForScreen:(NSScreen*)screen +{ + if(screen == nil) + return NSZeroRect; + + NSRect screenRect = [screen frame]; + NSRect totalRect = [self totalRect]; + + /* Recenter */ + screenRect.origin.x += (_origin.x - totalRect.origin.x); + screenRect.origin.y += (_origin.y - totalRect.origin.y); + + return screenRect; +} + +- (NSRect)drawRectForScreen:(NSScreen*)screen +{ + if(screen == nil) + return NSZeroRect; + + NSRect screenRect = [self rectForScreen:screen]; + + screenRect.origin.x = FORDRAW_ORIGIN(screenRect.origin.x*scaleFactor); + screenRect.origin.y = FORDRAW_ORIGIN(screenRect.origin.y*scaleFactor); + screenRect.size.width = FORDRAW_SIZE(screenRect.size.width*scaleFactor); + screenRect.size.height = FORDRAW_SIZE(screenRect.size.height*scaleFactor); + + return screenRect; +} + +- (NSRect)shareScreenRectFromDrawRect:(NSRect)drawRect +{ + return NSMakeRect(NSMaxX(drawRect) - 14.0, NSMaxY(drawRect) - 14.0, 12.0, 12.0); +} + + +- (NSRect)totalRect +{ + TPLocalHost * localHost = (TPLocalHost*)[self host]; + NSRect enclosingRect = NSZeroRect; + unsigned screenIndex = 0; + NSScreen * screen; + + while(screen = [localHost screenAtIndex:screenIndex++]) { + NSRect screenRect = [screen frame]; + + enclosingRect = NSUnionRect(enclosingRect, screenRect); + } + + return enclosingRect; +} + +- (NSSize)totalSize +{ + return [self totalRect].size; +} + +- (BOOL)isPointInside:(NSPoint)point +{ + unsigned screenIndex = 0; + NSScreen * screen; + + while(screen = [[self host] screenAtIndex:screenIndex++]) { + NSRect drawRect = [self drawRectForScreen:screen]; + if(NSPointInRect(point, drawRect)) { + return YES; + } + } + + return NO; +} + +- (BOOL)handleMouseDownAtPoint:(NSPoint)point +{ + if([self _canChooseSharedScreen]) { + _insideScreen = [self screenAtPoint:point]; + if(_insideScreen != nil) { + NSRect drawRect = [self drawRectForScreen:_insideScreen]; + NSRect shareScreenRect = [self shareScreenRectFromDrawRect:drawRect]; + if(NSPointInRect(point, shareScreenRect)) { + _insideButton = YES; + return YES; + } + } + } + + return NO; +} + +- (void)handleMouseDraggedAtPoint:(NSPoint)point +{ + if(_insideScreen != nil) { + NSRect drawRect = [self drawRectForScreen:_insideScreen]; + NSRect shareScreenRect = [self shareScreenRectFromDrawRect:drawRect]; + _insideButton = NSPointInRect(point, shareScreenRect); + } +} + +- (void)handleMouseUpAtPoint:(NSPoint)point +{ + if(_insideScreen != nil) { + NSRect drawRect = [self drawRectForScreen:_insideScreen]; + NSRect shareScreenRect = [self shareScreenRectFromDrawRect:drawRect]; + if(NSPointInRect(point, shareScreenRect)) { + [(TPLocalHost*)[self host] setSharedScreenIndex:[[[TPLocalHost localHost] screens] indexOfObject:_insideScreen]]; + } + } + + _insideButton = NO; +} + +- (NSScreen*)screenAtPoint:(NSPoint)point +{ + TPLocalHost * localHost = (TPLocalHost*)[self host]; + unsigned screenIndex = 0; + NSScreen * screen = nil; + + while((screen = [localHost screenAtIndex:screenIndex++]) != nil) { + NSRect drawRect = [self drawRectForScreen:screen]; + if(NSPointInRect(point, drawRect)) { + break; + } + } + + return screen; +} + +- (NSMenu*)menuForEvent:(NSEvent*)event +{ + if([self _canChooseSharedScreen]) { + TPLocalHost * localHost = [TPLocalHost localHost]; + NSMenu * menu = [[NSMenu alloc] init]; + + NSString * title = NSLocalizedStringFromTableInBundle(@"Screen to share", nil, [NSBundle bundleForClass:[self class]], nil); + NSMenuItem * titleMenuItem = [menu addItemWithTitle:title action:nil keyEquivalent:@""]; + [titleMenuItem setEnabled:NO]; + + NSArray * screens = [localHost screens]; + unsigned sharedScreenIndex = [(TPLocalHost*)[self host] sharedScreenIndex]; + unsigned i; + for(i=0; i<[screens count]; i++) { + NSScreen * screen = [screens objectAtIndex:i]; + NSSize screenSize = [screen frame].size; + NSString * title = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"Screen %d - %.0fx%.0f", nil, [NSBundle bundleForClass:[self class]], nil), i+1, screenSize.width, screenSize.height]; + NSMenuItem * screenMenuItem = [menu addItemWithTitle:title action:@selector(changeSharedScreen:) keyEquivalent:@""]; + [screenMenuItem setTag:i]; + [screenMenuItem setState:(i==sharedScreenIndex)?NSOnState:NSOffState]; + [screenMenuItem setTarget:self]; + } + + return [menu autorelease]; + } + else { + return [super menuForEvent:event]; + } +} + +- (void)changeSharedScreen:(NSMenuItem*)sender +{ + [(TPLocalHost*)[self host] setSharedScreenIndex:[sender tag]]; +} +#endif +@end diff --git a/TPLayoutRemoteHostView.h b/TPLayoutRemoteHostView.h new file mode 100644 index 0000000..0d83c80 --- /dev/null +++ b/TPLayoutRemoteHostView.h @@ -0,0 +1,79 @@ +// +// TPLayoutRemoteHost.h +// PrefsPanel +// +// Created by JuL on Mon Dec 08 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import +#import "TPLayoutHostView.h" +#import "TPHostSnapping.h" + +@class TPRemoteHost; + +@interface TPLayoutRemoteScreenView : TPLayoutScreenView +{ + NSTrackingArea * _trackingArea; + NSButton * _optionsButton; + NSButton * _unpairButton; + + unsigned _cachedBackgroundHash; + NSImage * _cachedBackgroundImage; +} + +- (void)_setupButtons; +- (void)_updateUnpairButton; +- (void)_updateOptionsButton; +@property (nonatomic, readonly) BOOL _canShowOptionsButton; + +@property (nonatomic, getter=isSharedScreen, readonly) BOOL sharedScreen; + +@end + +@interface TPLayoutRemoteHostView : TPLayoutHostView +{ + NSString * _remoteHostIdentifier; + + BOOL _dragging; + BOOL _snapped; + + NSPoint _deltaDrag; + + NSPoint _snappedPosition; + unsigned _snappedScreenIndex; + unsigned _draggingScreenIndex; +} + +@property (nonatomic) unsigned int draggingScreenIndex; + +- (NSPoint)adjustedScreenPositionFromPosition:(NSPoint)position localScreenIndex:(unsigned)screenIndex side:(TPSide)side; +- (void)pairToScreenIndex:(int)screenIndex atPosition:(NSPoint)position ofSide:(TPSide)side; +- (void)unpair; +@property (nonatomic, readonly) BOOL canUnpair; + +- (void)update; + +#if 0 +- (NSRect)rectWithSnapping:(BOOL)snapping side:(TPSide*)side; +- (NSRect)drawRectWithSnapping:(BOOL)snapping; +- (NSRect)unpairRectFromDrawRect:(NSRect)drawRect; + +- (void)moveToPoint:(NSPoint)point; + +- (NSPoint)snappedPosition; +- (unsigned)snappedScreenIndex; + +- (void)startDraggingAtPoint:(NSPoint)dragPoint; +- (void)stopDragging; +- (BOOL)isDragging; + +- (void)snapToScreenIndex:(int)screenIndex atPosition:(NSPoint)position ofSide:(TPSide)side; +- (void)unsnap; +- (BOOL)isSnapped; + +- (void)pair; +- (void)unpair; +- (BOOL)canUnpair; +#endif +@end diff --git a/TPLayoutRemoteHostView.m b/TPLayoutRemoteHostView.m new file mode 100644 index 0000000..776a59e --- /dev/null +++ b/TPLayoutRemoteHostView.m @@ -0,0 +1,796 @@ +// +// TPLayoutRemoteHost.m +// PrefsPanel +// +// Created by JuL on Mon Dec 08 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPLayoutRemoteHostView.h" +#import "TPRemoteHost.h" +#import "TPLocalHost.h" +#import "TPBezierPath.h" +#import "TPPreferencesManager.h" +#import "TPOptionsController.h" +#import "TPLayoutLocalHostView.h" +#import "TPAuthenticationManager.h" + +#import "PTKeyComboPanel.h" +#import "PTKeyCombo.h" +#import "PTHotKey.h" + +#define TEXT_MARGIN 6 + +@interface TPOptionsButtonCell : NSButtonCell +@end + +@implementation TPOptionsButtonCell + +- (void)drawBezelWithFrame:(NSRect)frame inView:(NSView *)controlView +{ + [[NSColor colorWithCalibratedWhite:0.0 alpha:0.5] set]; + [[NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 1.0, 3.0) xRadius:6.0 yRadius:6.0] fill]; + + [super drawBezelWithFrame:frame inView:controlView]; +} + +@end + + +static NSImage * _lockImage = nil; +static NSImage * _unpairImage = nil; + +@interface TPLayoutRemoteHostView (Internal) + +- (void)hostDidUpdate:(NSNotification*)notification; + +@end + +@implementation TPLayoutRemoteScreenView + +- (instancetype) initWithHostView:(TPLayoutHostView*)hostView screenIndex:(unsigned)screenIndex +{ + self = [super initWithHostView:hostView screenIndex:screenIndex]; + + [self _setupButtons]; + + return self; +} + +#pragma mark - +#pragma mark Actions + +- (void)_setupButtons +{ + _optionsButton = [[NSButton alloc] initWithFrame:NSZeroRect]; + TPOptionsButtonCell * cell = [[TPOptionsButtonCell alloc] initTextCell:@""]; + [_optionsButton setCell:cell]; + [cell setControlSize:NSMiniControlSize]; + [_optionsButton setBezelStyle:NSRecessedBezelStyle]; + [_optionsButton setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]]; + [_optionsButton setTitle:NSLocalizedString(@"Options..", @"Title of options button")]; + [_optionsButton setTarget:self]; + [_optionsButton setAction:@selector(showOptionsPanel:)]; + [_optionsButton sizeToFit]; + [_optionsButton setHidden:YES]; + + NSRect bounds = [self bounds]; + NSRect frame = [_optionsButton frame]; + frame.origin = NSMakePoint(floor((NSWidth(bounds) - NSWidth(frame))/2.0), 4.0); + [_optionsButton setFrame:frame]; + [_optionsButton setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin|NSViewMaxYMargin]; + + [self addSubview:_optionsButton]; + + _trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInKeyWindow|NSTrackingInVisibleRect owner:self userInfo:nil]; + [self addTrackingArea:_trackingArea]; + + _unpairButton = [[NSButton alloc] initWithFrame:NSZeroRect]; + [_unpairButton setBordered:NO]; + [_unpairButton setBezelStyle:NSRegularSquareBezelStyle]; + [_unpairButton setButtonType:NSMomentaryChangeButton]; + //[[_unpairButton cell] setControlSize:NSMiniControlSize]; + [_unpairButton setImage:_unpairImage]; + [_unpairButton setTarget:_hostView]; + [_unpairButton setAction:@selector(unpair)]; + [_unpairButton sizeToFit]; + [_unpairButton setHidden:YES]; + + frame = [_unpairButton frame]; + frame.origin = NSMakePoint(NSMinX(bounds) + 4.0, NSMaxY(bounds) - NSHeight(frame) - 4.0); + [_unpairButton setFrame:frame]; + [_unpairButton setAutoresizingMask:NSViewMaxXMargin|NSViewMinYMargin]; + + [self addSubview:_unpairButton]; +} + + +- (void)_updateUnpairButton +{ + if([(TPLayoutRemoteHostView*)_hostView canUnpair] && [self isSharedScreen]) + [_unpairButton setHidden:NO]; + else + [_unpairButton setHidden:YES]; +} + +- (void)_updateOptionsButton +{ + if([self _canShowOptionsButton]) { + NSPoint location = [NSEvent mouseLocation]; + location = [[self window] convertScreenToBase:location]; + location = [self convertPoint:location fromView:nil]; + + if(NSPointInRect(location, [self bounds])) { + [[_optionsButton animator] setHidden:NO]; + } + } + else { + [[_optionsButton animator] setHidden:YES]; + } +} + +- (void)update +{ + BOOL drawDimmed = NO; + unsigned draggingScreenIndex = [(TPLayoutRemoteHostView*)_hostView draggingScreenIndex]; + if(draggingScreenIndex == -1) { + drawDimmed = [(TPRemoteHost*)[_hostView host] isInState:TPHostPeeredState] && ![self isSharedScreen]; + } + else { + drawDimmed = (draggingScreenIndex != [self screenIndex]); + } + + if(drawDimmed) { + [self setAlphaValue:0.25]; + } + else { + [self setAlphaValue:1.0]; + } + + [self _updateUnpairButton]; + [self _updateOptionsButton]; + [self setNeedsDisplay:YES]; +} + +- (BOOL)isSharedScreen +{ + return ([(TPRemoteHost*)[_hostView host] sharedScreenIndex] == [self screenIndex]); +} + +- (BOOL)isDraggingScreen +{ + return ([(TPLayoutRemoteHostView*)_hostView draggingScreenIndex] == [self screenIndex]); +} + +- (void)mouseEntered:(NSEvent*)event +{ + [self _updateOptionsButton]; +} + +- (void)mouseExited:(NSEvent*)event +{ + [[_optionsButton animator] setHidden:YES]; +} + +- (BOOL)_canShowOptionsButton +{ + if(![self isSharedScreen]) { + return NO; + } + + TPRemoteHost * host = (TPRemoteHost*)[_hostView host]; + return [host isInState:TPHostPeeredOfflineState|TPHostPeeredOnlineState]; +} + +- (void)showOptionsPanel:(id)sender +{ + [self mouseExited:nil]; + + NSRect frame = [sender bounds]; + frame = [sender convertRect:frame toView:nil]; + frame.origin = [[sender window] convertBaseToScreen:frame.origin]; + + [[TPOptionsController controller] showOptionsForHost:(TPRemoteHost*)[_hostView host] sharedScreenIndex:[self screenIndex] fromRect:frame]; +} + + +#pragma mark - +#pragma mark Drawing + +- (BOOL)drawDecorations +{ + TPRemoteHost * host = (TPRemoteHost*)[_hostView host]; + BOOL isDragging = ([(TPLayoutRemoteHostView*)_hostView draggingScreenIndex] != -1); + BOOL drawDecorations; + + if(isDragging) { + drawDecorations = [self isDraggingScreen]; + } + else if([host isInState:TPHostPeeredState]) { + drawDecorations = [self isSharedScreen]; + } + else { + drawDecorations = [self isMainScreen]; + } + + return drawDecorations; +} + +- (void)drawRect:(NSRect)rect +{ + TPRemoteHost * host = (TPRemoteHost*)[_hostView host]; + TPHostState hostState = [host hostState]; + NSRect drawRect = [self bounds]; + NSRect titleRect = NSInsetRect(drawRect, 2.0, 2.0); + BOOL drawDecorations = [self drawDecorations]; + + if(_cachedBackgroundImage == nil) { + NSData * data = [host backgroundImageData]; + if(data != nil) { + _cachedBackgroundHash = [data hash]; + _cachedBackgroundImage = [[NSImage alloc] initWithData:data]; + } + } + else { + unsigned hash = [[host backgroundImageData] hash]; + if(hash != _cachedBackgroundHash) { + _cachedBackgroundImage = nil; + [self drawRect:rect]; + return; + } + } + + float widthInset = 0.0; + + switch(hostState) { + case TPHostPeeredOfflineState: + { + [NSBezierPath drawRect:drawRect withGradientFrom:[NSColor blackColor] to:[NSColor colorWithCalibratedWhite:0.2 alpha:1.0]]; + break; + } + case TPHostControlledState: + case TPHostPeeredOnlineState: + case TPHostSharedState: + case TPHostPeeringState: + { + [_cachedBackgroundImage drawInRect:drawRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + + if(drawDecorations && hostState == TPHostControlledState) { + NSImage * cursorImage = [[NSCursor arrowCursor] image]; + NSRect cursorRect = NSZeroRect; + cursorRect.size = [cursorImage size]; + + NSRect mouseRect = cursorRect; + mouseRect.origin = drawRect.origin; + mouseRect.origin.x += (drawRect.size.width - cursorRect.size.width)/2; + mouseRect.origin.y += (drawRect.size.height - cursorRect.size.height)/2; + + [cursorImage drawInRect:mouseRect fromRect:cursorRect operation:NSCompositeSourceOver fraction:1.0]; + } + + if(drawDecorations && [[TPPreferencesManager sharedPreferencesManager] boolForPref:ENABLED_ENCRYPTION]) { + if([host hasCapability:TPHostEncryptionCapability]) { + NSPoint lockPoint = NSMakePoint(NSMaxX(drawRect) - 15.0, NSMaxY(drawRect) - 15.0); + [_lockImage drawAtPoint:lockPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + widthInset = 14.0; + } + } + break; + } + case TPHostIncompatibleState: + { + [NSBezierPath drawRect:drawRect withGradientFrom:[NSColor colorWithCalibratedRed:0.15 green:0.0 blue:0.0 alpha:1.0] to:[NSColor colorWithCalibratedRed:0.35 green:0.0 blue:0.0 alpha:1.0]]; + break; + } + default: + break; + } + + // if([self canUnpair]) { + // NSRect unpairRect = [self unpairRectFromDrawRect:drawRect]; + // [_unpairImage compositeToPoint:unpairRect.origin operation:NSCompositeSourceOver fraction:_insideButton?1.0:0.75]; + // widthInset = 14.0; + // } + + if(widthInset > 0.0) { + titleRect = NSInsetRect(titleRect, widthInset, 0.0); + } + + if(drawDecorations) { + [self drawHostTitleInRect:titleRect dimmed:(hostState == TPHostPeeredOfflineState || hostState == TPHostIncompatibleState)]; + } + + [[NSColor colorWithCalibratedWhite:0.2 alpha:1.0] set]; + NSFrameRect(drawRect); +} + +@end + +@implementation TPLayoutRemoteHostView + ++ (void)initialize +{ + if(self == [TPLayoutRemoteHostView class]) { + NSString * imagePath = [[NSBundle bundleForClass:[self class]] pathForImageResource:@"Lock_White"]; + _lockImage = [[NSImage alloc] initWithContentsOfFile:imagePath]; + + imagePath = [[NSBundle bundleForClass:[self class]] pathForImageResource:@"remove.tiff"]; + _unpairImage = [[NSImage alloc] initWithContentsOfFile:imagePath]; + } +} + ++ (Class)screenViewClass +{ + return [TPLayoutRemoteScreenView class]; +} + +- (instancetype) initWithHost:(TPHost*)host layoutView:(TPLayoutView*)layoutView +{ + _draggingScreenIndex = -1; + + self = [super initWithHost:host layoutView:layoutView]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hostDidUpdate:) name:TPHostDidUpdateNotification object:host]; + + [self setToolTip:[host computerName]]; + + //[[self screenViews] _setupButtons]; +// [self setToolTip:[host address]]; + + return self; +} + +- (void)dealloc +{ + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; +} + + +- (void)setDraggingScreenIndex:(unsigned)draggingScreenIndex +{ + _draggingScreenIndex = draggingScreenIndex; + [self hostDidUpdate:nil]; +} + +- (unsigned)draggingScreenIndex +{ + return _draggingScreenIndex; +} + + +#pragma mark - +#pragma mark Pairing + +- (NSPoint)adjustedScreenPositionFromPosition:(NSPoint)position localScreenIndex:(unsigned)screenIndex side:(TPSide)side +{ + TPRemoteHost * host = (TPRemoteHost*)[self host]; + NSRect localScreenFrame = [[[TPLocalHost localHost] screenAtIndex:screenIndex] frame]; + + NSPoint snappedPosition = NSZeroPoint; + + NSRect hostRect = [host hostRect]; + + if((side & TPRightSide) != 0) { + snappedPosition.x = NSMaxX(localScreenFrame); + snappedPosition.y = round(position.y); + } + if((side & TPLeftSide) != 0) { + snappedPosition.x = NSMinX(localScreenFrame) - NSWidth(hostRect); + snappedPosition.y = round(position.y); + } + if((side & TPBottomSide) != 0) { + if(snappedPosition.x == 0.0) + snappedPosition.x = round(position.x); + snappedPosition.y = NSMinY(localScreenFrame) - NSHeight(hostRect); + } + if((side & TPTopSide) != 0) { + if(snappedPosition.x == 0.0) + snappedPosition.x = round(position.x); + snappedPosition.y = NSMaxY(localScreenFrame); + } + + snappedPosition.x = MAX(MIN(snappedPosition.x, NSMaxX(localScreenFrame)), NSMinX(localScreenFrame) - NSWidth(hostRect)); + snappedPosition.y = MAX(MIN(snappedPosition.y, NSMaxY(localScreenFrame)), NSMinY(localScreenFrame) - NSHeight(hostRect)); + + return snappedPosition; +} + +- (void)pairToScreenIndex:(int)screenIndex atPosition:(NSPoint)position ofSide:(TPSide)side +{ + TPRemoteHost * host = (TPRemoteHost*)[self host]; + + [host setSharedScreenIndex:_draggingScreenIndex]; + [host setLocalScreenIndex:screenIndex]; + + NSPoint snappedPosition = [self adjustedScreenPositionFromPosition:position localScreenIndex:screenIndex side:side]; + + [host setHostPosition:snappedPosition]; + + DebugLog(@"pairing %@ to screen index %d with shared screen index %d position %@", host, screenIndex, _draggingScreenIndex, NSStringFromPoint(snappedPosition)); + + if([host isInState:TPHostSharedState]) { + [host setHostState:TPHostPeeringState]; + [[TPAuthenticationManager defaultManager] requestAuthenticationOnHost:host]; + } +} + +- (void)unpair +{ + TPRemoteHost * host = (TPRemoteHost*)[self host]; + + DebugLog(@"unpairing host %@", host); + + if([host isInState:TPHostOnlineState]) { + [host setHostState:TPHostSharedState]; + } + else { + [host setHostState:TPHostUndefState]; + } +} + +- (BOOL)canUnpair +{ + TPRemoteHost * host = (TPRemoteHost*)[self host]; + return [host isInState:TPHostPeeredOfflineState|TPHostPeeredOnlineState]; +} + +- (void)update +{ + NSEnumerator * screenViewsEnum = [[self screenViews] objectEnumerator]; + TPLayoutScreenView * localScreenView; + + while((localScreenView = [screenViewsEnum nextObject]) != nil) { + [localScreenView update]; + } +} + +- (void)hostDidUpdate:(NSNotification*)notification +{ + [self update]; +} + +#if 0 + + +- initWithHost:(TPHost*)host layoutView:(TPLayoutView*)layoutView +{ + self = [super initWithHost:host layoutView:layoutView]; + + _remoteHostIdentifier = [[(TPRemoteHost*)host identifier] copy]; + _cachedBackgroundImage = nil; + + return self; +} + +- (void)dealloc +{ + [_remoteHostIdentifier release]; + [_cachedBackgroundImage release]; + [super dealloc]; +} + +- (NSRect)rectWithSnapping:(BOOL)snapping side:(TPSide*)side +{ + NSRect rect = NSZeroRect; + TPRemoteHost * host = (TPRemoteHost*)[self host]; + + rect.size = [host screenSize]; + + if([self isSnapped] && snapping) { + rect.origin = [self snappedPosition]; + NSScreen * screen = [host screenAtIndex:[self snappedScreenIndex]]; + + if(side != NULL) { + TPGluedRect(NULL, side, [screen frame], rect); + } + } + + rect.origin.x += _origin.x; + rect.origin.y += _origin.y; + + return rect; +} + +- (NSRect)drawRectWithSnapping:(BOOL)snapping +{ + NSRect drawRect; + TPSide side = TPUndefSide; + NSRect rect = [self rectWithSnapping:snapping side:&side]; + + drawRect.origin.x = FORDRAW_ORIGIN(rect.origin.x*scaleFactor); + drawRect.origin.y = FORDRAW_ORIGIN(rect.origin.y*scaleFactor); + drawRect.size.height = FORDRAW_SIZE(rect.size.height*scaleFactor); + drawRect.size.width = FORDRAW_SIZE(rect.size.width*scaleFactor); + + // Align on borders + if(side != TPUndefSide) { + NSRect localScreenDrawRect = [[_layoutView layoutLocalHost] drawRectForScreen:[(TPRemoteHost*)[self host] screenAtIndex:[self snappedScreenIndex]]]; + if((side & TPRightSide) != 0) + drawRect.origin.x = NSMaxX(localScreenDrawRect) - 1.0; + else if((side & TPLeftSide) != 0) + drawRect.origin.x = NSMinX(localScreenDrawRect) - NSWidth(drawRect) + 1.0; + if((side & TPTopSide) != 0) + drawRect.origin.y = NSMaxY(localScreenDrawRect) - 1.0; + else if((side & TPBottomSide) != 0) + drawRect.origin.y = NSMinY(localScreenDrawRect) - NSHeight(drawRect) + 1.0; + } + + return drawRect; +} + +- (NSRect)unpairRectFromDrawRect:(NSRect)drawRect +{ + return NSMakeRect(NSMinX(drawRect) + 2.0, NSMaxY(drawRect) - 14.0, 12.0, 12.0); +} + +- (void)moveToPoint:(NSPoint)point +{ + _origin.x = point.x/scaleFactor + _deltaDrag.x; + _origin.y = point.y/scaleFactor + _deltaDrag.y; + + //DebugLog(@"new pos:%d", layout.position); +} + +- (NSPoint)snappedPosition +{ + if(_snapped) { + return _snappedPosition; + } + else { + return [(TPRemoteHost*)[self host] adjustedHostRect].origin; + } +} + +- (unsigned)snappedScreenIndex +{ + if(_snapped) { + return _snappedScreenIndex; + } + else { + return [(TPRemoteHost*)[self host] localScreenIndex]; + } +} + + + +#pragma mark - +#pragma mark Events + +- (void)mouseDown:(NSEvent*)event +{ + NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; + + if(![(TPRemoteHost*)[self host] isInState:TPHostDraggableState]) { + NSBeep(); + } + else { + _dragging = YES; + + TPRemoteHost * host = (TPRemoteHost*)[self host]; + _snapped = [host isInState:TPHostPeeredState]; + if(_snapped) { + _snappedPosition = [host adjustedHostRect].origin; + _snappedScreenIndex = [host localScreenIndex]; + } + + NSPoint origin = [self rectWithSnapping:YES side:NULL].origin; + _deltaDrag.x = origin.x - dragPoint.x/scaleFactor; + _deltaDrag.y = origin.y - dragPoint.y/scaleFactor; + + + NSEvent * event = nil; + while(1) { + event = [NSApp nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]; + point = [self convertPoint:[event locationInWindow] fromView:nil]; + + if([event type] == NSLeftMouseDragged) { + NSPoint localPoint = [self convertPoint:point fromView:nil]; + + [self moveToPoint:localPoint]; + + NSRect remoteRect = [self drawRectWithSnapping:NO]; + + unsigned screenIndex = 0; + TPLocalHost * localHost = (TPLocalHost*)[_layoutLocalHost host]; + NSScreen * screen; + float minDist = INFINITY; + unsigned nearestScreenIndex = 0; + NSRect minGluedRect = NSZeroRect; + TPSide minSide = TPLeftSide; + + while(screen = [localHost screenAtIndex:screenIndex++]) { + NSRect localRect = [_layoutLocalHost drawRectForScreen:screen]; + + NSRect gluedRect; + TPSide side; + float dist = TPGluedRect(&gluedRect, &side, localRect, remoteRect); + if(dist < minDist) { + minDist = dist; + nearestScreenIndex = screenIndex-1; + minGluedRect = gluedRect; + minSide = side; + } + } + + // DebugLog(@"minDist=%f", minDist); + if(minDist < SQUARED_DISTANCE_TO_SNAP) { + NSPoint hostPosition = minGluedRect.origin; + hostPosition.x = (hostPosition.x/_scaleFactor - localHostPosition.x); + hostPosition.y = (hostPosition.y/_scaleFactor - localHostPosition.y); + [self snapToScreenIndex:nearestScreenIndex atPosition:hostPosition ofSide:minSide]; + } + else { + [self unsnap]; + } + + [self setFrame:[self drawRectWithSnapping:NO]]; + } + else { + break; + } + + [self display]; + } + + +// if(_scroller != nil) +// [_scroller setHidden:YES]; + } +} + + +- (BOOL)handleMouseDownAtPoint:(NSPoint)point +{ + if([self canUnpair]) { + NSRect drawRect = [self drawRectWithSnapping:YES]; + NSRect unpairRect = [self unpairRectFromDrawRect:drawRect]; + if(NSPointInRect(point, unpairRect)) { + _insideButton = YES; + return YES; + } + } + + return NO; +} + +- (void)handleMouseDraggedAtPoint:(NSPoint)point +{ + if([self canUnpair]) { + NSRect drawRect = [self drawRectWithSnapping:YES]; + NSRect unpairRect = [self unpairRectFromDrawRect:drawRect]; + _insideButton = NSPointInRect(point, unpairRect); + } +} + +- (void)handleMouseUpAtPoint:(NSPoint)point +{ + if([self canUnpair]) { + NSRect drawRect = [self drawRectWithSnapping:YES]; + NSRect unpairRect = [self unpairRectFromDrawRect:drawRect]; + if(NSPointInRect(point, unpairRect)) { + [self unpair]; + } + } + + _insideButton = NO; +} + +- (void)startDraggingAtPoint:(NSPoint)dragPoint +{ + _dragging = YES; + + TPRemoteHost * host = (TPRemoteHost*)[self host]; + _snapped = [host isInState:TPHostPeeredState]; + if(_snapped) { + _snappedPosition = [host adjustedHostRect].origin; + _snappedScreenIndex = [host localScreenIndex]; + } + + NSPoint origin = [self rectWithSnapping:YES side:NULL].origin; + _deltaDrag.x = origin.x - dragPoint.x/scaleFactor; + _deltaDrag.y = origin.y - dragPoint.y/scaleFactor; +} + +- (void)stopDragging +{ + TPRemoteHost * host = (TPRemoteHost*)[self host]; + + if([self isSnapped]) { + [host setHostPosition:_snappedPosition]; + [host setLocalScreenIndex:_snappedScreenIndex]; + + if([host isInState:TPHostSharedState]) { + [self pair]; + } + } + else { + if([host isInState:TPHostPeeredState]) { + [self unpair]; + } + } + + _dragging = NO; + _snapped = NO; +} + +- (BOOL)isDragging +{ + return _dragging; +} + + +#pragma mark - +#pragma mark Snapping + +- (void)snapToScreenIndex:(int)screenIndex atPosition:(NSPoint)position ofSide:(TPSide)side +{ + TPRemoteHost * host = (TPRemoteHost*)[self host]; + NSRect localScreenFrame = [[host screenAtIndex:screenIndex] frame]; + + _snappedScreenIndex = screenIndex; + _snappedPosition = NSZeroPoint; + + NSRect hostRect = [host adjustedHostRect]; + + if((side & TPRightSide) != 0) { + _snappedPosition.x = NSMaxX(localScreenFrame); + _snappedPosition.y = round(position.y); + } + if((side & TPLeftSide) != 0) { + _snappedPosition.x = localScreenFrame.origin.x - hostRect.size.width; + _snappedPosition.y = round(position.y); + } + if((side & TPBottomSide) != 0) { + if(_snappedPosition.x == 0.0) + _snappedPosition.x = round(position.x); + _snappedPosition.y = localScreenFrame.origin.y - hostRect.size.height; + } + if((side & TPTopSide) != 0) { + if(_snappedPosition.x == 0.0) + _snappedPosition.x = round(position.x); + _snappedPosition.y = NSMaxY(localScreenFrame); + } + + _snappedPosition.x = MAX(MIN(_snappedPosition.x, NSMaxX(localScreenFrame)), NSMinX(localScreenFrame) - NSWidth(hostRect)); + _snappedPosition.y = MAX(MIN(_snappedPosition.y, NSMaxY(localScreenFrame)), NSMinY(localScreenFrame) - NSHeight(hostRect)); + + DebugLog(@"side=%d position=%@, _snappedPosition=%@, localScreenFrame=%@", side, NSStringFromPoint(position), NSStringFromPoint(_snappedPosition), NSStringFromRect(localScreenFrame)); + + _snapped = YES; +} + +- (void)unsnap +{ + _snapped = NO; +} + +- (BOOL)isSnapped +{ + return (_dragging && _snapped) || (!_dragging && [(TPRemoteHost*)[self host] isInState:TPHostPeeredState]); +} + +- (TPHost*)host +{ + return [[TPHostsManager defaultManager] hostWithIdentifier:_remoteHostIdentifier]; +} + +- (BOOL)isPointInside:(NSPoint)point +{ + return NSPointInRect(point, [self drawRectWithSnapping:YES]); +} + +- (NSMenu*)menuForEvent:(NSEvent*)event +{ + if([_layoutView isActive] && [(TPRemoteHost*)[self host] isInState:TPHostPeeredState]) { + NSMenu * menu = [[NSMenu alloc] init]; + NSMenuItem * deletePeeringMenuItem = [menu addItemWithTitle:NSLocalizedStringFromTableInBundle(@"Delete pairing", nil, [NSBundle bundleForClass:[self class]], nil) action:@selector(unpair) keyEquivalent:@""]; + [deletePeeringMenuItem setTarget:self]; + + return [menu autorelease]; + } + else { + return [super menuForEvent:event]; + } +} +#endif +@end diff --git a/TPLayoutView.h b/TPLayoutView.h new file mode 100644 index 0000000..0c98831 --- /dev/null +++ b/TPLayoutView.h @@ -0,0 +1,41 @@ +// +// TPLayoutView.h +// teleport +// +// Created by JuL on Thu Feb 26 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import +#import + +@class TPLayoutLocalHostView, TPLayoutRemoteHostView, TPRemoteHost; + +extern NSRect TPScaledRect(NSRect rect, float scale); + +@interface TPLayoutView : NSView +{ + IBOutlet id delegate; + IBOutlet NSTextField * infoLabel; + + float _scaleFactor; + float _layoutHeight; + NSTimeInterval _snapTime; + + TPLayoutLocalHostView * _localHostView; + NSMutableDictionary * _remoteHostsViews; + + NSScroller * _scroller; + float _scrollerDeltaX; + + CALayer * _separationLine; + CALayer * _overlayLayer; + + NSWindow * _optionsWindow; +} + +- (TPLayoutRemoteHostView*)remoteHostViewForHost:(TPRemoteHost*)host; + +- (void)updateLayout; + +@end diff --git a/TPLayoutView.m b/TPLayoutView.m new file mode 100644 index 0000000..68140d1 --- /dev/null +++ b/TPLayoutView.m @@ -0,0 +1,612 @@ +// +// TPLayoutView.m +// teleport +// +// Created by JuL on Thu Feb 26 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPLayoutView.h" +#import "TPRemoteHost.h" +#import "TPLocalHost.h" + +#import "TPLayoutRemoteHostView.h" +#import "TPLayoutLocalHostView.h" + +#import "TPAnimationManager.h" +#import "TPHostAnimationController.h" + +#import "TPPreferencePane.h" +#import "TPHostsManager.h" +#import "TPHostSnapping.h" + +#define DEFAULT_SCALE 0.1 +#define DEFAULT_HEIGHT 100 +#define HOST_MARGIN 8 +#define LINE_MARGIN 0 +#define LINE_THICKNESS 1.0 + +#define DEBUG_SNAP 0 +#define SNAP_DURATION 2.0 +#define BOTTOM_MARGIN 20 + +NSRect TPScaledRect(NSRect rect, float scale) +{ + NSRect scaledRect = rect; + scaledRect.origin.x *= scale; + scaledRect.origin.y *= scale; + scaledRect.size.width *= scale; + scaledRect.size.height *= scale; + return scaledRect; +} + +@implementation TPLayoutView + +- (instancetype)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if(self) { + _scaleFactor = DEFAULT_SCALE; + + self.wantsLayer = YES; + + _localHostView = [[TPLayoutLocalHostView alloc] initWithHost:[TPLocalHost localHost] layoutView:self]; + [self addSubview:_localHostView]; + _remoteHostsViews = [[NSMutableDictionary alloc] init]; + +// _layoutHosts = [[NSMutableDictionary alloc] init]; +// _layoutLocalHost = [[TPLayoutLocalHostView alloc] initWithHost:[TPLocalHost localHost] layoutView:self]; +// [self addSubview:_layoutLocalHost]; +// _scrollerDeltaX = 0.0; +// _scroller = nil; + + } + return self; +} + + +- (void)awakeFromNib +{ + [self setWantsLayer:YES]; + CALayer *layer = [CALayer layer]; + self.layer = layer; + + CGColorRef backgroundColor = CGColorCreateGenericGray(1.0, 0.5); + layer.backgroundColor = backgroundColor; + CFRelease(backgroundColor); + + layer.borderWidth = 1.0; + + CGColorRef borderColor = CGColorCreateGenericGray(0.5, 1.0); + layer.borderColor = borderColor; + layer.cornerRadius = 4.0; + CFRelease(borderColor); +// [self performSelector:@selector(setWantsLayer:) withObject:[NSNumber numberWithBool:YES]]; + + [self updateLayout]; +} + + +#pragma mark - +#pragma mark Updating + +- (NSArray*)remoteHosts +{ + return [[TPHostsManager defaultManager] hostsWithState:(TPHostAllStates & ~TPHostUndefState)]; +} + +- (TPLayoutRemoteHostView*)remoteHostViewForHost:(TPRemoteHost*)host +{ + return _remoteHostsViews[[host identifier]]; +} + + +#pragma mark - +#pragma mark Layouting + +- (void)_addSubviewWithFadeIn:(NSView*)subview +{ + [NSAnimationContext beginGrouping]; + [[NSAnimationContext currentContext] setDuration:0.0]; + [subview setAlphaValue:0.0]; + [NSAnimationContext endGrouping]; + [self addSubview:subview]; + [[subview animator] setAlphaValue:1.0]; +} + +- (void)_updateViews +{ + NSArray * hosts = [[self remoteHosts] hostsWithState:(TPHostSharedState|TPHostIncompatibleState|TPHostPeeredState)]; + NSEnumerator * hostsEnum = [hosts objectEnumerator]; + TPRemoteHost * host; + + NSMutableSet * removedHosts = [[NSMutableSet alloc] initWithArray:[_remoteHostsViews allKeys]]; + + while((host = [hostsEnum nextObject])) { + NSString * remoteHostIdentifier = [host identifier]; + TPLayoutRemoteHostView * remoteHostView = _remoteHostsViews[remoteHostIdentifier]; + if(remoteHostView == nil) { + remoteHostView = [[TPLayoutRemoteHostView alloc] initWithHost:host layoutView:self]; + [remoteHostView update]; + _remoteHostsViews[remoteHostIdentifier] = remoteHostView; + [self _addSubviewWithFadeIn:remoteHostView]; + } + else { + [remoteHostView setHostIdentifier:remoteHostIdentifier]; + [removedHosts removeObject:remoteHostIdentifier]; + } + } + + NSEnumerator * removedHostsEnum = [removedHosts objectEnumerator]; + NSString * removedHostKey; + + while((removedHostKey = [removedHostsEnum nextObject])) { + TPLayoutRemoteHostView * remoteHostView = _remoteHostsViews[removedHostKey]; + [remoteHostView removeFromSuperview]; + [_remoteHostsViews removeObjectForKey:removedHostKey]; + } + +} + +- (void)_updateScaleFactor +{ + NSRect bounds = [self bounds]; + + NSRect localHostRect = [_localHostView totalFrame]; + + NSSize maxHostSize = NSZeroSize; + NSSize maxOnlineHostSize = NSZeroSize; + NSEnumerator * hostViewsEnum = [_remoteHostsViews objectEnumerator]; + TPLayoutRemoteHostView * hostView; + + while((hostView = [hostViewsEnum nextObject])) { + TPRemoteHost * host = (TPRemoteHost*)[hostView host]; + NSRect hostRect = [hostView totalFrame]; + maxHostSize.width = MAX(maxHostSize.width, NSWidth(hostRect)); + maxHostSize.height = MAX(maxHostSize.height, NSHeight(hostRect)); + + if([host isInState:TPHostOnlineState|TPHostPeeredState]) { + maxOnlineHostSize.width = MAX(maxOnlineHostSize.width, NSWidth(hostRect)); + maxOnlineHostSize.height = MAX(maxOnlineHostSize.height, NSHeight(hostRect)); + } + } + + if(!NSEqualSizes(maxHostSize, NSZeroSize)) { + float scaleFactorV = ((NSHeight(bounds) - BOTTOM_MARGIN) - 4*HOST_MARGIN - LINE_THICKNESS)/(3*maxHostSize.height + NSHeight(localHostRect)); + float scaleFactorH = (NSWidth(bounds) - 2*HOST_MARGIN)/(2*maxHostSize.width + NSWidth(localHostRect)); + + _scaleFactor = MIN(scaleFactorH, scaleFactorV); + } + else { + _scaleFactor = DEFAULT_SCALE; + } + + //NSLog(@"scaleFactor: %f", _scaleFactor); + + if(!NSEqualSizes(maxOnlineHostSize, NSZeroSize)) { + _layoutHeight = round(NSHeight(bounds) - (maxOnlineHostSize.height*_scaleFactor + 2*HOST_MARGIN)); + } + else { + _layoutHeight = NSHeight(bounds); + } +} + +- (void)_updateScroller +{ + NSArray * hosts = [[self remoteHosts] hostsWithState:(TPHostSharedState|TPHostIncompatibleState)]; + NSRect fullFrame = [self frame]; + + if([hosts count] == 0) { + if(_scroller != nil) { + [_scroller removeFromSuperview]; + _scroller = nil; + } + } + else { + /* Calculate width */ + float width = HOST_MARGIN; + NSEnumerator * hostsEnum = [hosts objectEnumerator]; + TPRemoteHost * host; + while(host = [hostsEnum nextObject]) { + NSRect hostRect = [host fullHostRect]; + width += (floor(hostRect.size.width*_scaleFactor) + HOST_MARGIN); + } + + float proportion = NSWidth(fullFrame)/width; + + if(proportion < 1.0) { + NSRect scrollerFrame = NSMakeRect(LINE_THICKNESS, _layoutHeight - [NSScroller scrollerWidth], NSWidth(fullFrame) - 2*LINE_THICKNESS, [NSScroller scrollerWidth]); + + if(_scroller == nil) { + _scroller = [[NSScroller alloc] initWithFrame:scrollerFrame]; + [self addSubview:_scroller]; + [_scroller setTarget:self]; + [_scroller setAction:@selector(scroll:)]; + } + + [_scroller setFloatValue:_scrollerDeltaX/(NSWidth(fullFrame) - width) knobProportion:proportion]; + [_scroller setFrame:scrollerFrame]; + [_scroller setEnabled:YES]; + [_scroller setHidden:NO]; + } + else { + [_scroller removeFromSuperview]; + _scroller = nil; + } + } +} + +- (void)_updateLayout +{ + NSRect bounds = [self bounds]; + + // Local host + NSRect localHostRect = [_localHostView totalFrame]; + localHostRect = TPScaledRect(localHostRect, _scaleFactor); + + NSPoint origin = NSMakePoint(floor((NSWidth(bounds) - NSWidth(localHostRect))/2.0), + floor((_layoutHeight + BOTTOM_MARGIN - NSHeight(localHostRect))/2.0)); + + [_localHostView updateLayoutWithScaleFactor:_scaleFactor]; + [_localHostView setFrameOrigin:origin]; + + // Remote hosts + NSEnumerator * hostViewsEnum = [_remoteHostsViews objectEnumerator]; + TPLayoutRemoteHostView * hostView; + + //float hostOrigin = _layoutHeight + HOST_MARGIN; + NSPoint sharedHostPoint = NSMakePoint(HOST_MARGIN+_scrollerDeltaX, _layoutHeight + HOST_MARGIN); + + while((hostView = [hostViewsEnum nextObject])) { + TPRemoteHost * host = (TPRemoteHost*)[hostView host]; + NSRect totalFrame = [hostView totalFrame]; + NSRect frame; + TPSide side = TPUndefSide; + if([host isInState:TPHostPeeredState]) { + NSRect localScreenFrame = [_localHostView screenFrameAtIndex:0]; + //NSLog(@"localHostView: %@, localScreenFrame: %@",localHostView,NSStringFromRect(localScreenFrame)); + + NSRect hostRect = [host hostRect]; + frame = TPScaledRect(hostRect, _scaleFactor); + frame.origin.x += NSMinX(localScreenFrame); + frame.origin.y += NSMinY(localScreenFrame); + + frame.size.width = floor(NSWidth(frame)); + frame.size.height = floor(NSHeight(frame)); + + frame = [hostView hostFrameFromScreenFrame:frame atIndex:[host sharedScreenIndex]]; + + NSRect representedRect = [host adjustedHostRect]; + NSRect parentRect = [[host localScreen] frame]; + TPGluedRect(NULL, &side, parentRect, representedRect, TPUndefSide); + } + else { + frame = TPScaledRect(totalFrame, _scaleFactor); + frame.size.width = floor(NSWidth(frame)); + frame.size.height = floor(NSHeight(frame)); + + frame.origin = sharedHostPoint; + sharedHostPoint.x += NSWidth(frame) + HOST_MARGIN; + } + + double (*xFunc)(double) = NULL; + double (*yFunc)(double) = NULL; + if(side & TPRightSide) { + xFunc = floor; + } + else if(side & TPLeftSide) { + xFunc = ceil; + } + else { + xFunc = floor; + } + + if(side & TPTopSide) { + yFunc = floor; + } + else if(side & TPBottomSide) { + yFunc = ceil; + } + else { + yFunc = floor; + } + + frame.origin.x = xFunc(NSMinX(frame)); + frame.origin.y = yFunc(NSMinY(frame)); + + [hostView updateLayoutWithScaleFactor:_scaleFactor]; + [hostView setFrameOrigin:frame.origin]; + } + + + [CATransaction begin]; + [CATransaction setValue:@YES forKey:kCATransactionDisableActions]; + + // Separation line + NSArray * onlineHosts = [[self remoteHosts] hostsWithState:TPHostSharedState|TPHostIncompatibleState|TPHostPeeringState]; + if(([onlineHosts count] > 0) && (_scroller == nil || [_scroller isHidden])) { + if(_separationLine == nil) { + _separationLine = [CALayer layer]; + [[self layer] insertSublayer:_separationLine atIndex:0]; + CGColorRef grayColor = CGColorCreateGenericGray(0.5, 1.0); + _separationLine.backgroundColor = grayColor; + CFRelease(grayColor); + } + _separationLine.frame = CGRectMake(LINE_MARGIN, _layoutHeight, NSWidth(bounds) - 2*LINE_MARGIN, LINE_THICKNESS); + } + else if(_separationLine != nil) { + [_separationLine removeFromSuperlayer]; + _separationLine = nil; + } + + NSArray *peeredHosts = [[self remoteHosts] hostsWithState:TPHostPeeredState|TPHostOnlineState]; + if (peeredHosts.count == 0) { + infoLabel.stringValue = NSLocalizedString(@"No shared Mac found on local network. Please check \\U201CShare this Mac\\U201D on the Macs you want to control.", nil); } + else { + infoLabel.stringValue = NSLocalizedString(@"Arrange the shared Macs around your screen to control them. Configure options on each one.", nil); + } + + [CATransaction commit]; +} + +- (void)updateLayout +{ + [NSAnimationContext beginGrouping]; + [[NSAnimationContext currentContext] setDuration:0]; + [self _updateViews]; + [self _updateScaleFactor]; + [self _updateScroller]; + [self _updateLayout]; + [NSAnimationContext endGrouping]; + //[self setNeedsDisplay:YES]; + +} + +- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize +{ + [self updateLayout]; +} + + +#pragma mark - +#pragma mark Events + +- (void)_dragHostView:(TPLayoutRemoteHostView*)hostView withInitialEvent:(NSEvent*)initialEvent +{ + NSPoint point = [initialEvent locationInWindow]; + point = [hostView convertPoint:point fromView:nil]; + unsigned draggingScreenIndex = [hostView indexOfScreenAtPoint:point]; + + if(draggingScreenIndex == -1) + return; + + // put the view at the top + [hostView removeFromSuperview]; + [self addSubview:hostView]; + + [hostView setDraggingScreenIndex:draggingScreenIndex]; + + TPRemoteHost * host = (TPRemoteHost*)[hostView host]; + NSEvent * event = initialEvent; + //NSRect viewFrame = [hostView frame]; + NSRect frame = [hostView screenFrameAtIndex:draggingScreenIndex]; + NSRect snappedFrame; + BOOL snapped = NO; + unsigned nearestScreenIndex = 0; + float minDist; + NSRect minGluedRect = NSZeroRect; + TPSide minSide = TPUndefSide; + NSPoint hostPosition; + + [hostView mouseExited:initialEvent]; + + TPHostPlacementIndicator * indicator = [[TPHostPlacementIndicator alloc] initWithHost:host]; + +#if DEBUG_SNAP + CALayer * debugSnapLayer = [CALayer layer]; + debugSnapLayer.frame = NSRectToCGRect([hostView screenFrameAtIndex:draggingScreenIndex]); + debugSnapLayer.backgroundColor = CGColorCreateGenericRGB(1.0, 0.0, 0.0, 0.5); + debugSnapLayer.zPosition = 10.0; + [[self layer] addSublayer:debugSnapLayer]; +#endif + + while(1) { + frame.origin.x += [event deltaX]; + frame.origin.y -= [event deltaY]; + +#if DEBUG_SNAP + [CATransaction begin]; + [CATransaction setValue:[NSNumber numberWithBool:YES] forKey:kCATransactionDisableActions]; + debugSnapLayer.frame = NSRectToCGRect(frame); + [CATransaction commit]; +#endif + + snappedFrame = frame; + +#define ANIMATE_SNAP 0 +#define DISTANCE_TO_SNAP 30 +#define SQUARED_DISTANCE_TO_SNAP (DISTANCE_TO_SNAP*DISTANCE_TO_SNAP) + + + nearestScreenIndex = [_localHostView nearestScreenIndexForFrame:frame distance:&minDist position:&(minGluedRect.origin) side:&minSide]; + + + hostPosition = minGluedRect.origin; + NSPoint localHostPosition = [_localHostView screenFrameAtIndex:0].origin; + hostPosition.x = (hostPosition.x - localHostPosition.x)/_scaleFactor; + hostPosition.y = (hostPosition.y - localHostPosition.y)/_scaleFactor; + //NSLog(@"hostPosition: %@", NSStringFromPoint(hostPosition)); + hostPosition = [hostView adjustedScreenPositionFromPosition:hostPosition localScreenIndex:nearestScreenIndex side:minSide]; + //NSLog(@"hostPosition2: %@", NSStringFromPoint(hostPosition)); + NSRect hostRect = [host hostRect]; + hostRect.origin = hostPosition; + + BOOL animate = NO; + + snappedFrame.origin = minGluedRect.origin; + + [indicator setHostRect:hostRect localScreenIndex:nearestScreenIndex]; + + // DebugLog(@"minDist=%f", minDist); + if(minDist < SQUARED_DISTANCE_TO_SNAP) { + if(!snapped) { + [indicator show]; + animate = ANIMATE_SNAP; + _snapTime = 0.0; + } + snapped = YES; +// hostPosition.x = (hostPosition.x/_scaleFactor - localHostPosition.x); +// hostPosition.y = (hostPosition.y/_scaleFactor - localHostPosition.y); + //[self snapToScreenIndex:nearestScreenIndex atPosition:hostPosition ofSide:minSide]; + } + else { + if(snapped) { + [indicator close]; + //animate = YES; + _snapTime = 0.0; + } + + snapped = NO; + //[self unsnap]; + } + + NSTimeInterval duration = 0.0; + + + +#if ANIMATE_SNAP + NSTimeInterval timeIntervalSinceReferenceDate = [NSDate timeIntervalSinceReferenceDate]; + + BOOL hasRunningAnimation = (_snapTime > timeIntervalSinceReferenceDate); + + if(animate && !hasRunningAnimation) { + _snapTime = timeIntervalSinceReferenceDate + SNAP_DURATION; + } + + duration = (_snapTime - timeIntervalSinceReferenceDate); + + + if(hasRunningAnimation) { + + CGFloat percent = (SNAP_DURATION - duration)/SNAP_DURATION; +// if(!snapped) +// percent = 1.0 - percent; + + NSRect intermediateFrame = frame; + + intermediateFrame.origin.x += percent * (NSMinX(snappedFrame) - NSMinX(frame)); + intermediateFrame.origin.y += percent * (NSMinY(snappedFrame) - NSMinY(frame)); + + + + [NSAnimationContext beginGrouping]; + + [hostView setFrame:intermediateFrame]; + + [NSAnimationContext endGrouping]; + + } + + //NSLog(@"duration: %f", duration); + +#endif + + NSRect hostFrame = snapped ? snappedFrame : frame; + hostFrame = [hostView hostFrameFromScreenFrame:hostFrame atIndex:draggingScreenIndex]; + + [NSAnimationContext beginGrouping]; + [[NSAnimationContext currentContext] setDuration:duration]; + + + if(duration > 0.0) + [[hostView animator] setFrame:hostFrame]; + else + [hostView setFrame:hostFrame]; + + [NSAnimationContext endGrouping]; + + event = [NSApp nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]; + + if([event type] == NSLeftMouseUp) { + break; + } + } + +#if DEBUG_SNAP + [debugSnapLayer removeFromSuperlayer]; +#endif + + [indicator close]; + + if(snapped) { + //[[TPHostAnimationController controller] showAppearanceAnimationForHost:host]; + [hostView pairToScreenIndex:nearestScreenIndex atPosition:hostPosition ofSide:minSide]; + } + else if([host isInState:TPHostPeeredState]) { + [hostView unpair]; + } + else { + [self updateLayout]; + } + + [hostView setDraggingScreenIndex:-1]; + +} + +- (void)mouseDown:(NSEvent*)event +{ + NSPoint point = [[self superview] convertPoint:[event locationInWindow] fromView:nil]; + NSView * view = [self hitTest:point]; + + if([view isKindOfClass:[TPLayoutRemoteScreenView class]]) { + TPLayoutRemoteHostView * remoteHostView = (TPLayoutRemoteHostView*)[(TPLayoutRemoteScreenView*)view hostView]; + [self _dragHostView:remoteHostView withInitialEvent:event]; + } +} + +- (IBAction)scroll:(id)sender +{ + NSRect fullFrame = [self bounds]; + float value = [_scroller floatValue]; + + _scrollerDeltaX = round(- value*(NSWidth(fullFrame)/[_scroller knobProportion]-NSWidth(fullFrame))); + + [self _updateLayout]; +} + +- (void)scrollWheel:(NSEvent *)theEvent +{ + if(_scroller == nil) return; + + NSRect fullFrame = [self bounds]; + CGFloat deltaX = - _scrollerDeltaX - [theEvent deltaX]; + + _scrollerDeltaX = round(- MAX(0.0, MIN((NSWidth(fullFrame)/[_scroller knobProportion]-NSWidth(fullFrame)), deltaX))); + + [self _updateScroller]; + [self _updateLayout]; +} + +//+ (id)defaultAnimationForKey:(NSString *)key +//{ +// return nil; +//} +// +//- (id)animationForKey:(NSString*)key +//{ +// NSLog(@"KEY: %@", key); +// return [CABasicAnimation animation]; +//} + + + +#pragma mark - +#pragma mark Animation + +//- (id)actionForKey:(NSString*)key +//{ +// NSLog(@"pouet"); +// return [CABasicAnimation animation]; +//} + +@end diff --git a/TPLinkTextField.h b/TPLinkTextField.h new file mode 100644 index 0000000..7d0db03 --- /dev/null +++ b/TPLinkTextField.h @@ -0,0 +1,17 @@ +// +// TPLinkTextField.h +// teleport +// +// Created by JuL on 10/07/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import + + +@interface TPLinkTextField : NSTextField +{ + NSTrackingRectTag _trackingRectTag; +} + +@end diff --git a/TPLinkTextField.m b/TPLinkTextField.m new file mode 100644 index 0000000..52bdd75 --- /dev/null +++ b/TPLinkTextField.m @@ -0,0 +1,104 @@ +// +// TPLinkTextField.m +// teleport +// +// Created by JuL on 10/07/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import "TPLinkTextField.h" + +static NSColor * _highlightedColor = nil; +static NSColor * _normalColor = nil; + +@implementation TPLinkTextField + ++ (void)initialize +{ + if(self == [TPLinkTextField class]) { + _highlightedColor = [NSColor colorWithCalibratedRed:0.8 green:0.08 blue:0.1 alpha:1.0]; + _normalColor = [NSColor blackColor]; + } +} + +- (instancetype) initWithCoder:(NSCoder*)coder +{ + self = [super initWithCoder:coder]; + + _trackingRectTag = -1; + + return self; +} + +- (instancetype) initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + + _trackingRectTag = -1; + + return self; +} + +- (void)dealloc +{ + if(_trackingRectTag > 0) + [self removeTrackingRect:_trackingRectTag]; +} + +- (void)_resetTrackingRect +{ + if(_trackingRectTag > 0) + [self removeTrackingRect:_trackingRectTag]; + if([self window] != nil) + _trackingRectTag = [self addTrackingRect:[self bounds] owner:self userData:NULL assumeInside:NO]; +} + +- (void)awakeFromNib +{ + [self _resetTrackingRect]; +} + +- (void)viewDidMoveToWindow +{ + [self _resetTrackingRect]; +} + +- (void)viewDidMoveToSuperview +{ + [self _resetTrackingRect]; +} + +- (void)setFrameSize:(NSSize)size +{ + [super setFrameSize:size]; + [self _resetTrackingRect]; +} + +- (void)setFrameOrigin:(NSPoint)origin +{ + [super setFrameOrigin:origin]; + [self _resetTrackingRect]; +} + +- (void)mouseEntered:(NSEvent*)event +{ + [self setTextColor:_highlightedColor]; + [[NSCursor pointingHandCursor] push]; + [self setNeedsDisplay:YES]; +} + +- (void)mouseExited:(NSEvent*)event +{ + [self setTextColor:_normalColor]; + [NSCursor pop]; + [self setNeedsDisplay:YES]; +} + +- (void)mouseDown:(NSEvent*)event +{ + NSURL * url = [NSURL URLWithString:[(NSTextFieldCell*)[self cell] placeholderString]]; + if(url != nil) + [[NSWorkspace sharedWorkspace] openURL:url]; +} + +@end diff --git a/TPLocalHost.h b/TPLocalHost.h new file mode 100644 index 0000000..8d1e859 --- /dev/null +++ b/TPLocalHost.h @@ -0,0 +1,42 @@ +// +// TPLocalHost.h +// teleport +// +// Created by JuL on Fri Feb 27 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +#import "TPHost.h" + +@interface TPLocalHost : TPHost +{ + NSArray * _potentialIdentities; + NSMutableArray * _backgroundImages; +} + ++ (TPLocalHost*)localHost; + +@property (nonatomic, readonly, copy) NSArray *potentialIdentities; +@property (nonatomic) SecIdentityRef identity; +@property (nonatomic, readonly) BOOL hasIdentity; +- (void)resetIdentity; +- (void)reloadIdentity; + +@property (nonatomic, readonly, copy) NSString *bonjourName; + +@property (nonatomic, readonly, strong) NSScreen *mainScreen; +- (NSScreen*)sharedScreen; +- (void)setSharedScreenIndex:(unsigned)screenIndex; +- (unsigned)sharedScreenIndex; +- (void)wakeUpScreen; + +- (NSImage*)backgroundImageForScreen:(NSScreen*)screen; + +- (void)checkCapabilities; + +@property (nonatomic, getter=isAccessibilityAPIEnabled, readonly) BOOL accessibilityAPIEnabled; +@property (nonatomic, readonly) BOOL checkAccessibility; + +@end diff --git a/TPLocalHost.m b/TPLocalHost.m new file mode 100644 index 0000000..b46a7fc --- /dev/null +++ b/TPLocalHost.m @@ -0,0 +1,568 @@ +// +// TPLocalHost.m +// teleport +// +// Created by JuL on Fri Feb 27 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPLocalHost.h" + +#import "TPPreferencesManager.h" + +#include +#include +#include +#include +#include +#include + +#define FAKE_MULTI_SCREEN 0 + +NSString * TPLocalHostCapabilitiesChangedNotification = @"TPLocalHostCapabilitiesChangedNotification"; + +static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices) +{ + kern_return_t kernResult; + CFMutableDictionaryRef matchingDict; + CFMutableDictionaryRef propertyMatchDict; + + matchingDict = IOServiceMatching(kIOEthernetInterfaceClass); + + if (NULL == matchingDict) { + printf("IOServiceMatching returned a NULL dictionary.\n"); + } + else { + propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + if (NULL == propertyMatchDict) { + printf("CFDictionaryCreateMutable returned a NULL dictionary.\n"); + } + else { + CFDictionarySetValue(propertyMatchDict, CFSTR(kIOPrimaryInterface), kCFBooleanTrue); + + CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); + CFRelease(propertyMatchDict); + } + } + + kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, matchingServices); + if (KERN_SUCCESS != kernResult) { + printf("IOServiceGetMatchingServices returned 0x%08x\n", kernResult); + } + + return kernResult; +} + +static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize) +{ + io_object_t intfService; + io_object_t controllerService; + kern_return_t kernResult = KERN_FAILURE; + + if (bufferSize < kIOEthernetAddressSize) { + return kernResult; + } + + bzero(MACAddress, bufferSize); + + while ((intfService = IOIteratorNext(intfIterator)) != 0) + { + CFTypeRef MACAddressAsCFData; + kernResult = IORegistryEntryGetParentEntry(intfService, + kIOServicePlane, + &controllerService); + + if (KERN_SUCCESS != kernResult) { + printf("IORegistryEntryGetParentEntry returned 0x%08x\n", kernResult); + } + else { + MACAddressAsCFData = IORegistryEntryCreateCFProperty(controllerService, + CFSTR(kIOMACAddress), + kCFAllocatorDefault, + 0); + if (MACAddressAsCFData) { + CFDataGetBytes(MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress); + CFRelease(MACAddressAsCFData); + } + + (void) IOObjectRelease(controllerService); + } + + (void) IOObjectRelease(intfService); + } + + return kernResult; +} + +static TPLocalHost * _localHost = nil; + +@interface TPLocalHost (Internal) + +- (void)_generateBackgroundImages; +- (NSString*)_desktopPicturePathForScreen:(NSScreen*)screen; +- (NSImage*)_desktopPictureForScreen:(NSScreen*)screen; + +@end + +@implementation TPLocalHost + ++ (TPLocalHost*)localHost +{ + if(_localHost == nil) + _localHost = [[TPLocalHost alloc] init]; + return _localHost; +} + +- (instancetype) init +{ + self = [super init]; + + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_generateBackgroundImages) name:@"com.apple.desktop" object:@"BackgroundChanged"]; + + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_accessibilityAPIChanged:) name:@"com.apple.accessibility.api" object:nil]; + + return self; +} + + +- (NSString*)identifier +{ + return [[NSWorkspace sharedWorkspace] computerIdentifier]; +} + +- (SInt32)osVersion +{ + SInt32 MacVersion = 0; + size_t size = 256; + char str[size]; + int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0); + + if (ret == noErr) { + MacVersion = atoi(str); + } + + return MacVersion; +} + +- (NSString*)address +{ + return @"127.0.0.1"; +} + +- (NSString*)computerName +{ + if(_computerName == nil) + _computerName = (NSString *)CFBridgingRelease(CSCopyMachineName()); + + return [super computerName]; +} + +- (IOEthernetAddress)MACAddress +{ + if(![self hasValidMACAddress]) { + kern_return_t kernResult = KERN_SUCCESS; + io_iterator_t intfIterator; + kernResult = FindEthernetInterfaces(&intfIterator); + + if(KERN_SUCCESS != kernResult) { + printf("FindEthernetInterfaces returned 0x%08x\n", kernResult); + } + else { + kernResult = GetMACAddress(intfIterator, _macAddress.bytes, kIOEthernetAddressSize); + + if(KERN_SUCCESS != kernResult) { + printf("GetMACAddress returned 0x%08x\n", kernResult); + } + } + + (void)IOObjectRelease(intfIterator); // Release the iterator. + } + + return [super MACAddress]; +} + +- (NSArray*)screens +{ +#if FAKE_MULTI_SCREEN + NSMutableArray * screens = [[NSScreen screens] mutableCopy]; + TPScreen * otherScreen = [[TPScreen alloc] init]; + NSRect frame = [[screens objectAtIndex:0] frame]; + frame.origin.x += NSWidth(frame); + frame.size.width *= 1.5; + frame.size.height *= 1.5; + otherScreen.frame = frame; + [screens addObject:otherScreen]; + [otherScreen release]; + return [screens autorelease]; +#else + return [NSScreen screens]; +#endif +} + + +#pragma mark - +#pragma mark Background image + +- (void)_generateBackgroundImages +{ + NSArray * screens = [[TPLocalHost localHost] screens]; + + + _backgroundImages = [[NSMutableArray alloc] initWithCapacity:[screens count]]; + + NSEnumerator * screenEnum = [screens objectEnumerator]; + NSScreen * screen; + + while((screen = [screenEnum nextObject]) != nil) { + NSImage * backgroundImage = nil; + NSImage * desktopPicture = [self _desktopPictureForScreen:screen]; + if(desktopPicture != nil) { + backgroundImage = [TPHost backgroundImageFromDesktopPicture:desktopPicture]; + } + else { + backgroundImage = [self defaultBackgroundImage]; + } + + if(backgroundImage != nil) + [_backgroundImages addObject:backgroundImage]; + } + + [self notifyChange]; +} + +- (NSImage*)backgroundImageForScreen:(NSScreen*)screen +{ + if(_backgroundImages == nil) + [self _generateBackgroundImages]; + + int index = [[[TPLocalHost localHost] screens] indexOfObject:screen]; + if(index >= 0 && index < [_backgroundImages count]) + return _backgroundImages[index]; + else + return nil; +} + +- (NSImage*)backgroundImage +{ + return [self backgroundImageForScreen:[self mainScreen]]; +} + +- (BOOL)hasCustomBackgroundImage +{ + return ([self _desktopPicturePathForScreen:[self mainScreen]] != nil); +} + +- (NSString*)_desktopPicturePathForScreen:(NSScreen*)screen +{ + if([[NSWorkspace sharedWorkspace] respondsToSelector:@selector(desktopImageURLForScreen:)]) { + NSURL * desktopPictureURL = [[NSWorkspace sharedWorkspace] desktopImageURLForScreen:screen]; + return [desktopPictureURL path]; + } + else { + NSUserDefaults * userDefaults = [[NSUserDefaults alloc] init]; + [userDefaults addSuiteNamed:@"com.apple.desktop"]; + [userDefaults synchronize]; + + NSDictionary * prefDict = [userDefaults dictionaryRepresentation]; + + NSNumber * screenNum = [screen deviceDescription][@"NSScreenNumber"]; + NSDictionary * backgroundDict = prefDict[@"Background"]; + + NSDictionary * screenDict = backgroundDict[[screenNum stringValue]]; + if(screenDict == nil) + screenDict = backgroundDict[@"default"]; + + NSString * imagePath = nil; + NSString * change = screenDict[@"Change"]; + if(change != nil && ![change isEqualToString:@"Never"]) { + imagePath = screenDict[@"ChangePath"]; + imagePath = [imagePath stringByAppendingPathComponent:screenDict[@"LastName"]]; + } + + if(imagePath == nil) + imagePath = screenDict[@"ImageFilePath"]; + + + if(imagePath == nil) + return nil; + + if(![[NSFileManager defaultManager] fileExistsAtPath:imagePath]) + return nil; + + return imagePath; + } +} + +- (NSImage*)_desktopPictureForScreen:(NSScreen*)screen +{ + NSString * path = [self _desktopPicturePathForScreen:screen]; + if(path == nil) + return nil; + + NSImage * desktopPicture = [[NSImage alloc] initByReferencingFile:path]; + return desktopPicture; +} + + +#pragma mark - +#pragma mark Capabilities + +- (TPHostCapability)capabilities +{ + TPHostCapability capabilities = TPHostNoCapability; + + int c; + for(c=0; c<=MAX_CAPABILITIES; c++) { + TPHostCapability capability = (1 << c); + + if([self hasCapability:capability]) + capabilities |= capability; + } + + return capabilities; +} + +- (BOOL)isAccessibilityAPIEnabled +{ + return AXAPIEnabled(); +} + +- (BOOL)checkAccessibility +{ + return AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)@{(__bridge NSString*)kAXTrustedCheckOptionPrompt: @YES}); +} + +- (BOOL)hasCapability:(TPHostCapability)capability +{ + switch(capability) { + case TPHostEncryptionCapability: + return [self hasIdentity] && [[TPPreferencesManager sharedPreferencesManager] boolForPref:ENABLED_ENCRYPTION]; + case TPHostEventTapsCapability: + return ([self osVersion] >= TPHostOSVersion(4)) && [self isAccessibilityAPIEnabled]; + case TPHostDirectEventTapsCapability: + return ([self osVersion] >= TPHostOSVersion(5)) && [self isAccessibilityAPIEnabled] && ![[TPPreferencesManager sharedPreferencesManager] boolForPref:TIGER_BEHAVIOR]; + default: + return NO; + } + + return NO; +} + +- (void)checkCapabilities +{ + [self willChangeValueForKey:@"supportDragNDrop"]; + [self didChangeValueForKey:@"supportDragNDrop"]; +} + +- (void)_accessibilityAPIChanged:(NSNotification*)notification +{ + [self notifyChange]; +} + + +#pragma mark - +#pragma mark Identity + +- (SecKeychainRef)_keychainRef +{ + static SecKeychainRef keychainRef = NULL; + + if(keychainRef == NULL) { + OSErr err; + NSString * keychainName = nil; + + if(keychainName != nil) { + NSString * keychainsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Keychains"]; + NSString * fullPath = [keychainsDirectory stringByAppendingPathComponent:keychainName]; + + if((err = SecKeychainOpen([fullPath UTF8String], &keychainRef)) != noErr) { + NSLog(@"Unable to open keychain: %d", err); + } + } + else if((err = SecKeychainCopyDefault(&keychainRef)) != noErr) { + NSLog(@"Unable to copy default keychain: %d", err); + } + } + + return keychainRef; +} + +- (id)_identifierForIdentity:(SecIdentityRef)identity +{ + UInt32 tags[] = { kSecPublicKeyHashItemAttr }; + UInt32 formats[] = { CSSM_DB_ATTRIBUTE_FORMAT_BLOB }; + SecKeychainAttributeInfo info = { 1, tags, formats }; + SecKeychainAttributeList * attrList = NULL; + + OSStatus err; + + SecCertificateRef certRef; + if((err = SecIdentityCopyCertificate(identity, &certRef)) != noErr) { + NSLog(@"Unable to copy certificate: %d", err); + return nil; + } + + if((err = SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)certRef, &info, NULL, &attrList, NULL, NULL)) != noErr) { + NSLog(@"Unable to copy identifier: %d", err); + CFRelease(certRef); + return nil; + } + + CFRelease(certRef); + + if(attrList->count != 1) { + NSLog(@"unexpected number of serial number: %d", attrList->count); + SecKeychainItemFreeAttributesAndData(attrList, NULL); + return nil; + } + + SecKeychainAttribute identifierAttribute = attrList->attr[0]; + + NSData * identifierData = [[NSData alloc] initWithBytes:identifierAttribute.data length:identifierAttribute.length]; + SecKeychainItemFreeAttributesAndData(attrList, NULL); + return identifierData; +} + +- (NSArray*)potentialIdentities +{ + if(_potentialIdentities == nil) { + OSErr err; + + SecKeychainRef keychainRef = [self _keychainRef]; + + SecIdentitySearchRef searchRef = NULL; + if((err = SecIdentitySearchCreate(keychainRef, CSSM_KEYUSE_ENCRYPT|CSSM_KEYUSE_DECRYPT|CSSM_KEYUSE_SIGN|CSSM_KEYUSE_VERIFY, &searchRef)) != noErr) { + NSLog(@"Unable to create keychain search: %d", err); + CFRelease(keychainRef); + return nil; + } + + id identifier = [[TPPreferencesManager sharedPreferencesManager] valueForPref:CERTIFICATE_IDENTIFIER]; + NSMutableArray * potentialIdentities = [[NSMutableArray alloc] init]; + SecIdentityRef identityRef; + + while((err = SecIdentitySearchCopyNext(searchRef, &identityRef)) == noErr) { + id identityIdentifier = [self _identifierForIdentity:identityRef]; + if((identifier != nil) && [identifier isEqual:identityIdentifier]) { + [potentialIdentities insertObject:(id)CFBridgingRelease(identityRef) atIndex:0]; // put current identity first in the potential identities + } + else { + [potentialIdentities addObject:(id)CFBridgingRelease(identityRef)]; + } + } + + CFRelease(searchRef); + + if(err != errSecItemNotFound) { + DebugLog(@"Unable to get next search result: %d", err); + } + else { + _potentialIdentities = potentialIdentities; + } + } + + return _potentialIdentities; +} + +- (SecIdentityRef)identity +{ + NSArray * potentialIdentities = [self potentialIdentities]; + + if(potentialIdentities == nil || ([potentialIdentities count] == 0)) { + return NULL; + } + else { + return (__bridge SecIdentityRef)potentialIdentities[0]; + } +} + +- (void)setIdentity:(SecIdentityRef)identity +{ + if(identity != NULL) { + id identifier = [self _identifierForIdentity:identity]; + [[TPPreferencesManager sharedPreferencesManager] setValue:identifier forKey:CERTIFICATE_IDENTIFIER]; + } + + [self reloadIdentity]; +} + +- (BOOL)hasIdentity +{ + return ([self identity] != NULL); +} + +- (void)resetIdentity +{ + [self setIdentity:NULL]; +} + +- (void)reloadIdentity +{ + _potentialIdentities = nil; +} + +- (NSString*)bonjourName +{ + return [NSString stringWithFormat:@"%@@%@", NSUserName(), [self computerName]]; +} + + +#pragma mark - +#pragma mark Screen + +- (NSScreen*)mainScreen +{ + return [self screenAtIndex:0]; +} + +- (NSScreen*)sharedScreen +{ + NSScreen * sharedScreen = [self screenAtIndex:[self sharedScreenIndex]]; + + if(sharedScreen == nil) { + sharedScreen = [self mainScreen]; + } + + return sharedScreen; +} + +- (void)setSharedScreenIndex:(unsigned)screenIndex +{ + [[TPPreferencesManager sharedPreferencesManager] setValue:@(screenIndex) forKey:SHARED_SCREEN_INDEX]; + [self notifyChange]; +} + +- (unsigned)sharedScreenIndex +{ + return [[[TPPreferencesManager sharedPreferencesManager] valueForKey:SHARED_SCREEN_INDEX] unsignedIntValue]; +} + +- (void)wakeUpScreen +{ + IOPMAssertionID assertionID; + IOReturn success = IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &assertionID); + if(success == kIOReturnSuccess) { + IOPMAssertionRelease(assertionID); + } + + io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault, + "IOService:/IOResources/IODisplayWrangler"); + if (entry != MACH_PORT_NULL) { + IORegistryEntrySetCFProperty(entry, CFSTR("IORequestIdle"), kCFBooleanFalse); + IOObjectRelease(entry); + } +} + + +- (id)valueForUndefinedKey:(NSString*)key +{ + if([key isEqualToString:@"supportDragNDrop"]) + return @([self hasCapability:TPHostDragNDropCapability]); + else + return [super valueForUndefinedKey:key]; +} + +@end diff --git a/TPMainController.h b/TPMainController.h new file mode 100644 index 0000000..bb7ddf3 --- /dev/null +++ b/TPMainController.h @@ -0,0 +1,42 @@ +// +// TPMainController.h +// Teleport +// +// Created by JuL on Thu Dec 25 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +typedef uint32_t CGSUInt32; + +struct CPSProcessSerNum +{ + CGSUInt32 hi; + CGSUInt32 lo; +}; +typedef struct CPSProcessSerNum CPSProcessSerNum; + +@class TPNetworkConfigurationWatcher; + +@interface TPMainController : NSObject +{ + CPSProcessSerNum _frontProcessNum; + TPNetworkConfigurationWatcher * _networkConfigurationWatcher; +} + ++ (TPMainController*)sharedController; + +- (BOOL)canBeControlledByHostWithIdentifier:(NSString*)identifier; +- (BOOL)canControlHostWithIdentifier:(NSString*)identifier; + +/* UI */ +- (void)goFrontmost; +- (void)leaveFrontmost; +- (int)presentAlert:(NSAlert*)alert; + +/* Version checking */ +- (void)checkVersionFromNotification:(NSNotification*)notification; +- (void)checkVersionsAndConfirm:(BOOL)confirm; + +@end diff --git a/TPMainController.m b/TPMainController.m new file mode 100644 index 0000000..8853470 --- /dev/null +++ b/TPMainController.m @@ -0,0 +1,458 @@ +// +// TPMainController.m +// Teleport +// +// Created by JuL on Thu Dec 25 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPMainController.h" +#import "TPClientController.h" +#import "TPServerController.h" +#import "TPNetworkConfigurationWatcher.h" +#import "TPStatusItemController.h" +#import "TPAuthenticationManager.h" +#import "TPRemoteHost.h" +#import "TPLocalHost.h" +#import "TPMessage.h" +#import "TPBonjourController.h" +#import "TPHostsManager.h" +#import "TPHostSnapping.h" +#import "TPPreferencesManager.h" +#import "TPTransfersManager.h" +#import "TPBezelController.h" +#import "TPPreferencePane.h" + +#import +#import +#import +#import +#import +#import +#import +#import + +#import "TPNetworkConnection.h" +#import "TPTestsController.h" + +typedef CGError CGSError; + +extern CGSError CPSGetFrontProcess( CPSProcessSerNum *pPSN); +extern CGSError CPSSetFrontProcess( const CPSProcessSerNum *pPSN); + +#define VERSION_CHECK_URL @"http://www.abyssoft.com/software/teleport/version.plist" + +static TPMainController * _mainController = nil; + +@interface TPMainController (Internal) + +- (void)_checkAccessibility; +- (void)_checkEncryption; + +@end + +@implementation TPMainController + ++ (TPMainController*)sharedController +{ + return _mainController; +} + +- (BOOL)canBeControlledByHostWithIdentifier:(NSString*)identifier +{ + if([[TPClientController defaultController] isControlling]) { + TPRemoteHost * controlledHost = [[[TPClientController defaultController] currentConnection] connectedHost]; + return ![[controlledHost identifier] isEqualToString:identifier]; + } + else + return YES; +} + +- (BOOL)canControlHostWithIdentifier:(NSString*)identifier +{ + if([[TPServerController defaultController] isControlled]) { + TPRemoteHost * controllingHost = [[[TPServerController defaultController] currentConnection] connectedHost]; + return ![[controllingHost identifier] isEqualToString:identifier]; + } + else + return YES; +} + + +#pragma mark - +#pragma mark Application delegate + +- (void)applicationDidFinishLaunching:(NSNotification*)notification +{ +#if RUN_TESTS + [TPTestsController runTests]; +#else + _mainController = self; + _frontProcessNum.lo = 0; + + /* Setup Notifications */ + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_prefDidChange:) name:TPDefaultsDidChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_prefsDidUpgrade:) name:TPPreferencesDidUpgradeNotification object:nil]; + + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(screenDidLock:) name:@"com.apple.screenIsLocked" object:nil]; + + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(sessionDidResignActive:) name:NSWorkspaceSessionDidResignActiveNotification object:nil]; + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(sessionDidBecomeActive:) name:NSWorkspaceSessionDidBecomeActiveNotification object:nil]; + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:) name:NSWorkspaceWillSleepNotification object:nil]; + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerDidWake:) name:NSWorkspaceDidWakeNotification object:nil]; + + /* Waken controllers */ + [TPPreferencesManager sharedPreferencesManager]; + [TPClientController defaultController]; + [TPServerController defaultController]; + [TPStatusItemController defaultController]; + + /* Watch the network configuration */ + _networkConfigurationWatcher = [[TPNetworkConfigurationWatcher alloc] init]; + [_networkConfigurationWatcher setDelegate:self]; + [_networkConfigurationWatcher startWatching]; + + /* Data loading */ + [[TPHostsManager defaultManager] loadHosts]; + [[TPAuthenticationManager defaultManager] loadHosts]; + + /* Browse on Bonjour */ + [[TPBonjourController defaultController] browse]; + + /* Misc */ + [[TPTransfersManager manager] setDelegate:[TPBezelController defaultController]]; + + /* Warning dialogs */ + [self performSelector:@selector(_showWarningDialogs) withObject:nil afterDelay:0.0]; + + /* Auto-check version */ + if([[TPPreferencesManager sharedPreferencesManager] boolForPref:AUTOCHECK_VERSION]) + [self checkVersionsAndConfirm:NO]; + + /* Setup initial host borders */ + [[TPClientController defaultController] updateTriggersAndShowVisualHint:YES]; + + /* Notify that we're ready */ + [[TPHostsManager defaultManager] notifyChanges]; + + NSArray *peeredHosts = [[TPHostsManager defaultManager] hostsWithState:TPHostPeeredState]; + if (peeredHosts.count == 0 && ![[TPPreferencesManager sharedPreferencesManager] boolForPref:ALLOW_CONTROL]) { + [[TPPreferencePane preferencePane] showWindow:nil]; + } + +#if 0 + [TPTestsController runFakeHostsTest]; +#endif +#endif +} + +- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag +{ + [[TPPreferencePane preferencePane] showWindow:nil]; + return YES; +} + +- (void)_showWarningDialogs +{ + [self _checkAccessibility]; + [self _checkEncryption]; +} + +- (void)applicationWillTerminate:(NSNotification *)notification +{ + [[TPHostsManager defaultManager] saveHosts]; + [[TPAuthenticationManager defaultManager] saveHosts]; + + if([[TPClientController defaultController] isControlling]) + [[TPClientController defaultController] stopControl]; + + if([[TPServerController defaultController] allowControl]) + [[TPServerController defaultController] stopSharing]; +} + +- (void)applicationWillResignActive:(NSNotification *)aNotification +{ + if([[TPClientController defaultController] isControlling]) + [NSApp activateIgnoringOtherApps:YES]; +} + +//- (void)applicationDidResignActive:(NSNotification *)aNotification +//{ +// NSLog(@"did resign"); +// if([[TPClientController defaultController] isControlling]) { +// NSLog(@"ACTIVATE"); +// [NSApp activateIgnoringOtherApps:YES]; +// } +//} + +- (void)applicationDidChangeScreenParameters:(NSNotification *)aNotification +{ + [[TPHostsManager defaultManager] notifyChanges]; + + if([[TPServerController defaultController] allowControl]) { + [[TPBonjourController defaultController] updateTXTRecordOfPublishService]; + } + + [[TPClientController defaultController] updateTriggersAndShowVisualHint:YES]; +} + +- (void)screenDidLock:(NSNotification*)notification +{ + TPClientController * clientController = [TPClientController defaultController]; + if([clientController isControlling]) + [clientController stopControl]; +} + +- (void)sessionDidResignActive:(NSNotification*)notification +{ + TPClientController * clientController = [TPClientController defaultController]; + if([clientController isControlling]) + [clientController stopControl]; + else + [clientController setCurrentConnection:nil]; + + TPServerController * serverController = [TPServerController defaultController]; + if([serverController isControlled]) + [serverController stopControl]; + else + [serverController setCurrentConnection:nil]; + + if([serverController allowControl]) + [serverController stopSharing]; +} + +- (void)sessionDidBecomeActive:(NSNotification*)notification +{ + if([[TPServerController defaultController] allowControl]) + [[TPServerController defaultController] startSharing]; +} + +- (void)computerWillSleep:(NSNotification*)notification +{ + if([[TPClientController defaultController] isControlling]) + [[TPClientController defaultController] stopControl]; + + if([[TPServerController defaultController] allowControl]) + [[TPServerController defaultController] stopSharing]; + + [[TPHostsManager defaultManager] saveHosts]; + [[TPAuthenticationManager defaultManager] saveHosts]; +} + +- (void)computerDidWake:(NSNotification*)notification +{ + [[TPHostsManager defaultManager] notifyChanges]; + [[TPClientController defaultController] updateTriggersAndShowVisualHint:YES]; + + CFDictionaryRef sessionInfoDict = CGSessionCopyCurrentDictionary(); + + if(CFDictionaryGetValue(sessionInfoDict, kCGSessionOnConsoleKey) == kCFBooleanTrue && [[TPServerController defaultController] allowControl]) + [[TPServerController defaultController] startSharing]; + + CFRelease(sessionInfoDict); +} + +- (void)networkConfigurationDidChange:(TPNetworkConfigurationWatcher*)watcher +{ + DebugLog(@"Network configuration did change"); + + if([[TPPreferencesManager sharedPreferencesManager] boolForPref:DISCONNECT_ON_NETWORK_CONFIG_CHANGE]) { + TPServerController * server = [TPServerController defaultController]; + if([server allowControl]) { + [server stopSharing]; + [server startSharing]; + } + + [[TPBonjourController defaultController] stopBrowsing]; + [[TPBonjourController defaultController] browse]; + + [[TPClientController defaultController] stopControl]; + } +} + + +#pragma mark - +#pragma mark Misc + +- (void)_checkAccessibility +{ + if (![[TPLocalHost localHost] checkAccessibility]) { + NSAlert * alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Disabled access for assistive devices", @"Text in dialog about accessibility") defaultButton:NSLocalizedString(@"OK", @"Button title") alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(@"The access for assistive devices has been disabled although teleport needs this to operate.\nPlease allow teleport and launch it again.", @"Explanation in dialog for accessibility")]; + + int returnCode = [self presentAlert:alert]; + + + [NSApp terminate:nil]; + } +} + +- (void)_checkEncryption +{ + if([[TPPreferencesManager sharedPreferencesManager] boolForPref:ENABLED_ENCRYPTION]) { + if(![[TPLocalHost localHost] hasIdentity]) { + NSAlert * alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Encryption requires a certificate", @"Text in dialog for certificate missing error") defaultButton:NSLocalizedString(@"Launch Assistant", @"Button title") alternateButton:NSLocalizedString(@"Disable Encryption", @"Button title") otherButton:nil informativeTextWithFormat:NSLocalizedString(@"In order to activate encryption, teleport needs a valid certificate, but couldn't find one in your Keychain.\nYou'll need to create a certificate and re-activate encryption. Note that the certificate algorithm and key size must match between the server and the clients.\n\nWould you like to launch Certificate Assistant to create a new self-signed certificate?\nRecommended settings: RSA 1024 bits, all others to default values.", @"Explanation in dialog for certificate missing error")]; + + int returnCode = [self presentAlert:alert]; + + [[TPPreferencesManager sharedPreferencesManager] setValue:@NO forKey:ENABLED_ENCRYPTION]; + + switch(returnCode) { + case NSAlertDefaultReturn: + [[NSWorkspace sharedWorkspace] launchAppWithBundleIdentifier:@"com.apple.CertificateAssistant" options:NSWorkspaceLaunchDefault additionalEventParamDescriptor:nil launchIdentifier:NULL]; + break; + case NSAlertAlternateReturn: + break; + } + } + } + else + [[TPLocalHost localHost] resetIdentity]; +} + +- (void)_prefDidChange:(NSNotification*)notification +{ + if([[notification object] isEqualToString:ENABLED_ENCRYPTION]) { + [self _checkEncryption]; + [[TPLocalHost localHost] notifyChange]; + } + else if([[notification object] isEqualToString:WAKE_ON_LAN]) { + [[[TPHostsManager defaultManager] hostsWithState:TPHostPeeredOfflineState] makeObjectsPerformSelector:@selector(notifyChange)]; + } + else if([[notification object] isEqualToString:CERTIFICATE_IDENTIFIER]) { + [[TPLocalHost localHost] reloadIdentity]; + } + else if([[notification object] isEqualToString:SHARED_SCREEN_INDEX]) { + [[TPLocalHost localHost] notifyChange]; + } +} + +#define TPTerminateDaemonNotification @"TPTerminateDaemonNotification" +- (void)_prefsDidUpgrade:(NSNotification*)notification +{ + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:TPTerminateDaemonNotification object:nil userInfo:nil deliverImmediately:YES]; // be sure to quit previous teleport +} + + +#pragma mark - +#pragma mark UI + +- (void)goFrontmost +{ + if(CPSGetFrontProcess(&_frontProcessNum) != kCGErrorSuccess) { + _frontProcessNum.lo = 0; + DebugLog(@"get front process failed"); + } + else { + DebugLog(@"get front process succeded: hi=%d low=%d", _frontProcessNum.hi, _frontProcessNum.lo); + } + +// ProcessSerialNumber psn; +// GetCurrentProcess(&psn); +// SetFrontProcess(&psn); + [NSApp activateIgnoringOtherApps:TRUE]; +} + +- (void)leaveFrontmost +{ + if(_frontProcessNum.lo > 0) { + CPSSetFrontProcess(&_frontProcessNum); + DebugLog(@"switched back to process hi=%d low=%d", _frontProcessNum.hi, _frontProcessNum.lo); + _frontProcessNum.lo = 0; + } +} + +- (int)presentAlert:(NSAlert*)alert +{ + [self goFrontmost]; + int result = [alert runModal]; + [self leaveFrontmost]; + return result; +} + + +#pragma mark - +#pragma mark Version checking + +- (void)checkVersionFromNotification:(NSNotification*)notification +{ + [self checkVersionsAndConfirm:YES]; +} + +- (void)checkVersionsAndConfirm:(BOOL)confirm +{ + if(confirm) { + [[SUUpdater sharedUpdater] checkForUpdates:nil]; + } + else { + [[SUUpdater sharedUpdater] checkForUpdatesInBackground]; + } +} + +//- (void)_handleCheckVersionWithDictionary:(NSDictionary*)productVersionDict +//{ +// +// +//// NSNumber * confirm = [productVersionDict objectForKey:@"TPConfirm"]; +//// NSString * currentBuildString = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]; +//// if(currentBuildString == nil) +//// return; +//// int currentBuild = [currentBuildString intValue]; +//// int latestBuild = [[productVersionDict objectForKey:@"build"] intValue]; +//// NSString * latestVersionString = [productVersionDict objectForKey:@"version"]; +//// +//// if(latestBuild > currentBuild) { +//// NSString * description = [productVersionDict valueForKey:@"description"]; +//// NSString * info = [NSString stringWithFormat:NSLocalizedString(@"teleport %@ is now available.\n%@\n\nWould you like to download the new version now?", nil), latestVersionString, description]; +//// +//// NSAlert * alert = [NSAlert alertWithMessageText:NSLocalizedString(@"A new version of teleport is available!", nil) defaultButton:NSLocalizedString(@"Yes", nil) alternateButton:NSLocalizedString(@"No", nil) otherButton:NSLocalizedString(@"Don't show again", nil) informativeTextWithFormat:info]; +//// int result = [(TPMainController*)[NSApp delegate] presentAlert:alert]; +//// +//// switch(result) { +//// case NSAlertDefaultReturn: +//// [self updateToVersion:latestVersionString fromURL:[productVersionDict objectForKey:@"url"]]; +//// break; +//// case NSAlertAlternateReturn: +//// break; +//// case NSAlertOtherReturn: +//// [[TPPreferencesManager sharedPreferencesManager] setValue:[NSNumber numberWithBool:NO] forKey:AUTOCHECK_VERSION]; +//// break; +//// } +//// } +//// else if(latestBuild < currentBuild) { +//// if([confirm boolValue]) { +//// NSAlert * alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Betatester!", nil) defaultButton:NSLocalizedString(@"OK", nil) alternateButton:nil otherButton:nil informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"Your build (%d) is newer than the latest available (%d), so you must be a betatester. Congratulations!", nil), currentBuild, latestBuild]]; +//// [(TPMainController*)[NSApp delegate] presentAlert:alert]; +//// } +//// } +//// else { +//// if([confirm boolValue]) { +//// NSAlert * alert = [NSAlert alertWithMessageText:NSLocalizedString(@"teleport is up-to-date.", nil) defaultButton:NSLocalizedString(@"OK", nil) alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(@"You have the most recent version of teleport.", nil)]; +//// [(TPMainController*)[NSApp delegate] presentAlert:alert]; +//// } +//// } +//} + +//- (void)threadedCheckVersionAndConfirm:(NSNumber*)confirm +//{ +// NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +// +// NSMutableDictionary * productVersionDict = [NSMutableDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:VERSION_CHECK_URL]]; +// if(productVersionDict == nil) { +// [pool release]; +// return; +// } +// +// [productVersionDict setObject:confirm forKey:@"TPConfirm"]; +// +// [self performSelectorOnMainThread:@selector(_handleCheckVersionWithDictionary:) withObject:productVersionDict waitUntilDone:YES]; +// +// [pool release]; +//} +// +//- (void)updateToVersion:(NSString*)version fromURL:(NSString*)urlString +//{ +// NSURL * url = [NSURL URLWithString:urlString]; +// [[NSWorkspace sharedWorkspace] openURL:url]; +//} + +@end diff --git a/TPMessage.h b/TPMessage.h new file mode 100644 index 0000000..46e0000 --- /dev/null +++ b/TPMessage.h @@ -0,0 +1,40 @@ +// +// TPMessage.h +// Teleport +// +// Created by JuL on Wed Dec 03 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +extern const unsigned TPMessageHeaderLength; + +@interface TPMessage : NSObject +{ + TPDataLength _dataLength; + TPMsgType _msgType; + + NSData * _additionalData; +} + +- (instancetype) initWithRawData:(NSData*)rawData; + +/* Protocol messages */ ++ (instancetype) messageWithType:(TPMsgType)type; ++ (instancetype) messageWithType:(TPMsgType)type andData:(NSData*)data; ++ (instancetype) messageWithType:(TPMsgType)type andString:(NSString*)string; ++ (instancetype) messageWithType:(TPMsgType)type andInfoDict:(NSDictionary*)infoDict; + +/* Accessors */ +@property (nonatomic, readonly, copy) NSData *rawData; + +@property (nonatomic, readonly) TPMsgType msgType; +@property (nonatomic, readonly) TPDataLength msgLength; +@property (nonatomic, readonly) TPDataLength dataLength; + +@property (nonatomic, readonly, copy) NSData *data; +@property (nonatomic, readonly, copy) NSString *string; +@property (nonatomic, readonly, copy) NSDictionary *infoDict; + +@end diff --git a/TPMessage.m b/TPMessage.m new file mode 100644 index 0000000..e4a773f --- /dev/null +++ b/TPMessage.m @@ -0,0 +1,189 @@ +// +// TPMessage.m +// Teleport +// +// Created by JuL on Wed Dec 03 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPMessage.h" +#import "TPRemoteHost.h" +#import "TPLocalHost.h" +#import "TPUtils.h" + +const unsigned TPMessageHeaderLength = sizeof(TPMsgType)+sizeof(TPDataLength); + +@interface TPMessage (Private) + +- (instancetype) initWithType:(TPMsgType)type; +- (instancetype) initWithType:(TPMsgType)type andData:(NSData*)data; +- (instancetype) initWithType:(TPMsgType)type andString:(NSString*)string; +- (instancetype) initWithType:(TPMsgType)type andInfoDict:(NSDictionary*)infoDict; + +@end + +@implementation TPMessage + +- (instancetype) init +{ + self = [super init]; + + _additionalData = nil; + + return self; +} + +- (instancetype) initWithRawData:(NSData*)rawData +{ + self = [self init]; + + int pos = 0; + + @try { + /* Read message type */ + [rawData _readBytes:&_msgType withSize:sizeof(TPMsgType) atPos:&pos]; + + /* Read data length */ + [rawData _readBytes:&_dataLength withSize:sizeof(TPDataLength) atPos:&pos]; + if([self dataLength] > 0) { + TPDataLength dataLength = [self dataLength]; + if(dataLength > [rawData length]-pos) { // not enough data! + return nil; + } + else + _additionalData = [[rawData subdataWithRange:NSMakeRange(pos, dataLength)] copy]; + } + else + _additionalData = nil; + } + @catch(NSException * exception) { + return nil; + } + + return self; +} + + +- (NSData*)rawData +{ + NSMutableData * rawData = [[NSMutableData alloc] init]; + + /* Write message type */ + [rawData appendData:[NSData dataWithBytes:&_msgType length:sizeof(TPMsgType)]]; + + /* Optionally write data */ + if(_additionalData != nil) { + _dataLength = NSSwapHostLongLongToBig([_additionalData length]); + [rawData appendData:[NSData dataWithBytes:&_dataLength length:sizeof(TPDataLength)]]; + [rawData appendData:_additionalData]; + } + else { + _dataLength = NSSwapHostLongLongToBig(0); + [rawData appendData:[NSData dataWithBytes:&_dataLength length:sizeof(TPDataLength)]]; + } + + return rawData; +} + + +#pragma mark - +#pragma mark Protocol messages + +- (instancetype) initWithType:(TPMsgType)type +{ + self = [self init]; + _msgType = NSSwapHostIntToBig(type); + return self; +} + +- (instancetype) initWithType:(TPMsgType)type andData:(NSData*)data +{ + self = [self initWithType:type]; + + _additionalData = data; + + return self; +} + +- (instancetype) initWithType:(TPMsgType)type andString:(NSString*)string +{ + self = [self initWithType:type]; + + _additionalData = [string dataUsingEncoding:NSUTF8StringEncoding]; + + return self; +} + +- (instancetype) initWithType:(TPMsgType)type andInfoDict:(NSDictionary*)infoDict +{ + self = [self initWithType:type]; + + _additionalData = [NSArchiver archivedDataWithRootObject:infoDict]; + + return self; +} + ++ (instancetype) messageWithType:(TPMsgType)type +{ + TPMessage * message = [[TPMessage alloc] initWithType:type]; + return message; +} + ++ (instancetype) messageWithType:(TPMsgType)type andData:(NSData*)data +{ + TPMessage * message = [[TPMessage alloc] initWithType:type andData:data]; + return message; +} + ++ (instancetype) messageWithType:(TPMsgType)type andString:(NSString*)string +{ + TPMessage * message = [[TPMessage alloc] initWithType:type andString:string]; + return message; +} + ++ (instancetype) messageWithType:(TPMsgType)type andInfoDict:(NSDictionary*)infoDict +{ + TPMessage * message = [[TPMessage alloc] initWithType:type andInfoDict:infoDict]; + return message; +} + + +#pragma mark - +#pragma mark Accessors + +- (TPMsgType)msgType +{ + return NSSwapBigIntToHost(_msgType); +} + +- (TPDataLength)msgLength +{ + return [self dataLength] + TPMessageHeaderLength; +} + +- (TPDataLength)dataLength +{ + return NSSwapBigLongLongToHost(_dataLength); +} + +- (NSData*)data +{ + return _additionalData; +} + +- (NSString*)string +{ + return [[NSString alloc] initWithData:_additionalData encoding:NSUTF8StringEncoding]; +} + +- (NSDictionary*)infoDict +{ + return (NSDictionary*)[NSUnarchiver unarchiveObjectWithData:_additionalData]; +} + +- (NSString*)description +{ + return [NSString stringWithFormat:@"%@ type=%ld data length=%lu", [super description], _msgType, (unsigned long)[_additionalData length]]; +} + +@end diff --git a/TPNetworkConfigurationWatcher.h b/TPNetworkConfigurationWatcher.h new file mode 100644 index 0000000..17eb773 --- /dev/null +++ b/TPNetworkConfigurationWatcher.h @@ -0,0 +1,31 @@ +// +// TPNetworkConfigurationWatcher.h +// teleport +// +// Created by Julien Robert on 13/04/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import +#import + +@interface TPNetworkConfigurationWatcher : NSObject +{ + SCDynamicStoreRef _storeRef; + CFRunLoopSourceRef _sourceRef; + + id _delegate; +} + +- (void)setDelegate:(id)delegate; + +- (void)startWatching; +- (void)stopWatching; + +@end + +@interface NSObject (TPNetworkConfigurationWatcherDelegate) + +- (void)networkConfigurationDidChange:(TPNetworkConfigurationWatcher*)watcher; + +@end diff --git a/TPNetworkConfigurationWatcher.m b/TPNetworkConfigurationWatcher.m new file mode 100644 index 0000000..20bb7d1 --- /dev/null +++ b/TPNetworkConfigurationWatcher.m @@ -0,0 +1,117 @@ +// +// TPNetworkConfigurationWatcher.m +// teleport +// +// Created by Julien Robert on 13/04/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import "TPNetworkConfigurationWatcher.h" + +static void _networkChangeCallBack(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info); + +@implementation TPNetworkConfigurationWatcher + +- (void)setDelegate:(id)delegate +{ + _delegate = delegate; +} + +- (void)startWatching +{ + SCDynamicStoreContext context = { + 0, // version + (__bridge void *)(self), // info + NULL, // retain + NULL, // release + NULL, // copyDescription + }; + + _storeRef = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("com.abyssoft.teleport"), &_networkChangeCallBack, &context); + if(_storeRef == NULL) { + NSLog(@"Error creating the dynamic store"); + } + else { + CFMutableArrayRef keys = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFMutableArrayRef patterns = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFStringRef key; + + /* watch for IPv4 configuration changes (e.g. new default route) */ + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetIPv4); + CFArrayAppendValue(keys, key); + CFRelease(key); + + /* as above, but for IPv6 */ + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetIPv6); + CFArrayAppendValue(keys, key); + CFRelease(key); + + /* watch for IPv4 interface configuration changes */ + key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); + CFArrayAppendValue(patterns, key); + CFRelease(key); + + /* as above, but for IPv6 */ + key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); + CFArrayAppendValue(patterns, key); + CFRelease(key); + + /* watch for DNS interface configuration changes */ + key = SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetDNS); + CFArrayAppendValue(keys, key); + CFRelease(key); + + _sourceRef = SCDynamicStoreCreateRunLoopSource(NULL, _storeRef, 0); + + if(_sourceRef == NULL) { + NSLog(@"Error creating the dynamic store runloop source"); + } + else { + CFRunLoopAddSource(CFRunLoopGetCurrent(), _sourceRef, kCFRunLoopDefaultMode); + + if(!SCDynamicStoreSetNotificationKeys(_storeRef, (CFArrayRef)keys, (CFArrayRef)patterns)) { + NSLog(@"Error setting the dynamic store notification keys"); + } + + CFRelease(_sourceRef); + } + + CFRelease(keys); + CFRelease(patterns); + } +} + +- (void)stopWatching +{ + if (_sourceRef) { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), _sourceRef, kCFRunLoopDefaultMode); + CFRelease(_sourceRef); + _sourceRef = NULL; + } +} + +- (void)_networkChanged +{ + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_notifyNetworkChanged) object:nil]; + [self performSelector:@selector(_notifyNetworkChanged) withObject:nil afterDelay:1.0]; +} + +- (void)_notifyNetworkChanged +{ + if([_delegate respondsToSelector:@selector(networkConfigurationDidChange:)]) { + [_delegate networkConfigurationDidChange:self]; + } +} + +@end + +static void _networkChangeCallBack(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) +{ + @autoreleasepool { + TPNetworkConfigurationWatcher * self = (__bridge TPNetworkConfigurationWatcher*)info; + + [self _networkChanged]; + + } +} + diff --git a/TPNetworkConnection.h b/TPNetworkConnection.h new file mode 100644 index 0000000..99cffe5 --- /dev/null +++ b/TPNetworkConnection.h @@ -0,0 +1,54 @@ +// +// TPNetworkConnection.h +// teleport +// +// Created by JuL on Mon Dec 29 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +#import "TPHost.h" + +@class TPMessage, TPRemoteHost; +@class TPTransfersManager, TPTransfer; + +@interface TPNetworkConnection : NSObject +{ + TPTCPSocket * _socket; + + NSMutableArray * _pendingMessages; + TPRemoteHost * _connectedHost; + TPHostCapability _capabilities; + + NSMutableData * _msgBuffer; + + TPTransfersManager * _transfersManager; + + id _delegate; +} + +- (instancetype) initWithSocket:(TPTCPSocket*)socket NS_DESIGNATED_INITIALIZER; +@property (nonatomic, readonly, strong) TPTCPSocket *socket; + +@property (nonatomic, readonly, strong) TPTransfersManager *transfersManager; + +@property (nonatomic, strong) TPRemoteHost *connectedHost; + +- (BOOL)isValidForHost:(TPRemoteHost*)host; + +@property (nonatomic, readonly) TPHostCapability localHostCapabilities; + +@property (nonatomic, unsafe_unretained) id delegate; + +- (void)disconnect; +- (BOOL)sendMessage:(TPMessage*)message; + +@end + +@interface NSObject (TPNetworkConnection_delegate) + +- (void)connectionDisconnected:(TPNetworkConnection*)connection; +- (void)connection:(TPNetworkConnection*)connection receivedMessage:(TPMessage*)message; + +@end diff --git a/TPNetworkConnection.m b/TPNetworkConnection.m new file mode 100644 index 0000000..645ba54 --- /dev/null +++ b/TPNetworkConnection.m @@ -0,0 +1,237 @@ +// +// TPNetworkConnection.m +// teleport +// +// Created by JuL on Mon Dec 29 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPNetworkConnection.h" +//#import "TPNetworkConnection_Private.h" +#import "TPPreferencesManager.h" +#import "TPTransfersManager.h" +#import "TPBezelController.h" +#import "TPHostsManager.h" +#import "TPLocalHost.h" +#import "TPTCPSecureSocket.h" +#import "TPMessage.h" + +@interface TPNetworkConnection (Internal) + +@property (nonatomic, readonly) BOOL _sendPendingMessages; + +@end + +@implementation TPNetworkConnection + +- (instancetype) initWithSocket:(TPTCPSocket*)socket +{ + self = [super init]; + + _socket = socket; + [_socket setDelegate:self]; + + _connectedHost = nil; + _capabilities = [[TPLocalHost localHost] capabilities]; + + _delegate = nil; + _msgBuffer = [[NSMutableData alloc] init]; + + _pendingMessages = [[NSMutableArray alloc] init]; + + DebugLog(@"init connection %@", self); + + return self; +} + +- (void)dealloc +{ + [self disconnect]; + + DebugLog(@"connection %@ dealloc", self); + _delegate = nil; + if([_socket delegate] == self) { + [_socket setDelegate:nil]; + } +} + +#if 0 +- (id)retain +{ + DebugLog(@"retain connection %@ (%d>%d)", self, [self retainCount], [self retainCount]+1); + return [super retain]; +} + +- (void)release +{ + DebugLog(@"release connection %@ (%d>%d)", self, [self retainCount], [self retainCount]-1); + [super release]; +} + +- (id)autorelease +{ + DebugLog(@"autorelease connection %@ (%d)", self, [self retainCount]); + return [super autorelease]; +} +#endif + +- (TPTCPSocket*)socket +{ + return _socket; +} + +- (void)setDelegate:(id)delegate +{ + _delegate = delegate; +} + +- (id)delegate +{ + return _delegate; +} + +- (TPTransfersManager*)transfersManager +{ + return _transfersManager; +} + +- (TPHostCapability)localHostCapabilities +{ + return _capabilities; +} + +- (TPRemoteHost*)connectedHost +{ + return _connectedHost; +} + +- (void)setConnectedHost:(TPRemoteHost*)connectedHost +{ + if(connectedHost != _connectedHost) { + _connectedHost = connectedHost; + } + + NSString * remoteAddress = [_socket remoteAddress]; + if(remoteAddress != nil) + [_connectedHost setAddress:remoteAddress]; +} + +- (BOOL)isValidForHost:(TPRemoteHost*)host +{ + if(host == nil || _connectedHost == nil) { + return NO; + } + if(![_connectedHost isEqual:host]) { + return NO; + } + if([self localHostCapabilities] != [[TPLocalHost localHost] capabilities]) { + return NO; + } + if(![[_socket remoteAddress] isEqualToString:[host address]]) { + return NO; + } + return YES; +} + + +#pragma mark - +#pragma mark Commands + +- (void)disconnect +{ + [_socket close]; + [_pendingMessages removeAllObjects]; +} + +- (BOOL)sendMessage:(TPMessage*)message +{ + if([_socket socketState] == TPSocketConnectedState) { +#if DEBUG_GENERAL + DebugLog(@"%s %@", __PRETTY_FUNCTION__, message); +#endif + NSData * rawData = [message rawData]; + BOOL result = [_socket sendData:rawData]; + if(!result) { + if(_delegate && [_delegate respondsToSelector:@selector(connectionDisconnected:)]) + [_delegate connectionDisconnected:self]; + } + return result; + } + else if([_socket socketState] == TPSocketConnectingState) { + [_pendingMessages addObject:message]; + return YES; + } + else + return NO; +} + +- (BOOL)_sendPendingMessages +{ + BOOL success = YES; + + if([_pendingMessages count] > 0) { + NSEnumerator * messagesEnum = [_pendingMessages objectEnumerator]; + TPMessage * pendingMessage; + + while((pendingMessage = [messagesEnum nextObject]) != nil) { + if(![self sendMessage:pendingMessage]) { + success = NO; + DebugLog(@"error sending pending message %@", pendingMessage); + } + } + + [_pendingMessages removeAllObjects]; + } + + return success; +} + + +#pragma mark - +#pragma mark TCP Socket delegate + +- (void)tcpSocket:(TPTCPSocket*)tcpSocket gotData:(NSData*)data +{ +#if DEBUG_GENERAL + DebugLog(@"%@ got data %@", tcpSocket, data); +#endif + + if([data length] == 0) { + DebugLog(@"empty data"); + return; + } + + [_msgBuffer appendData:data]; + + BOOL cont = YES; + while([_msgBuffer length] >= TPMessageHeaderLength && cont) { + TPMessage * message = [[TPMessage alloc] initWithRawData:_msgBuffer]; + if(message != nil) { + TPDataLength msgLength = [message msgLength]; + if(msgLength > 0) { + [_msgBuffer replaceBytesInRange:NSMakeRange(0, msgLength) withBytes:NULL length:0]; + if([_delegate respondsToSelector:@selector(connection:receivedMessage:)]) + [_delegate connection:self receivedMessage:message]; + } + else + cont = NO; + } + else + cont = NO; + } + +} + +- (void)tcpSocketConnectionClosed:(TPTCPSocket*)tcpSocket +{ + [_msgBuffer setData:[NSData data]]; + + + if(_delegate && [_delegate respondsToSelector:@selector(connectionDisconnected:)]) + [_delegate connectionDisconnected:self]; + + [self disconnect]; + +} + +@end diff --git a/TPOptionsController.h b/TPOptionsController.h new file mode 100644 index 0000000..359911f --- /dev/null +++ b/TPOptionsController.h @@ -0,0 +1,38 @@ +// +// TPOptionsController.h +// teleport +// +// Created by Julien Robert on 02/04/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import + +@class TPRemoteHost, TPLayoutView, TPKeyComboView, TPLayoutScreenView, TPLayoutOptionsProxy; + +@interface TPOptionsController : NSResponder +{ + IBOutlet NSObjectController * hostController; + IBOutlet NSTextField * titleTextField; + IBOutlet NSView * hostOptionsView; + IBOutlet TPLayoutView * layoutView; + IBOutlet TPKeyComboView * keyComboView; + IBOutlet NSButton * restoreSaveDefaultsButton; + + TPRemoteHost * _host; + TPLayoutOptionsProxy * _optionsProxy; + TPLayoutScreenView * _currentScreenView; + NSRect _currentScreenFrame; +} + ++ (TPOptionsController*)controller; + +- (void)showOptionsForHost:(TPRemoteHost*)host sharedScreenIndex:(unsigned)screenIndex fromRect:(NSRect)frame; +- (IBAction)restoreToDefaults:(id)sender; +- (IBAction)useAsDefaults:(id)sender; +- (IBAction)closeOptions:(id)sender; + +@property (nonatomic, readonly, strong) TPRemoteHost *host; +@property (nonatomic, readonly, strong) id hostOptions; + +@end diff --git a/TPOptionsController.m b/TPOptionsController.m new file mode 100644 index 0000000..2af71c5 --- /dev/null +++ b/TPOptionsController.m @@ -0,0 +1,241 @@ +// +// TPOptionsController.m +// teleport +// +// Created by Julien Robert on 02/04/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import "TPOptionsController.h" +#import "TPLayoutView.h" +#import "TPLayoutRemoteHostView.h" +#import "TPAnimationManager.h" +#import "TPRemoteHost.h" +#import "PTKeyCombo.h" +#import "TPKeyComboView.h" + +static TPOptionsController * _controller = nil; + +@interface TPLayoutOptionsProxy : NSObject +{ + TPOptionsController * _optionsController; + NSMutableSet * _allObservedKeys; +} + +- (instancetype) initWithOptionsController:(TPOptionsController*)optionsController NS_DESIGNATED_INITIALIZER; + +- (void)willChangeAllObservedKeys; +- (void)didChangeAllObservedKeys; + +@end + +@implementation TPLayoutOptionsProxy + +- (instancetype) initWithOptionsController:(TPOptionsController*)optionsController +{ + self = [super init]; + + _optionsController = optionsController; + + return self; +} + + +- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context +{ + [super addObserver:observer forKeyPath:keyPath options:options context:context]; + + if(_allObservedKeys == nil) { + _allObservedKeys = [[NSMutableSet alloc] init]; + } + + [_allObservedKeys addObject:keyPath]; +} + +- (id)valueForKey:(NSString*)key +{ + return [[_optionsController host] optionForKey:key]; +} + +- (void)setValue:(id)value forKey:(NSString*)key +{ + [self willChangeValueForKey:key]; + [_optionsController willChangeValueForKey:@"hasCustomOptions"]; + [[[_optionsController host] options] setValue:value forKey:key]; + [_optionsController didChangeValueForKey:@"hasCustomOptions"]; + [self didChangeValueForKey:key]; +} + +- (void)willChangeAllObservedKeys +{ + for(NSString * key in _allObservedKeys) { + [self willChangeValueForKey:key]; + } +} + +- (void)didChangeAllObservedKeys +{ + for(NSString * key in _allObservedKeys) { + [self didChangeValueForKey:key]; + } +} + +@end + + +@implementation TPOptionsController + ++ (TPOptionsController*)controller +{ + if(_controller == nil) + _controller = [[TPOptionsController alloc] init]; + return _controller; +} + +- (instancetype) init +{ + self = [super init]; + + _controller = self; + + return self; +} + +- (void)awakeFromNib +{ + [keyComboView bind:@"keyCombo" toObject:self withKeyPath:@"host.keyCombo" options:nil]; + [keyComboView setDelegate:(id)self]; + + //NSResponder * nextResponder = [hostOptionsView nextResponder]; + [hostOptionsView setNextResponder:self]; + //[self setNextResponder:nextResponder]; +} + +- (void)showOptionsForHost:(TPRemoteHost*)host sharedScreenIndex:(unsigned)screenIndex fromRect:(NSRect)blabla +{ + [self willChangeValueForKey:@"hostOptions"]; + + _host = host; + _optionsProxy = [[TPLayoutOptionsProxy alloc] initWithOptionsController:self]; + + [self didChangeValueForKey:@"hostOptions"]; + + [titleTextField setStringValue:[NSString stringWithFormat:@"Options for %@", [host computerName]]]; + + TPLayoutRemoteHostView * remoteHostView = [layoutView remoteHostViewForHost:host]; + _currentScreenView = [remoteHostView screenViewAtIndex:screenIndex]; + _currentScreenFrame = [_currentScreenView frame]; + NSRect frame = [_currentScreenView convertRect:[_currentScreenView bounds] toView:layoutView]; + + [_currentScreenView removeFromSuperview]; + [_currentScreenView setFrame:frame]; + [layoutView addSubview:_currentScreenView]; + + NSRect optionsFrame = [TPAnimationManager rect:[hostOptionsView bounds] centeredAtPoint:NSMakePoint(NSMidX(frame), NSMidY(frame))]; + optionsFrame = [TPAnimationManager rect:optionsFrame snappedInsideRect:[layoutView bounds] margin:16.0]; + optionsFrame.origin.x = round(optionsFrame.origin.x); + optionsFrame.origin.y = round(optionsFrame.origin.y); + + [hostOptionsView setFrame:optionsFrame]; + [hostOptionsView setHidden:YES]; + [layoutView addSubview:hostOptionsView]; + + [TPAnimationManager flipAnimationFromView:_currentScreenView toView:hostOptionsView invertRotation:NO delegate:self]; +} + +- (IBAction)restoreToDefaults:(id)sender +{ + [self willChangeValueForKey:@"hasCustomOptions"]; + [_optionsProxy willChangeAllObservedKeys]; + [_host resetCustomOptions]; + [_optionsProxy didChangeAllObservedKeys]; + [self didChangeValueForKey:@"hasCustomOptions"]; +} + +- (IBAction)useAsDefaults:(id)sender +{ + [self willChangeValueForKey:@"hasCustomOptions"]; + [_host makeDefaultOptions]; + [self didChangeValueForKey:@"hasCustomOptions"]; +} + +- (IBAction)closeOptions:(id)sender +{ + [TPAnimationManager flipAnimationFromView:hostOptionsView toView:_currentScreenView invertRotation:YES delegate:self]; +} + +- (void)keyComboDidChange:(PTKeyCombo*)keyCombo +{ + [_host setKeyCombo:keyCombo]; +} + +- (void)flagsChanged:(NSEvent *)theEvent +{ + if(([theEvent modifierFlags] & NSAlternateKeyMask) != 0) { + [restoreSaveDefaultsButton setTitle:NSLocalizedStringFromTableInBundle(@"Restore to Defaults", nil, [NSBundle bundleForClass:[self class]], @"Button title")]; + [restoreSaveDefaultsButton setAction:@selector(restoreToDefaults:)]; + } + else { + [restoreSaveDefaultsButton setTitle:NSLocalizedStringFromTableInBundle(@"Use as Defaults", nil, [NSBundle bundleForClass:[self class]], @"Button title")]; + [restoreSaveDefaultsButton setAction:@selector(useAsDefaults:)]; + } +} + +- (void)keyDown:(NSEvent*)event +{ + unsigned short keyCode = [event keyCode]; + if(keyCode == 53) { + [self closeOptions:nil]; + } +} + +- (void)animationDidComplete +{ + if([hostOptionsView isHidden]) { + [self willChangeValueForKey:@"hostOptions"]; + + _host = nil; + _optionsProxy = nil; + + [self didChangeValueForKey:@"hostOptions"]; + + [layoutView updateLayout]; + + [[NSNotificationCenter defaultCenter] postNotificationName:TPHostDidUpdateNotification object:_host userInfo:nil]; + + TPLayoutHostView * hostView = [_currentScreenView hostView]; + [_currentScreenView removeFromSuperview]; + [_currentScreenView setFrame:_currentScreenFrame]; + [hostView addSubview:_currentScreenView]; + + [layoutView updateLayout]; + + _currentScreenView = nil; + } + else { + [[hostOptionsView window] makeFirstResponder:hostOptionsView]; + [hostOptionsView setNextResponder:self]; + } +} + +- (TPRemoteHost*)host +{ + return _host; +} + +- (id)hostOptions +{ + return _optionsProxy; +} + ++ (NSSet*)keyPathsForValuesAffectingHasCustomOptions +{ + return [NSSet setWithObject:@"hostOptions"]; +} + +- (BOOL)hasCustomOptions +{ + return [_host hasCustomOptions]; +} + +@end diff --git a/TPOptionsView.h b/TPOptionsView.h new file mode 100644 index 0000000..d34dfa5 --- /dev/null +++ b/TPOptionsView.h @@ -0,0 +1,16 @@ +// +// TPOptionsView.h +// teleport +// +// Created by Julien Robert on 16/01/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import + + +@interface TPOptionsView : NSView +{ +} + +@end diff --git a/TPOptionsView.m b/TPOptionsView.m new file mode 100644 index 0000000..db30d65 --- /dev/null +++ b/TPOptionsView.m @@ -0,0 +1,73 @@ +// +// TPOptionsView.m +// teleport +// +// Created by Julien Robert on 16/01/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import "TPOptionsView.h" +#import "TPBezierPath.h" +#import "TPRemoteHost.h" + +@implementation TPOptionsView + +- (instancetype)initWithFrame:(NSRect)frameRect +{ + self = [super initWithFrame:frameRect]; + +#if 0 + if ([self respondsToSelector:@selector(setAppearance:)]) { + [self setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameLightContent]]; + } +#endif + + NSShadow * shadow = [[NSShadow alloc] init]; + [shadow setShadowBlurRadius:2.0]; + [shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.5]]; + [shadow setShadowOffset:NSMakeSize(0.0, -2.0)]; + [self setShadow:shadow]; + + return self; +} + +- (void)drawRect:(NSRect)rect +{ + CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort]; + +// TPRemoteHost * remoteHost = (TPRemoteHost*)[hostController content]; + +// NSData * data = [remoteHost backgroundImageData]; +// if(data != nil) { +// CGContextScaleCTM(ctx, 1.0, -1.0); +// NSImage * backgroundImage = [[NSImage alloc] initWithData:data]; +// NSRect adjustedRect = [self bounds]; +// adjustedRect.origin.y -= NSHeight(adjustedRect); +// [backgroundImage drawInRect:adjustedRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; +// [backgroundImage release]; +// } +// +// CGContextScaleCTM(ctx, 1.0, -1.0); + +// NSBezierPath * path = [NSBezierPath bezierPathWithRect:[self bounds]]; + [NSBezierPath drawRect:[self bounds] withGradientFrom:[NSColor colorWithCalibratedWhite:0.9 alpha:1.0] to:[NSColor colorWithCalibratedWhite:0.95 alpha:1.0]]; + +// +// CGContextSetGrayFillColor(ctx, 0.9, 0.85); + CGRect strokeRect = NSRectToCGRect(NSInsetRect([self bounds], 0.5, 0.5)); + CGContextSetLineWidth(ctx, 1.0); + + CGContextSetGrayStrokeColor(ctx, 0.7, 1.0); + CGContextStrokeRect(ctx, strokeRect); + + strokeRect = CGRectInset(strokeRect, 1.0, 1.0); + CGContextSetGrayStrokeColor(ctx, 1.0, 1.0); + CGContextStrokeRect(ctx, strokeRect); +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +@end diff --git a/TPPasteboardTransfer.h b/TPPasteboardTransfer.h new file mode 100644 index 0000000..312e902 --- /dev/null +++ b/TPPasteboardTransfer.h @@ -0,0 +1,31 @@ +// +// TPPasteboardTransfer.h +// teleport +// +// Created by JuL on 14/02/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import + +#import "TPTransfer.h" + +@interface TPOutgoingPasteboardTransfer : TPOutgoingTransfer +{ + NSString * _pasteboardName; + unsigned long long _maxSize; +} + +- (void)setPasteboardName:(NSString*)pasteboardName; +- (void)setMaxSize:(unsigned long long)maxSize; + +@property (nonatomic, readonly) CFIndex generationCount; + +@end + +@interface TPIncomingPasteboardTransfer : TPIncomingTransfer +{ + NSString * _pasteboardName; +} + +@end diff --git a/TPPasteboardTransfer.m b/TPPasteboardTransfer.m new file mode 100644 index 0000000..c843e33 --- /dev/null +++ b/TPPasteboardTransfer.m @@ -0,0 +1,227 @@ +// +// TPPasteboardTransfer.m +// teleport +// +// Created by JuL on 14/02/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import "TPTransfer_Private.h" +#import "TPPasteboardTransfer.h" +#import "TPPreferencesManager.h" + +static NSString * TPPasteboardTransferNameKey = @"TPPasteboardTransferName"; +static NSString * TPPasteboardTransferChangeCountKey = @"TPPasteboardTransferChangeCount"; + +static inline BOOL TPTypeNeedsSwapping(NSString * type) +{ + if(CFByteOrderGetCurrent() == CFByteOrderBigEndian) { + return NO; + } + else { + return [type isEqualToString:@"public.utf16-plain-text"] || [type isEqualToString:@"CorePasteboardFlavorType 0x75747874"]; // utxt + } +} + +static inline BOOL TPTypeNeedsIgnoring(NSString * type) +{ + return [type isEqualToString:@"com.apple.txn.text-multimedia-data"] || [type isEqualToString:@"CorePasteboardFlavorType 0x7478746E"]; // txtn +} + +@implementation TPOutgoingPasteboardTransfer + +- (instancetype) init +{ + self = [super init]; + + _pasteboardName = NSGeneralPboard; + + return self; +} + + +- (NSString*)type +{ + return @"TPIncomingPasteboardTransfer"; +} + +- (void)setPasteboardName:(NSString*)pasteboardName +{ + if(pasteboardName != _pasteboardName) { + _pasteboardName = pasteboardName; + } +} + +- (void)setMaxSize:(unsigned long long)maxSize +{ + _maxSize = maxSize; +} + +- (BOOL)shouldBeEncrypted +{ + return YES; +} + +- (BOOL)requireTrustedHost +{ + return YES; +} + +- (TPTransferPriority)priority +{ + return TPTransferHighPriority; +} + +- (BOOL)hasFeedback +{ + return YES; +} + +- (NSString*)completionMessage +{ + NSString * sizeString = [NSString sizeStringForSize:[self totalDataLength]]; + return [NSString stringWithFormat:NSLocalizedString(@"Pasteboard sent (%@)", nil), sizeString]; +} + +- (NSString*)errorMessage +{ + return NSLocalizedString(@"Pasteboard not sent", nil); +} + +- (NSDictionary*)infoDict +{ + NSMutableDictionary * infoDict = [[NSMutableDictionary alloc] initWithDictionary:[super infoDict]]; + + [infoDict addEntriesFromDictionary:@{TPPasteboardTransferNameKey: _pasteboardName, + TPPasteboardTransferChangeCountKey: [NSNumber numberWithInt:[[NSPasteboard pasteboardWithName:_pasteboardName] changeCount]]}]; + + return infoDict; +} + +- (NSData*)dataToTransfer +{ + NSMutableDictionary * dictionary = [[NSMutableDictionary alloc] init]; + NSPasteboard * pasteboard = [NSPasteboard pasteboardWithName:_pasteboardName]; + NSArray * pasteboardTypes = [pasteboard types]; + NSEnumerator * typeEnum = [pasteboardTypes objectEnumerator]; + NSString * type; + + unsigned long long pasteboardSize = 0; + while((type = [typeEnum nextObject])) { + if(TPTypeNeedsIgnoring(type)) { + DebugLog(@"Ignoring type %@", type); + continue; + } + + NSData * data = [pasteboard dataForType:type]; + if(data != nil) { + if(TPTypeNeedsSwapping(type)) { + DebugLog(@"Swapping type %@ to BE", type); + CFStringRef string = CFStringCreateFromExternalRepresentation(NULL, (CFDataRef)data, kCFStringEncodingUTF16LE); + if(string != NULL) { + CFDataRef newData = CFStringCreateExternalRepresentation(NULL, (CFStringRef)string, kCFStringEncodingUTF16BE, '?'); + CFRelease(string); + if(newData != NULL) { + data = (NSData*)CFBridgingRelease(newData); + } + } + } + + if([data bytes] != NULL) { // work-around Tiger bug + dictionary[type] = data; + pasteboardSize += [data length]; + } + } + } + + if(pasteboardSize == 0) { + return nil; + } + else if(_maxSize > 0 && pasteboardSize > _maxSize) { + DebugLog(@"maxSize=%lld pasteboardSize=%lld", _maxSize, pasteboardSize); + return nil; + } + + NSData * dataToTransfer = [NSKeyedArchiver archivedDataWithRootObject:dictionary]; + + return dataToTransfer; +} + +- (CFIndex)generationCount +{ +// CFPasteboardRef pasteboardRef = CFPasteboardCreate(NULL, (CFStringRef)_pasteboardName); +// CFPasteboardGetGenerationCount(pasteboardRef); + return 0; +} + +@end + +@implementation TPIncomingPasteboardTransfer + + +- (TPTransferPriority)priority +{ + return TPTransferHighPriority; +} + +- (BOOL)hasFeedback +{ + return YES; +} + +- (NSString*)completionMessage +{ + NSString * sizeString = [NSString sizeStringForSize:_totalDataLength]; + return [NSString stringWithFormat:NSLocalizedString(@"Pasteboard received (%@)", nil), sizeString]; +} + +- (NSString*)errorMessage +{ + return NSLocalizedString(@"Pasteboard failed to receive", nil); +} + +- (BOOL)prepareToReceiveDataWithInfoDict:(NSDictionary*)infoDict fromHost:(TPRemoteHost*)host onPort:(int*)port delegate:(id)delegate +{ + _pasteboardName = [infoDict[TPPasteboardTransferNameKey] copy]; + // int changeCount = [[infoDict objectForKey:TPPasteboardTransferChangeCountKey] intValue]; + // + // NSPasteboard * pasteboard = [NSPasteboard pasteboardWithName:_pasteboardName]; + + return [super prepareToReceiveDataWithInfoDict:infoDict fromHost:host onPort:port delegate:delegate]; +} + +- (void)_receiverDataTransferCompleted +{ + NSMutableDictionary * dictionary = [NSKeyedUnarchiver unarchiveObjectWithData:_data]; + NSPasteboard * pasteboard = [NSPasteboard pasteboardWithName:_pasteboardName]; + NSArray * pasteboardTypes = [dictionary allKeys]; + NSEnumerator * typeEnum = [pasteboardTypes objectEnumerator]; + NSString * type; + + [pasteboard declareTypes:pasteboardTypes owner:nil]; + + while((type = [typeEnum nextObject])) { + NSData * data = dictionary[type]; + + if(TPTypeNeedsSwapping(type)) { + DebugLog(@"Swapping type %@ from BE", type); + CFStringRef string = CFStringCreateFromExternalRepresentation(NULL, (CFDataRef)data, kCFStringEncodingUTF16BE); + if(string != NULL) { + CFDataRef newData = CFStringCreateExternalRepresentation(NULL, (CFStringRef)string, kCFStringEncodingUTF16LE, '?'); + CFRelease(string); + if(newData != NULL) { + data = (NSData*)CFBridgingRelease(newData); + } + } + } + + if([data bytes] != NULL) { + [pasteboard setData:data forType:type]; + } + } + + [super _receiverDataTransferCompleted]; +} + +@end + diff --git a/TPPreferencePane.h b/TPPreferencePane.h new file mode 100644 index 0000000..dbbff29 --- /dev/null +++ b/TPPreferencePane.h @@ -0,0 +1,61 @@ +// +// TPPreferencePane.h +// Teleport +// +// Created by JuL on Mon Dec 08 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +@class TPLayoutView, TPVerticalView, TPLocalHost, TPRemoteHost; +@class TPPreferencesManager; + +@interface TPPreferencePane : NSWindowController +{ + NSView * view; + IBOutlet TPLayoutView * layoutView; + IBOutlet id allowControlCheckbox; + IBOutlet id statusCheckbox; + IBOutlet id aboutWindow; + IBOutlet id versionTextField; + IBOutlet id logoView; + IBOutlet id trustedHostsWindow; + IBOutlet id trustedHostsTableView; + + IBOutlet id showCertificateButton; + IBOutlet id chooseCertificateButton; + + NSWindow * _alertPanel; + NSArray * _trustedHosts; + BOOL _active; +} + ++ (instancetype)preferencePane; + +/* For bindings */ +@property (nonatomic, readonly, strong) TPPreferencesManager *prefs; +@property (nonatomic, readonly, strong) TPLocalHost *localHost; + +- (IBAction)showAboutSheet:(id)sender; +- (void)closeAboutSheet; + +/* Add other */ +- (IBAction)addOther:(id)sender; +- (IBAction)closeAddOther:(id)sender; +- (IBAction)confirmAddOther:(id)sender; + +/* Trusted hosts */ +- (IBAction)showTrustedHosts:(id)sender; +- (IBAction)closeTrustedHosts:(id)sender; +- (IBAction)deleteTrustedHost:(id)sender; +- (void)reloadTrustedHosts; + +/* Certificate actions */ +- (IBAction)showCertificateViewer:(id)sender; +- (IBAction)showCertificateChooser:(id)sender; + +/* Other actions */ +- (IBAction)checkVersion:(id)sender; + +@end diff --git a/TPPreferencePane.m b/TPPreferencePane.m new file mode 100644 index 0000000..9b41798 --- /dev/null +++ b/TPPreferencePane.m @@ -0,0 +1,347 @@ +// +// TPPreferencePane.m +// Teleport +// +// Created by JuL on Mon Dec 08 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPPreferencePane.h" + +#import +#import +#import + +#import "TPLayoutView.h" +#import "TPRemoteHost.h" +#import "TPVersionTextField.h" +#import "TPPreferencesManager.h" +#import "TPHostsManager.h" +#import "TPLocalHost.h" +#import "TPAuthenticationManager.h" +#import "TPMainController.h" + +#define LAYOUT_HEIGHT 500 +#define OPTIONS_HEIGHT 250 + +@implementation TPPreferencePane + ++ (instancetype)preferencePane +{ + static TPPreferencePane *prefPane = nil; + if (prefPane == nil) { + prefPane = [[TPPreferencePane alloc] initWithWindowNibName:@"teleportPref"]; + } + + return prefPane; +} + +- (void)windowDidLoad +{ +#if ! LEGACY_BUILD + if([[TPLocalHost localHost] osVersion] >= TPHostOSVersion(6)) { + [logoView setEnabled:YES]; + } +#endif + + NSBundle * mainBundle = [NSBundle bundleForClass:[self class]]; + NSDictionary * infoDict = [mainBundle infoDictionary]; + NSDictionary * localizedInfoDict = [mainBundle localizedInfoDictionary]; + + NSString * publicVersion = infoDict[@"CFBundleVersion"]; + NSString * buildVersion = infoDict[@"TPRevision"]; + NSString * translationInfo = localizedInfoDict[@"TPTranslationInfoString"]; + + NSArray * versions = @[[NSString stringWithFormat:NSLocalizedString(@"Version %@", nil), publicVersion], + [NSString stringWithFormat:NSLocalizedString(@"Build %@", nil), buildVersion]]; + + if (translationInfo != nil) { + versions = [versions arrayByAddingObject:translationInfo]; + } + + [versionTextField setVersions:versions]; + + NSCell * buttonCell = [[trustedHostsTableView tableColumnWithIdentifier:@"certificate"] dataCell]; + [buttonCell setTarget:self]; + [buttonCell setAction:@selector(showTrustedCertificate:)]; + + BOOL hasSeveralPotentialIdentities = ([[[TPLocalHost localHost] potentialIdentities] count] > 1); + [showCertificateButton setHidden:hasSeveralPotentialIdentities]; + [chooseCertificateButton setHidden:!hasSeveralPotentialIdentities]; + + [[NSNotificationCenter defaultCenter] addObserver:layoutView selector:@selector(updateLayout) name:TPHostsConfigurationDidChangeNotification object:nil]; +} + +- (void)showWindow:(id)sender +{ + [super showWindow:sender]; + [NSApp activateIgnoringOtherApps:YES]; +} + +- (SUUpdater *)updater +{ + return [SUUpdater sharedUpdater]; +} + + +#pragma mark - +#pragma mark IBActions + +- (IBAction)showAboutSheet:(id)sender +{ + [NSApp beginSheet:aboutWindow modalForWindow:[self window] modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; +} + +- (void)closeAboutSheet +{ + //DebugLog(@"closeAboutSheet"); + [NSApp endSheet:aboutWindow]; + [aboutWindow orderOut:nil]; +} + +- (TPPreferencesManager*)prefs +{ + return [TPPreferencesManager sharedPreferencesManager]; +} + +- (TPLocalHost*)localHost +{ + return [TPLocalHost localHost]; +} + + +#pragma mark - +#pragma mark Add other + +- (IBAction)addOther:(id)sender +{ +#if 0 + [addOtherAddressField setStringValue:@""]; + [NSApp beginSheet:addOtherSheet modalForWindow:[[self mainView] window] modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; +#endif +} + +- (IBAction)closeAddOther:(id)sender +{ +#if 0 + [NSApp endSheet:addOtherSheet]; + [addOtherSheet close]; +#endif +} + +- (IBAction)confirmAddOther:(id)sender +{ +#if 0 + NSString * name = [addOtherNameField stringValue]; + NSString * address = [addOtherAddressField stringValue]; + NSSize screenSize; + + screenSize.width = [addOtherWidthField floatValue]; + screenSize.height = [addOtherHeightField floatValue]; + + DebugLog(@"adding %@ (%@) width size %@", name, address, NSStringFromSize(screenSize)); + + TPRemoteHost * remoteHost = [[TPRemoteHost alloc] initWithIdentifier:name address:address]; + + [remoteHost setComputerName:name]; + [remoteHost setScreenSize:screenSize]; + + [remoteHost release]; + [self closeAddOther:sender]; +#endif +} + + +#pragma mark - +#pragma mark Authentication + +- (void)showAuthenticationPendingDialog:(NSNotification*)notification +{ + TPRemoteHost * remoteHost = [[TPHostsManager defaultManager] hostWithIdentifier:[notification object]]; + NSString * msgString = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"Asking host \\U201C%@\\U201D for trust", nil, [NSBundle bundleForClass:[self class]], @"Trust request dialog"), [remoteHost computerName]]; + _alertPanel = NSGetAlertPanel(msgString,NSLocalizedStringFromTableInBundle(@"You should now grant the trust on your other Mac.", nil, [NSBundle bundleForClass:[self class]], @"Trust request message"),NSLocalizedStringFromTableInBundle(@"Cancel", nil, [NSBundle bundleForClass:[self class]], @"Generic cancel button in dialog"),nil,nil); + [NSApp beginSheet:_alertPanel modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain(remoteHost)]; +} + +- (void)closeAuthenticationPendingDialog:(NSNotification*)notification +{ + if(_alertPanel != nil) { + [NSApp endSheet:_alertPanel]; + } +} + +- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + TPRemoteHost * remoteHost = (__bridge TPRemoteHost*)contextInfo; + + switch(returnCode) { + case NSAlertDefaultReturn: + [remoteHost setHostState:TPHostSharedState]; + [[TPAuthenticationManager defaultManager] abortAuthenticationRequest]; + break; + default: + break; + } + + [sheet close]; + _alertPanel = nil; +} + + +#pragma mark - +#pragma mark Trusted hosts + +- (BOOL)tableView:(NSTableView*)tableView handleKeyDown:(NSEvent*)event +{ + BOOL handled = NO; + + NSString * string = [event charactersIgnoringModifiers]; + if([string length] > 0) { + unichar c = [string characterAtIndex:0]; + switch(c) { + case 127: // backspace + [self deleteTrustedHost:tableView]; + handled = YES; + break; + default: + break; + } + } + + return handled; +} + +- (IBAction)showTrustedHosts:(id)sender +{ + [self reloadTrustedHosts]; + [NSApp beginSheet:trustedHostsWindow modalForWindow:[self window] modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; +} + +- (IBAction)closeTrustedHosts:(id)sender +{ + [NSApp endSheet:trustedHostsWindow]; + [trustedHostsWindow orderOut:nil]; +} + +- (IBAction)deleteTrustedHost:(id)sender +{ + NSIndexSet * selectedRows = [trustedHostsTableView selectedRowIndexes]; +#if LEGACY_BUILD + unsigned currentIndex; +#else + NSUInteger currentIndex; +#endif + currentIndex = [selectedRows firstIndex]; + while (currentIndex != NSNotFound) { + TPRemoteHost * trustedHost = _trustedHosts[currentIndex]; + [[TPAuthenticationManager defaultManager] host:trustedHost setTrusted:NO]; + currentIndex = [selectedRows indexGreaterThanIndex:currentIndex]; + } + + [self reloadTrustedHosts]; +} + +#if LEGACY_BUILD +- (int)numberOfRowsInTableView:(NSTableView *)tableView +#else +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView +#endif +{ + return [_trustedHosts count]; +} + +#if LEGACY_BUILD +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row +#else +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row +#endif +{ + TPRemoteHost * remoteHost = _trustedHosts[row]; + NSString * identifier = [tableColumn identifier]; + + if([identifier isEqualToString:@"name"]) + return remoteHost==nil?NSLocalizedStringFromTableInBundle(@"unknown name", nil, [NSBundle bundleForClass:[self class]], @"Name of unknown trusted host"):[remoteHost computerName]; + else if([identifier isEqualToString:@"address"]) + return remoteHost==nil?NSLocalizedStringFromTableInBundle(@"unknown address", nil, [NSBundle bundleForClass:[self class]], @"Address of unknown trusted host"):[remoteHost address]; + + return nil; +} + +#if LEGACY_BUILD +- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)row +#else +- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row +#endif +{ + NSString * identifier = [tableColumn identifier]; + + if([identifier isEqualToString:@"certificate"]) { + TPRemoteHost * remoteHost = _trustedHosts[row]; + [cell setEnabled:[remoteHost isCertified]]; + } +} + +- (IBAction)showTrustedCertificate:(id)sender +{ + TPRemoteHost * remoteHost = _trustedHosts[[sender clickedRow]]; + + NSData * certificateData = [remoteHost certificateData]; + OSErr err; + CSSM_DATA cssmCertData = {[certificateData length], (uint8 *)[certificateData bytes]}; + SecCertificateRef certRef; + if((err = SecCertificateCreateFromData(&cssmCertData, CSSM_CERT_UNKNOWN, CSSM_CERT_ENCODING_UNKNOWN, &certRef)) != noErr) + NSLog(@"Error reading certificate for %@: %d", self, err); + else { + [[SFCertificatePanel sharedCertificatePanel] beginSheetForWindow:[self window] modalDelegate:nil didEndSelector:NULL contextInfo:NULL certificates:@[(__bridge id)certRef] showGroup:NO]; + CFRelease(certRef); + } +} + +- (void)reloadTrustedHosts +{ + _trustedHosts = [[TPAuthenticationManager defaultManager] trustedHosts]; + [trustedHostsTableView reloadData]; +} + +- (IBAction)checkVersion:(id)sender +{ + [[TPMainController sharedController] checkVersionsAndConfirm:YES]; +} + +- (IBAction)showCertificateViewer:(id)sender +{ + SecIdentityRef identityRef = [[TPLocalHost localHost] identity]; + + if(identityRef != NULL) { + SecCertificateRef certRef; + SecIdentityCopyCertificate(identityRef, &certRef); + if(certRef != NULL) { + [[SFCertificatePanel sharedCertificatePanel] beginSheetForWindow:[self window] modalDelegate:nil didEndSelector:NULL contextInfo:NULL certificates:@[(__bridge id)certRef] showGroup:NO]; + CFRelease(certRef); + } + } +} + +- (IBAction)showCertificateChooser:(id)sender +{ + SFChooseIdentityPanel * chooserPanel = [SFChooseIdentityPanel sharedChooseIdentityPanel]; + + [chooserPanel setAlternateButtonTitle:NSLocalizedStringFromTableInBundle(@"Cancel", nil, [NSBundle bundleForClass:[self class]], @"Generic cancel button in dialog")]; + + if([chooserPanel respondsToSelector:@selector(setInformativeText:)]) { + [(id)chooserPanel setInformativeText:NSLocalizedStringFromTableInBundle(@"Select the certificate to be used for encryption. It must have the same encryption algorithm as the one used on the other Macs.", nil, [NSBundle bundleForClass:[self class]], @"Informative text in certificate selection panel")]; + } + + [chooserPanel beginSheetForWindow:[self window] modalDelegate:self didEndSelector:@selector(chooseIdentitySheetDidEnd:returnCode:contextInfo:) contextInfo:(__bridge void *)(chooserPanel) identities:[[TPLocalHost localHost] potentialIdentities] message:NSLocalizedStringFromTableInBundle(@"Choose certificate", nil, [NSBundle bundleForClass:[self class]], @"Title in certificate selection panel")]; +} + +- (void)chooseIdentitySheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + if(returnCode == NSOKButton) { + SFChooseIdentityPanel * chooserPanel = (__bridge SFChooseIdentityPanel*)contextInfo; + SecIdentityRef identity = [chooserPanel identity]; + [[self localHost] setIdentity:identity]; + } +} + +@end diff --git a/TPPreferencesManager.h b/TPPreferencesManager.h new file mode 100644 index 0000000..4ca5e12 --- /dev/null +++ b/TPPreferencesManager.h @@ -0,0 +1,92 @@ +// +// TPPreferencesManager.h +// teleport +// +// Created by JuL on 11/08/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import + +#define CURRENT_PREFS_VERSION 7 +#define PREFS_VERSION @"prefsVersion" +#define SHARE_PASTEBOARD @"sharePasteboard" +#define ALLOW_CONTROL @"allowControl" +#define REQUIRE_KEY @"requireKey" +#define DELAYED_SWITCH @"delayedSwitch" +#define SWITCH_DELAY @"switchDelay" +#define SWITCH_WITH_DOUBLE_TAP @"switchWithDoubleTap" +#define LIMIT_PASTEBOARD_SIZE @"limitPasteboardSize" +#define MAX_PASTEBOARD_SIZE @"maxPasteboardSize" +#define AUTOCHECK_VERSION @"autocheckVersion" +#define TRUST_REQUEST_BEHAVIOR @"trustRequestBehavior" +#define ENABLED_ENCRYPTION @"enableEncryption" +#define SWITCH_KEY_TAG @"switchKeyTag" +#define COPY_FILES @"copyFiles" +#define REQUIRE_PASTEBOARD_KEY @"requirePasteboardKey" +#define PASTEBOARD_KEY_TAG @"pasteboardKeyTag" +#define INHIBITION_PERIOD @"hotBorderActivationDelay" +#define SYNC_FIND_PASTEBOARD @"syncFindPasteboard" +#define WARNED_ABOUT_ACCESSIBILITY @"warnedAboutAccessibility" +#define WAKE_ON_LAN @"wakeOnLAN" +#define HIDE_CONTROL_BEZEL @"hideControlBezel" +#define TRUST_LOCAL_CERTIFICATE @"trustLocalCertificate" +#define CERTIFICATE_IDENTIFIER @"certificateIdentifier" +#define SHARED_SCREEN_INDEX @"sharedScreenIndex" +#define SHOW_SWITCH_ANIMATION @"showSwitchAnimation" +#define DOUBLE_TAP_INTERVAL @"doubleTapInterval" +#define SHOW_TEXTUAL_STATUS @"showTextualStatus" +#define TIGER_BEHAVIOR @"tigerBehavior" +#define ADD_TO_LOGIN_ITEMS @"addToLoginItems" +#define DISCONNECT_ON_NETWORK_CONFIG_CHANGE @"disconnectOnNetworkConfigChange" +#define WRAP_ON_STOP_CONTROL @"wrapOnStopControl" +#define PLAY_SWITCH_SOUND @"playSwitchSound" +#define SWITCH_SOUND_PATH @"switchSoundPath" +#define APPLICATIONS_DISABLING_TELEPORT @"appIdentifiersDisablingTeleport" +#define SYNC_MODIFIERS @"syncModifiers" + +#define COMMAND_PORT @"commandPort" +#define TRANSFER_PORT @"transferPort" + +#define COMMAND_KEY_TAG 20 +#define ALT_KEY_TAG 19 +#define CTRL_KEY_TAG 18 +#define SHIFT_KEY_TAG 17 +#define CAPSLOCK_KEY_TAG 16 + +enum { + TRUST_REQUEST_ASK = 0, + TRUST_REQUEST_REJECT = 1, + TRUST_REQUEST_ACCEPT = 2 +}; + +extern NSString * TPPreferencesDidUpgradeNotification; +extern NSString * TPDefaultsDidChangeNotification; + +extern NSString * TPPreferencesPreviousVersionKey; + +@interface NSObject (PreferencesAdditions) + +- (void)bind:(NSString*)binding toPref:(NSString*)prefKey; + +@end + +@interface TPPreferencesManager : NSObject +{ + BOOL _isLocal; +} + ++ (TPPreferencesManager*)sharedPreferencesManager; + +@property (nonatomic, getter=isLocal, readonly) BOOL local; + +- (BOOL)boolForPref:(NSString*)pref; +- (int)intForPref:(NSString*)pref; +- (float)floatForPref:(NSString*)pref; +- (id)valueForPref:(NSString*)pref; + +- (int)portForPref:(NSString*)pref; + +- (BOOL)event:(NSEvent*)event hasRequiredKeyIfNeeded:(NSString*)boolPref withTag:(NSString*)tagPref; + +@end diff --git a/TPPreferencesManager.m b/TPPreferencesManager.m new file mode 100644 index 0000000..cc99f68 --- /dev/null +++ b/TPPreferencesManager.m @@ -0,0 +1,259 @@ +// +// TPPreferencesManager.m +// teleport +// +// Created by JuL on 11/08/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import "TPPreferencesManager.h" +#import "TPLocalHost.h" + +#include + +#if DEBUG_BUILD || PRERELEASE_BUILD + +NSString * TPDefaultsWillChangeNotification = @"TPDefaultsWillChangeDebugNotification"; +NSString * TPDefaultsDidChangeNotification = @"TPDefaultsDidChangeDebugNotification"; +NSString * TPDefaultsChangeNotification = @"TPDefaultsChangeDebugNotification"; + +NSString * TPPreferencesDidUpgradeNotification = @"TPPreferencesDidUpgradeDebugNotification"; + +#else + +NSString * TPDefaultsWillChangeNotification = @"TPDefaultsWillChangeNotification"; +NSString * TPDefaultsDidChangeNotification = @"TPDefaultsDidChangeNotification"; +NSString * TPDefaultsChangeNotification = @"TPDefaultsChangeNotification"; + +NSString * TPPreferencesDidUpgradeNotification = @"TPPreferencesDidUpgradeNotification"; + +#endif + +NSString * TPPreferencesPreviousVersionKey = @"TPPreferencesPreviousVersionKey"; + +static TPPreferencesManager * _sharedPreferencesManager = nil; + +@interface NSUserDefaults (Private) + ++ (void)setStandardUserDefaults:(NSUserDefaults *)sud; + +@end + +@implementation NSObject (PreferencesAdditions) + +- (void)bind:(NSString*)binding toPref:(NSString*)prefKey +{ + [self bind:binding toObject:[TPPreferencesManager sharedPreferencesManager] withKeyPath:prefKey options:nil]; +} + +@end + +@interface TPPreferencesManager (Private) + +@property (nonatomic, readonly, strong) NSUserDefaultsController *_defaultsController; + +@end + +@implementation NSString (PreferencesAdditions) + +- (BOOL)boolValue +{ + NSString * lowercaseString = [self lowercaseString]; + return [lowercaseString isEqualToString:@"yes"] || [lowercaseString isEqualToString:@"true"] || [lowercaseString isEqualToString:@"1"]; +} + +@end + +@implementation TPPreferencesManager + ++ (TPPreferencesManager*)sharedPreferencesManager +{ + if(_sharedPreferencesManager == nil) + _sharedPreferencesManager = [[TPPreferencesManager alloc] init]; + return _sharedPreferencesManager; +} + +- (instancetype) init +{ + self = [super init]; + + NSString * appDomainName = [[NSBundle mainBundle] bundleIdentifier]; + NSString * domainName = [[NSBundle bundleForClass:[self class]] bundleIdentifier]; + _isLocal = [domainName isEqualToString:appDomainName]; + + if(_isLocal) { + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(changeDefault:) name:TPDefaultsChangeNotification object:nil]; + } + else { + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(defaultsWillChange:) name:TPDefaultsWillChangeNotification object:nil]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(defaultsDidChange:) name:TPDefaultsDidChangeNotification object:nil]; + } + + return self; +} + +- (void)dealloc +{ + if(_isLocal) { + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:TPDefaultsChangeNotification object:nil]; + } + else { + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:TPDefaultsWillChangeNotification object:nil]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:TPDefaultsDidChangeNotification object:nil]; + } +} + +- (BOOL)isLocal +{ + return _isLocal; +} + +- (NSUserDefaultsController*)_defaultsController +{ + static NSUserDefaultsController * _userDefaultsController = nil; + + if(_userDefaultsController == nil) { + NSString * appDomainName = [[NSBundle mainBundle] bundleIdentifier]; + NSString * domainName = [[NSBundle bundleForClass:[self class]] bundleIdentifier]; + NSUserDefaults * defaults = [[NSUserDefaults alloc] init]; + + if (![appDomainName isEqualToString:domainName]) { + [defaults removeSuiteNamed:appDomainName]; + [defaults addSuiteNamed:domainName]; + } + + _userDefaultsController = [[NSUserDefaultsController alloc] initWithDefaults:defaults initialValues:nil]; + + [_userDefaultsController setInitialValues:@{SHARE_PASTEBOARD: @YES, + ALLOW_CONTROL: @NO, + REQUIRE_KEY: @NO, + DELAYED_SWITCH: @NO, + SWITCH_DELAY: @0.5f, + SWITCH_WITH_DOUBLE_TAP: @NO, + LIMIT_PASTEBOARD_SIZE: @YES, + MAX_PASTEBOARD_SIZE: @1024, + AUTOCHECK_VERSION: @YES, + /* Secret stuff */ + HIDE_CONTROL_BEZEL: @NO, + @"visibleBorders": @NO, + INHIBITION_PERIOD: @0.2f, + SYNC_FIND_PASTEBOARD: @NO, + WARNED_ABOUT_ACCESSIBILITY: @NO, +#if DEBUG_BUILD || PRERELEASE_BUILD + COMMAND_PORT: @44276, + TRANSFER_PORT: @44277, +#else + COMMAND_PORT: @44176, + TRANSFER_PORT: @44177, +#endif + TRUST_REQUEST_BEHAVIOR: @(TRUST_REQUEST_ASK), + ENABLED_ENCRYPTION: @([[TPLocalHost localHost] hasIdentity]), + SWITCH_KEY_TAG: @ALT_KEY_TAG, + COPY_FILES: @YES, + REQUIRE_PASTEBOARD_KEY: @NO, + PASTEBOARD_KEY_TAG: @COMMAND_KEY_TAG, + WAKE_ON_LAN: @NO, + TRUST_LOCAL_CERTIFICATE: @YES, + SHOW_SWITCH_ANIMATION: @YES, + DOUBLE_TAP_INTERVAL: @0.5f, + SHOW_TEXTUAL_STATUS: @YES, + TIGER_BEHAVIOR: @NO, + ADD_TO_LOGIN_ITEMS: @YES, + DISCONNECT_ON_NETWORK_CONFIG_CHANGE: @NO, + WRAP_ON_STOP_CONTROL: @YES, + PLAY_SWITCH_SOUND: @NO, + SYNC_MODIFIERS: [NSNumber numberWithInt:YES]}]; + + /* Static prefs */ + if([self isLocal]) { + int previousVersion = [self intForPref:PREFS_VERSION]; + if(previousVersion != CURRENT_PREFS_VERSION) { + [self setValue:@CURRENT_PREFS_VERSION forKey:PREFS_VERSION]; + [[NSNotificationCenter defaultCenter] postNotificationName:TPPreferencesDidUpgradeNotification object:self userInfo:@{TPPreferencesPreviousVersionKey: @(previousVersion)}]; + } + } + + } + + return _userDefaultsController; +} + +- (id)valueForKey:(NSString*)key +{ + return [[self _defaultsController] valueForKeyPath:[NSString stringWithFormat:@"values.%@", key]]; +} + +- (BOOL)boolForPref:(NSString*)pref +{ + return [[self valueForKey:pref] boolValue]; +} + +- (int)intForPref:(NSString*)pref +{ + return [[self valueForKey:pref] intValue]; +} + +- (float)floatForPref:(NSString*)pref +{ + return [[self valueForKey:pref] floatValue]; +} + +- (id)valueForPref:(NSString*)pref +{ + return [self valueForKey:pref]; +} + +- (int)portForPref:(NSString*)pref +{ + if([pref isEqualToString:TRANSFER_PORT]) + return [self portForPref:COMMAND_PORT] + 1; + else + return [self intForPref:pref]; +} + +- (void)setValue:(id)value forKey:(NSString*)key +{ + if(_isLocal) { + NSUserDefaultsController * defaultsController = [self _defaultsController]; + [self willChangeValueForKey:key]; + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:TPDefaultsWillChangeNotification object:key userInfo:nil deliverImmediately:YES]; + [defaultsController setValue:value forKeyPath:[NSString stringWithFormat:@"values.%@", key]]; + [[defaultsController defaults] synchronize]; + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:TPDefaultsDidChangeNotification object:key userInfo:nil deliverImmediately:YES]; + [self didChangeValueForKey:key]; + } + else { + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:TPDefaultsChangeNotification object:key userInfo:@{key: value} deliverImmediately:YES]; + } +} + +- (void)changeDefault:(NSNotification*)notification +{ + NSString * key = [notification object]; + id value = [notification userInfo][key]; + [self setValue:value forKeyPath:key]; +} + +- (void)defaultsWillChange:(NSNotification*)notification +{ + NSString * keyPath = [notification object]; + [self willChangeValueForKey:keyPath]; +} + +- (void)defaultsDidChange:(NSNotification*)notification +{ + NSString * keyPath = [notification object]; + [[[self _defaultsController] defaults] synchronize]; + [self didChangeValueForKey:keyPath]; +} + +- (BOOL)event:(NSEvent*)event hasRequiredKeyIfNeeded:(NSString*)boolPref withTag:(NSString*)tagPref +{ + if(![self boolForPref:boolPref]) + return YES; + + unsigned int keyMask = NSEventMaskFromType([self intForPref:tagPref]); + return (([event modifierFlags] & keyMask) != 0); +} + +@end diff --git a/TPRemoteHost.h b/TPRemoteHost.h new file mode 100644 index 0000000..153a360 --- /dev/null +++ b/TPRemoteHost.h @@ -0,0 +1,106 @@ +// +// TPRemoteHost.h +// teleport +// +// Created by JuL on Fri Feb 27 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPHost.h" +#import "PTKeyCombo.h" + +typedef NS_OPTIONS(NSUInteger, TPHostState) { + TPHostUndefState = 0, + TPHostSharedState = 1 << 0, + TPHostPeeredOfflineState = 1 << 1, + TPHostPeeredOnlineState = 1 << 2, + TPHostControlledState = 1 << 3, + TPHostIncompatibleState = 1 << 4, + TPHostPeeringState = 1 << 5, + TPHostPeeredState = TPHostPeeredOfflineState | TPHostPeeredOnlineState | TPHostControlledState | TPHostPeeringState, + TPHostOnlineState = TPHostSharedState | TPHostPeeredOnlineState | TPHostControlledState | TPHostIncompatibleState | TPHostPeeringState, + TPHostDraggableState = TPHostSharedState | TPHostPeeredOnlineState, + TPHostAllStates = 0xFFFF +} ; + +@class TPOptionsProxy; + +@interface TPRemoteHost : TPHost +{ + NSString * _identifier; + SInt32 _osVersion; + SecCertificateRef _certRef; + NSString * _address; + int _port; + NSImage * _backgroundImage; + NSArray * _screens; + int _localScreenIndex; + int _sharedScreenIndex; + NSPoint _sharedScreenPosition; + PTKeyCombo * _keyCombo; + NSMutableDictionary * _customOptions; + TPOptionsProxy * _optionsProxy; + + TPHostCapability _capabilities; + TPHostState _state; + TPHostState _previousHostState; +} + +- (instancetype) initWithIdentifier:(NSString*)identifier address:(NSString*)address port:(int)port; + +- (void)setIdentifier:(NSString*)identifier; +- (void)setOSVersion:(SInt32)osVersion; + +/* Address */ +- (void)setAddress:(NSString*)address; +@property (nonatomic) int port; + +/* Certificate */ +@property (nonatomic) SecCertificateRef certificate; +@property (nonatomic, readonly, copy) NSData *certificateData; +@property (nonatomic, getter=isCertified, readonly) BOOL certified; + +/* Screens */ +@property (nonatomic, copy) NSArray *screens; + +@property (nonatomic, readonly, strong) NSScreen *localScreen; +@property (nonatomic) unsigned int localScreenIndex; + +- (unsigned)sharedScreenIndex; +- (void)setSharedScreenIndex:(unsigned)sharedScreenIndex; + +@property (nonatomic, readonly) NSRect hostRect; +@property (nonatomic, readonly) NSRect adjustedHostRect; +@property (nonatomic, readonly) NSRect fullHostRect; +- (void)setHostPosition:(NSPoint)position; + +@property (nonatomic, copy) PTKeyCombo *keyCombo; + +- (void)setBackgroundImage:(NSImage*)backgroundImage; + +/* Capabilities */ +- (void)setCapabilities:(TPHostCapability)capabilities; +- (void)setCapability:(TPHostCapability)capability isEnabled:(BOOL)enabled; + +/* State */ +@property (nonatomic) TPHostState hostState; +@property (nonatomic, readonly) TPHostState previousHostState; +- (BOOL)isInState:(TPHostState)hostState; + +/* Custom options */ +@property (nonatomic, readonly, strong) id options; +- (id)optionForKey:(NSString*)key; +- (void)setCustomOption:(id)option forKey:(NSString*)key; +@property (nonatomic, readonly, copy) NSArray *customizedOptions; +@property (nonatomic, readonly) BOOL hasCustomOptions; + +- (void)resetCustomOptions; +- (void)makeDefaultOptions; + +@end + +@interface NSArray (TPRemoteHostAdditions) + +- (NSArray*)hostsWithState:(TPHostState)state; + +@end diff --git a/TPRemoteHost.m b/TPRemoteHost.m new file mode 100644 index 0000000..0d330f6 --- /dev/null +++ b/TPRemoteHost.m @@ -0,0 +1,657 @@ +// +// TPRemoteHost.m +// teleport +// +// Created by JuL on Fri Feb 27 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPRemoteHost.h" +#import "TPHostSnapping.h" +#import "TPPreferencesManager.h" +#import "TPLocalHost.h" + +NSString * TPRemoteHostAddressKey = @"address"; +NSString * TPRemoteHostScreensKey = @"screens"; +NSString * TPRemoteHostSharedScreenIndexKey = @"sharedScreenIndex"; +NSString * TPRemoteHostSharedScreenPositionKey = @"sharedScreenPosition"; +NSString * TPRemoteHostLocalScreenIndexKey = @"localScreenIndex"; +NSString * TPRemoteHostCertificateKey = @"certificate"; +NSString * TPRemoteHostStateKey = @"state"; +NSString * TPRemoteHostKeyComboKey = @"keyCombo"; +NSString * TPRemoteHostCustomOptionsKey = @"customOptions"; + +@interface TPOptionsProxy : NSObject +{ + TPRemoteHost * _remoteHost; +} + +- (instancetype) initWithRemoteHost:(TPRemoteHost*)remoteHost NS_DESIGNATED_INITIALIZER; + +- (void)resetCustomOptions; + +@end + +@implementation TPOptionsProxy + +- (instancetype) initWithRemoteHost:(TPRemoteHost*)remoteHost +{ + self = [super init]; + + _remoteHost = remoteHost; + + return self; +} + +- (id)valueForKey:(NSString*)key +{ + return [_remoteHost optionForKey:key]; +} + +- (void)setValue:(id)value forKey:(NSString*)key +{ + [self willChangeValueForKey:key]; + [_remoteHost setCustomOption:value forKey:key]; + [self didChangeValueForKey:key]; +} + +- (void)resetCustomOptions +{ + NSArray * customizedOptions = [_remoteHost customizedOptions]; + NSEnumerator * optionsEnum = [customizedOptions objectEnumerator]; + NSString * key; + while((key = [optionsEnum nextObject]) != nil) { + [self willChangeValueForKey:key]; + [_remoteHost setCustomOption:nil forKey:key]; + [self didChangeValueForKey:key]; + } +} + +@end + +@implementation TPRemoteHost + +- (instancetype) init +{ + self = [super init]; + + _state = TPHostUndefState; + _previousHostState = TPHostUndefState; + _localScreenIndex = -1; + _certRef = NULL; + + return self; +} + +- (instancetype) initWithIdentifier:(NSString*)identifier address:(NSString*)address port:(int)port +{ + self = [self init]; + + _identifier = [identifier copy]; + _address = [address copy]; + _port = port; + _computerName = [identifier copy]; + + return self; +} + +- (instancetype) initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder:coder]; + + _identifier = [[coder decodeObjectForKey:TPHostIdentifierKey] copy]; + _address = [[coder decodeObjectForKey:TPRemoteHostAddressKey] copy]; + + if([coder containsValueForKey:TPRemoteHostScreensKey]) + _screens = [TPScreen screensFromString:[coder decodeObjectForKey:TPRemoteHostScreensKey]]; + else + _screens = nil; + + if([coder containsValueForKey:TPRemoteHostSharedScreenIndexKey]) + _sharedScreenIndex = [coder decodeIntForKey:TPRemoteHostSharedScreenIndexKey]; + else + _sharedScreenIndex = -1; + + if([coder containsValueForKey:TPRemoteHostSharedScreenPositionKey]) + _sharedScreenPosition = [coder decodePointForKey:TPRemoteHostSharedScreenPositionKey]; + else + _sharedScreenPosition = NSZeroPoint; + + if([coder containsValueForKey:TPRemoteHostLocalScreenIndexKey]) + _localScreenIndex = [coder decodeIntForKey:TPRemoteHostLocalScreenIndexKey]; + else + _localScreenIndex = -1; + + if([coder containsValueForKey:TPHostBackgroundImageDataKey]) { +#if LEGACY_BUILD + unsigned length = 0; +#else + NSUInteger length = 0; +#endif + const uint8_t * bytes = [coder decodeBytesForKey:TPHostBackgroundImageDataKey returnedLength:&length]; + NSData * backgroundImageData = [NSData dataWithBytesNoCopy:(void*)bytes length:length freeWhenDone:NO]; + if(backgroundImageData != nil) + _backgroundImage = [[NSImage alloc] initWithData:backgroundImageData]; + else + _backgroundImage = nil; + } + else + _backgroundImage = nil; + + if([coder containsValueForKey:TPRemoteHostCertificateKey]) { + OSErr err; + CSSM_DATA cssmCertData; +#if LEGACY_BUILD + unsigned length = 0; +#else + NSUInteger length = 0; +#endif + cssmCertData.Data = (uint8 *)[coder decodeBytesForKey:TPRemoteHostCertificateKey returnedLength:&length]; + cssmCertData.Length = length; + + if((err = SecCertificateCreateFromData(&cssmCertData, CSSM_CERT_UNKNOWN, CSSM_CERT_ENCODING_UNKNOWN, &_certRef)) != noErr) + NSLog(@"Error reading certificate for %@: %d", self, err); + } + + _capabilities = [coder decodeIntForKey:TPHostCapabilitiesKey]; + _osVersion = [coder decodeInt32ForKey:TPHostOSVersionKey]; + + if([coder containsValueForKey:TPRemoteHostStateKey]) + _state = [coder decodeIntForKey:TPRemoteHostStateKey]; + else + _state = -1; + + if([coder containsValueForKey:TPRemoteHostKeyComboKey]) { + _keyCombo = [[PTKeyCombo alloc] initWithPlistRepresentation:[coder decodeObjectForKey:TPRemoteHostKeyComboKey]]; + } + + if([coder containsValueForKey:TPRemoteHostCustomOptionsKey]) { + _customOptions = [[coder decodeObjectForKey:TPRemoteHostCustomOptionsKey] mutableCopy]; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [super encodeWithCoder:coder]; + + [coder encodeObject:_address forKey:TPRemoteHostAddressKey]; + [coder encodeObject:[TPScreen stringFromScreens:_screens] forKey:TPRemoteHostScreensKey]; + [coder encodeInt:_sharedScreenIndex forKey:TPRemoteHostSharedScreenIndexKey]; + [coder encodePoint:_sharedScreenPosition forKey:TPRemoteHostSharedScreenPositionKey]; + [coder encodeInt:_localScreenIndex forKey:TPRemoteHostLocalScreenIndexKey]; + +#if 1 + if([self hasCustomBackgroundImage]) { + NSData * backgroundImageData = [self backgroundImageData]; + if(backgroundImageData != nil) + [coder encodeBytes:[backgroundImageData bytes] length:[backgroundImageData length] forKey:TPHostBackgroundImageDataKey]; + } +#endif + + if(_certRef != NULL) { + OSErr err; + CSSM_DATA cssmCertData; + if((err = SecCertificateGetData(_certRef, &cssmCertData)) != noErr) + NSLog(@"Error writing certificate for %@: %d", self, err); + else + [coder encodeBytes:cssmCertData.Data length:cssmCertData.Length forKey:TPRemoteHostCertificateKey]; + } + + [coder encodeInt:_state forKey:TPRemoteHostStateKey]; + + if((_keyCombo != nil) && ![_keyCombo isClearCombo] && [_keyCombo isValidHotKeyCombo]) { + [coder encodeObject:[_keyCombo plistRepresentation] forKey:TPRemoteHostKeyComboKey]; + } + + if(_customOptions != nil) { + [coder encodeObject:_customOptions forKey:TPRemoteHostCustomOptionsKey]; + } +} + +- copyWithZone:(NSZone*)zone +{ + TPRemoteHost * copy = [[TPRemoteHost alloc] initWithIdentifier:[self identifier] address:_address port:_port]; + copy->_keyCombo = [_keyCombo copy]; + copy->_customOptions = [_customOptions mutableCopy]; + return copy; +} + +- (void)dealloc +{ + if(_certRef != NULL) + CFRelease(_certRef); +} + +- (NSString*)identifier +{ + return _identifier; +} + +- (void)setIdentifier:(NSString*)identifier +{ + if(identifier != _identifier) { + _identifier = identifier; + } +} + +- (SInt32)osVersion +{ + return _osVersion; +} + +- (void)setOSVersion:(SInt32)osVersion +{ + _osVersion = osVersion; +} + + +#pragma mark - +#pragma mark Address + +- (NSString*)address +{ + return _address; +} + +- (void)setAddress:(NSString*)address +{ + if(address != _address) { + _address = [address copy]; + } +} + +- (void)setPort:(int)port +{ + _port = port; + [self notifyChange]; +} + +- (int)port +{ + return _port; +} + + +#pragma mark - +#pragma mark Certificate + +- (SecCertificateRef)certificate +{ + return _certRef; +} + +- (NSData*)certificateData +{ + if(_certRef == NULL) + return nil; + else { + OSErr err; + CSSM_DATA cssmCertData; + if((err = SecCertificateGetData(_certRef, &cssmCertData)) != noErr) { + NSLog(@"Error writing certificate for %@: %d", self, err); + return nil; + } + else + return [NSData dataWithBytesNoCopy:cssmCertData.Data length:cssmCertData.Length freeWhenDone:NO]; + } +} + +- (void)setCertificate:(SecCertificateRef)certRef +{ + if(_certRef != NULL) + CFRelease(_certRef); + _certRef = certRef; + if(_certRef != NULL) + CFRetain(_certRef); +} + +- (BOOL)isCertified +{ + return (_certRef != NULL); +} + + +#pragma mark - +#pragma mark Screen + +- (NSArray*)screens +{ + return _screens; +} + +- (void)setScreens:(NSArray*)screens +{ + if(screens != _screens) { + _screens = screens; + } +} + + +//- (NSSize)screenSize +//{ +// return _hostRect.size; +//} +// +//- (void)setScreenSize:(NSSize)screenSize +//{ +// if(!NSEqualSizes(screenSize, _hostRect.size)) { +//// NSRect localScreenRect = [[self localScreen] frame]; +//// float deltaX = 0.0; +//// float deltaY = 0.0; +//// +//// if(_hostRect.origin.x == (localScreenRect.origin.x + localScreenRect.size.width)) { // on the right +//// //deltaX = 0.0;//(_hostRect.size.width - screenSize.width)/2.0; +//// } +//// else if(_hostRect.origin.x == (localScreenRect.origin.x - _hostRect.size.width)) { // on the left +//// deltaX = (_hostRect.size.width - screenSize.width); +//// } +//// else if(_hostRect.origin.y == (localScreenRect.origin.y + localScreenRect.size.height)) { // on the top +//// //deltaX = 0.0;(_hostRect.size.width - screenSize.width)/2.0; +//// } +//// else if(_hostRect.origin.y == (localScreenRect.origin.y - _hostRect.size.height)) { // on the bottom +//// deltaY = (_hostRect.size.height - screenSize.height); +//// } +// +// _hostRect.size = screenSize; +// +// [self notifyChange]; +// } +//} + +- (NSScreen*)localScreen +{ + return [[TPLocalHost localHost] screenAtIndex:_localScreenIndex]; +} + +- (unsigned)localScreenIndex +{ + return _localScreenIndex; +} + +- (void)setLocalScreenIndex:(unsigned)inLocalScreenIndex +{ + if(_localScreenIndex != inLocalScreenIndex) { + _localScreenIndex = inLocalScreenIndex; + [self notifyChange]; + } +} + +- (unsigned)sharedScreenIndex +{ + return _sharedScreenIndex; +} + +- (void)setSharedScreenIndex:(unsigned)sharedScreenIndex +{ + _sharedScreenIndex = sharedScreenIndex; +} + +- (NSRect)hostRect +{ + NSArray * screens = [self screens]; + if([screens count] > 0) { + NSRect hostRect; + NSScreen * screen = [self screens][_sharedScreenIndex]; + hostRect = [screen frame]; + hostRect.origin = _sharedScreenPosition; + return hostRect; + } + else { + return NSZeroRect; + } +} + +- (NSRect)fullHostRect +{ + NSRect fullHostRect = NSZeroRect; + NSArray * screens = [self screens]; + NSEnumerator * screenEnum = [screens objectEnumerator]; + NSScreen * screen; + while((screen = [screenEnum nextObject]) != nil) { + NSRect screenRect = [screen frame]; + fullHostRect = NSUnionRect(fullHostRect, screenRect); + } + return fullHostRect; +} + +- (void)setHostPosition:(NSPoint)position +{ + _sharedScreenPosition = position; + [self notifyChange]; +} + +- (NSRect)adjustedHostRect +{ + NSRect hostRect = [self hostRect]; + NSRect localRect = [[self localScreen] frame]; + NSRect adjustedHostRect; + TPGluedRect(&adjustedHostRect, NULL, localRect, hostRect, TPUndefSide); + return adjustedHostRect; +} + +- (PTKeyCombo*)keyCombo +{ + return _keyCombo; +} + +- (void)setKeyCombo:(PTKeyCombo*)keyCombo +{ + + if(keyCombo == nil) { + _keyCombo = nil; + } + else { + _keyCombo = [[PTKeyCombo alloc] initWithPlistRepresentation:[keyCombo plistRepresentation]]; + } + + [self notifyChange]; +} + + +#pragma mark - +#pragma mark Background image + +- (void)setBackgroundImage:(NSImage*)backgroundImage +{ + if(backgroundImage != _backgroundImage) { + _backgroundImage = backgroundImage; + + [self notifyChange]; + } +} + +- (NSImage*)backgroundImage +{ + if(_backgroundImage != nil) + return _backgroundImage; + else + return [self defaultBackgroundImage]; +} + +- (BOOL)hasCustomBackgroundImage +{ + return (_backgroundImage != nil); +} + + +#pragma mark - +#pragma mark Capabilities + +- (TPHostCapability)capabilities +{ + return _capabilities; +} + +- (BOOL)hasCapability:(TPHostCapability)capability +{ + return ((_capabilities & capability) != 0); +} + +- (void)setCapabilities:(TPHostCapability)capabilities +{ + if(capabilities != _capabilities) { + _capabilities = capabilities; + [self notifyChange]; + } +} + +- (void)setCapability:(TPHostCapability)capability isEnabled:(BOOL)enabled +{ + if(enabled) + _capabilities |= capability; + else + _capabilities &= ~capability; + + [self notifyChange]; +} + + +#pragma mark - +#pragma mark State + +- (TPHostState)hostState +{ + return _state; +} + +- (TPHostState)previousHostState +{ + return _previousHostState; +} + +- (BOOL)isInState:(TPHostState)hostState +{ + return ((_state & hostState) != 0); +} + +- (void)setHostState:(TPHostState)state +{ +// DebugLog(@"changing state of host %p (%@) from %d to %d", self, _identifier, _state, state); + if(state != _state) { + _previousHostState = _state; + _state = state; + [self notifyChange]; + } +} + + +#pragma mark - +#pragma mark Custom options + +- (id)options +{ + if(_optionsProxy == nil) { + _optionsProxy = [[TPOptionsProxy alloc] initWithRemoteHost:self]; + } + + return _optionsProxy; +} + +- (id)optionForKey:(NSString*)key +{ + id option = (_customOptions == nil) ? nil : _customOptions[key]; + if(option == nil) { + option = [[TPPreferencesManager sharedPreferencesManager] valueForPref:key]; + } + return option; +} + +- (void)setCustomOption:(id)option forKey:(NSString*)key +{ + [self willChangeValueForKey:@"hasCustomOptions"]; + + if(_customOptions == nil && option != nil) { + _customOptions = [[NSMutableDictionary alloc] init]; + } + + if(_customOptions != nil) { + if(option == nil) { + [_customOptions removeObjectForKey:key]; + } + else { + if([option isEqual:[[TPPreferencesManager sharedPreferencesManager] valueForPref:key]]) { + [_customOptions removeObjectForKey:key]; + } + else { + _customOptions[key] = option; + } + } + + if([_customOptions count] == 0) { + _customOptions = nil; + } + } + + [self didChangeValueForKey:@"hasCustomOptions"]; +} + +- (NSArray*)customizedOptions +{ + return (_customOptions == nil) ? @[] : [_customOptions allKeys]; +} + +- (BOOL)hasCustomOptions +{ + return (_customOptions != nil); +} + +- (void)resetCustomOptions +{ + [self willChangeValueForKey:@"hasCustomOptions"]; + + [[self options] resetCustomOptions]; + + _customOptions = nil; + + [self didChangeValueForKey:@"hasCustomOptions"]; +} + +- (void)makeDefaultOptions +{ + [self willChangeValueForKey:@"hasCustomOptions"]; + + NSEnumerator * optionsEnum = [_customOptions keyEnumerator]; + NSString * key; + while((key = [optionsEnum nextObject]) != nil) { + id value = [self optionForKey:key]; + [[TPPreferencesManager sharedPreferencesManager] setValue:value forKey:key]; + } + + _customOptions = nil; + + [self didChangeValueForKey:@"hasCustomOptions"]; +} + + +#pragma mark - +#pragma mark Misc + +- (NSString*)description +{ + return [NSString stringWithFormat:@"host %p id=%@ state=%lu address=%@ rect=%@", self, [self identifier], _state, [self address], NSStringFromRect([self hostRect])]; +} + + +@end + +@implementation NSArray (TPRemoteHostAdditions) + +- (NSArray*)hostsWithState:(TPHostState)state +{ + NSMutableArray * hosts = [[NSMutableArray alloc] init]; + NSEnumerator * hostEnum = [self objectEnumerator]; + TPRemoteHost * host; + + while((host = [hostEnum nextObject]) != nil) { + if(![host isKindOfClass:[TPRemoteHost class]]) + continue; + if(([host hostState] & state) != 0) + [hosts addObject:host]; + } + + return hosts; +} + +@end diff --git a/TPRemoteOperationsController.h b/TPRemoteOperationsController.h new file mode 100644 index 0000000..5a15bf2 --- /dev/null +++ b/TPRemoteOperationsController.h @@ -0,0 +1,31 @@ +// +// TPRemoteOperationsController.h +// Teleport +// +// Created by JuL on Wed Dec 03 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +#import "TPEventsController.h" +#import "TPMessage.h" + +#define BUTTON_COUNT 8 + +@protocol TPEventDelegate + +- (BOOL)applicationWillSendEvent:(NSEvent*)event; + +@end + +@class TPEventCatcherWindow; + +@interface TPRemoteOperationsController : TPEventsController +{ + TPEventCatcherWindow * _eventCatcherWindow; + NSMutableSet * _modifierStates; // stores modifiers that are currently down + BOOL _buttonStates[BUTTON_COUNT]; +} + +@end diff --git a/TPRemoteOperationsController.m b/TPRemoteOperationsController.m new file mode 100644 index 0000000..c3b49a0 --- /dev/null +++ b/TPRemoteOperationsController.m @@ -0,0 +1,466 @@ +// +// TPRemoteOperationsController.mm +// Teleport +// +// Created by JuL on Wed Dec 03 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPRemoteOperationsController.h" +#import "TPMainController.h" +#import "TPUtils.h" + +typedef int CGSConnection; +typedef NS_ENUM(NSInteger, CGSGlobalHotKeyOperatingMode) { + CGSGlobalHotKeyEnable = 0, + CGSGlobalHotKeyDisable = 1, +} ; +extern CGSConnection _CGSDefaultConnection(void); +extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode); + +static TPRemoteOperationsController * _remoteOperationsController = nil; + + +@interface TPEventCatcherWindow : NSWindow +{ + id _eventsDelegate; +} + +- (void)setEventDelegate:(id)eventDelegate; + +@end + + +@implementation TPEventCatcherWindow + +#if LEGACY_BUILD +- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag +#else +- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag +#endif +{ + self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + [self setBackgroundColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.01]]; + [self setAlphaValue:1.0]; + [self setOpaque:NO]; + [self setHasShadow:NO]; + [self setLevel:kCGCursorWindowLevel-1]; + [self setIgnoresMouseEvents:NO]; + [self setAcceptsMouseMovedEvents:YES]; + [self setReleasedWhenClosed:YES]; + + return self; +} + +- (void)setEventDelegate:(id)eventDelegate +{ + _eventsDelegate = eventDelegate; +} + +- (void)sendEvent:(NSEvent *)event +{ + BOOL sendToSuper = YES; + + DebugLog(@"sendEvent: %@ (%d)", event, (int)[event type]); + + if(_eventsDelegate != nil && [_eventsDelegate respondsToSelector:@selector(applicationWillSendEvent:)]) { + if(![_eventsDelegate applicationWillSendEvent:event]) + sendToSuper = NO; + } + + if(sendToSuper) + [super sendEvent:event]; +} + +@end + +@interface TPRemoteOperationsController (Internal) + +/* Getting events */ +- (void)_sendEventToListener:(NSEvent*)event; +- (NSData*)_eventDataFromEvent:(NSEvent*)event; +- (TPKey)_getKeyFromEvent:(NSEvent*)event; + +/* Posting events */ +- (NSPoint)_mousePositionFromCurrentPosition:(NSPoint)currentMousePosition andMouseDelta:(TPMouseDelta)mouseDelta; + +@end + +@implementation TPRemoteOperationsController + ++ (TPEventsController*)defaultController +{ + if(_remoteOperationsController == nil) + _remoteOperationsController = [[TPRemoteOperationsController alloc] init]; + return _remoteOperationsController; +} + +- (instancetype) init +{ + self = [super init]; + + _modifierStates = [[NSMutableSet alloc] init]; + + return self; +} + + + +#pragma mark - +#pragma mark Getting events + +- (void)_sendModifierEventWithKeyCode:(unsigned short)keyCode +{ + NSEvent * modifierEvent = [NSEvent keyEventWithType:NSFlagsChanged location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:0 context:NULL characters:NULL charactersIgnoringModifiers:NULL isARepeat:NO keyCode:keyCode]; + [self _sendEventToListener:modifierEvent]; +} + +- (void)_startGettingEventsOnScreen:(NSScreen*)screen +{ + [(TPMainController*)[NSApp delegate] goFrontmost]; + + CGDisplayHideCursor(CGMainDisplayID()); + + NSRect frame = [screen frame]; + NSPoint centerPoint = NSMakePoint(NSMidX(frame), NSMidY(frame)); + [self warpMouseToPosition:centerPoint]; + + CGAssociateMouseAndMouseCursorPosition(FALSE); + + CGSConnection conn = _CGSDefaultConnection(); + CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyDisable); + + _eventCatcherWindow = [[TPEventCatcherWindow alloc] initWithContentRect:frame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + [_eventCatcherWindow setEventDelegate:self]; + [_eventCatcherWindow makeKeyAndOrderFront:self]; + [NSApp setEventDelegate:self]; + + /* Send events for currently down modifiers */ + NSEventModifierFlags modifiers = [NSEvent modifierFlags]; + if((modifiers & NSCommandKeyMask) != 0) + [self _sendModifierEventWithKeyCode:55]; + if((modifiers & NSShiftKeyMask) != 0) + [self _sendModifierEventWithKeyCode:56]; + if((modifiers & NSAlphaShiftKeyMask) != 0) + [self _sendModifierEventWithKeyCode:57]; + if((modifiers & NSAlternateKeyMask) != 0) + [self _sendModifierEventWithKeyCode:58]; + if((modifiers & NSControlKeyMask) != 0) + [self _sendModifierEventWithKeyCode:59]; +} + +- (void)_stopGettingEvents +{ + [NSApp setEventDelegate:nil]; + [_eventCatcherWindow close]; + _eventCatcherWindow = nil; + + [_modifierStates removeAllObjects]; + + CGSConnection conn = _CGSDefaultConnection(); + CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyEnable); + + [(TPMainController*)[NSApp delegate] leaveFrontmost]; + + CGAssociateMouseAndMouseCursorPosition(TRUE); +} + +- (void)cleanupGettingEvents +{ + CGDisplayShowCursor(CGMainDisplayID()); +} + +- (BOOL)applicationWillSendEvent:(NSEvent*)event +{ + switch([event type]) { + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSRightMouseDown: + case NSRightMouseUp: + case NSOtherMouseDown: + case NSOtherMouseUp: + case NSMouseMoved: + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + case NSFlagsChanged: + case NSScrollWheel: + case NSKeyUp: + case NSKeyDown: + [self _sendEventToListener:event]; + return NO; + case NSSystemDefined: + case NSMouseEntered: + case NSMouseExited: + case NSAppKitDefined: + case NSApplicationDefined: + case NSPeriodic: + case NSCursorUpdate: + default: + return YES; + } +} + +- (BOOL)_shouldSkipFirstEvent:(id)e +{ + NSEvent * event = (NSEvent*)e; + switch([event type]) { + case NSMouseMoved: + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + return YES; + default: + return NO; + } +} + +- (BOOL)_isEmergencyStopEvent:(id)e +{ + NSEvent * event = (NSEvent*)e; + + if([event type] != NSKeyDown) + return NO; + if([event keyCode] != ESC_KEYCODE) + return NO; + if(([event modifierFlags] & NSShiftKeyMask) == 0) + return NO; + if(([event modifierFlags] & NSControlKeyMask) == 0) + return NO; + if(([event modifierFlags] & NSAlternateKeyMask) == 0) + return NO; + return YES; +} + +- (NSData*)_eventDataFromEvent:(NSEvent*)event +{ + NSMutableData * eventData = [[NSMutableData alloc] init]; + + NSEventType eventType = [event type]; + int swappedEventType = NSSwapHostIntToBig(eventType); + + DebugLog(@"eventDataFromEventType: %d", (int)eventType); + + /* Write event type */ + [eventData appendData:[NSData dataWithBytes:&swappedEventType length:sizeof(int)]]; + + /* Write event data */ + switch(eventType) { + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSRightMouseDown: + case NSRightMouseUp: + case NSOtherMouseDown: + case NSOtherMouseUp: + { + int buttonNumber = [event buttonNumber]; + int swappedButtonNumber = NSSwapHostIntToBig(buttonNumber); + [eventData appendData:[NSData dataWithBytes:&swappedButtonNumber length:sizeof(int)]]; + break; + } + case NSMouseMoved: + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + case NSScrollWheel: + { + int64_t deltaX = llroundf([event deltaX]); + int64_t deltaY = llroundf([event deltaY]); + int64_t swappedDeltaX = NSSwapHostLongLongToBig(deltaX); + int64_t swappedDeltaY = NSSwapHostLongLongToBig(deltaY); + [eventData appendData:[NSData dataWithBytes:&swappedDeltaX length:sizeof(int64_t)]]; + [eventData appendData:[NSData dataWithBytes:&swappedDeltaY length:sizeof(int64_t)]]; + break; + } + case NSKeyUp: + case NSKeyDown: + case NSFlagsChanged: + { + TPKey key = [self _getKeyFromEvent:event]; + short short1 = NSSwapHostShortToBig(key.charCode); + short short2 = NSSwapHostShortToBig(key.keyCode); + [eventData appendData:[NSData dataWithBytes:&short1 length:sizeof(short)]]; + [eventData appendData:[NSData dataWithBytes:&short2 length:sizeof(short)]]; + break; + } + default: + break; + } + + return eventData; +} + +- (TPKey)_getKeyFromEvent:(NSEvent*)event +{ + TPKey key; + key.keyCode = [event keyCode]; + key.charCode = 0; + + if([event type] == NSKeyDown || [event type] == NSKeyUp) { + NSString * characters = [event charactersIgnoringModifiers]; + + if(characters != nil && [characters length] > 0) + key.charCode = [characters characterAtIndex:0]; + } + + return key; +} + + +#pragma mark - +#pragma mark Posting events + +- (void)startPostingEvents +{ + [_modifierStates removeAllObjects]; +} + +- (void)stopPostingEvents +{ + NSEnumerator * modifiersEnum = [_modifierStates objectEnumerator]; + NSNumber * modifierNum; + while((modifierNum = [modifiersEnum nextObject]) != nil) + CGPostKeyboardEvent(0, [modifierNum unsignedShortValue], NO); + + [_modifierStates removeAllObjects]; +} + +- (TPKey)_keyFromEventData:(NSData*)eventData pos:(int*)pos +{ + short swappedShort1; + short swappedShort2; + + [eventData _readBytes:&swappedShort1 withSize:sizeof(short) atPos:pos]; + [eventData _readBytes:&swappedShort2 withSize:sizeof(short) atPos:pos]; + + TPKey key; + key.charCode = NSSwapBigShortToHost(swappedShort1); + key.keyCode = NSSwapBigShortToHost(swappedShort2); + + return key; +} + +- (void)_postMouseEvent +{ + CGPostMouseEvent(_currentMouseLocation, TRUE, BUTTON_COUNT, _buttonStates[0], _buttonStates[1], _buttonStates[2], _buttonStates[3], _buttonStates[4], _buttonStates[5], _buttonStates[6], _buttonStates[7]); +} + +- (void)_postEventWithEventData:(NSData*)eventData +{ + int pos = 0; + + int swappedEventType; + [eventData _readBytes:&swappedEventType withSize:sizeof(int) atPos:&pos]; + NSEventType eventType = NSSwapBigIntToHost(swappedEventType); + + switch(eventType) { + case NSLeftMouseDown: + case NSRightMouseDown: + case NSOtherMouseDown: + { + int swappedButtonNumber, buttonNumber; + [eventData _readBytes:&swappedButtonNumber withSize:sizeof(int) atPos:&pos]; + buttonNumber = MIN(NSSwapBigIntToHost(swappedButtonNumber), BUTTON_COUNT-1); + _buttonStates[buttonNumber] = TRUE; + [self _postMouseEvent]; + break; + } + case NSLeftMouseUp: + case NSRightMouseUp: + case NSOtherMouseUp: + { + int swappedButtonNumber, buttonNumber; + [eventData _readBytes:&swappedButtonNumber withSize:sizeof(int) atPos:&pos]; + buttonNumber = MIN(NSSwapBigIntToHost(swappedButtonNumber), BUTTON_COUNT-1); + _buttonStates[buttonNumber] = FALSE; + [self _postMouseEvent]; + break; + } + case NSMouseMoved: + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + { + int64_t swappedDeltaX, swappedDeltaY; + + [eventData _readBytes:&swappedDeltaX withSize:sizeof(int64_t) atPos:&pos]; + [eventData _readBytes:&swappedDeltaY withSize:sizeof(int64_t) atPos:&pos]; + + TPMouseDelta mouseDelta; + mouseDelta.x = NSSwapBigLongLongToHost(swappedDeltaX); + mouseDelta.y = NSSwapBigLongLongToHost(swappedDeltaY); + + [self _updateMouseLocationWithMouseDelta:mouseDelta]; + [self _postMouseEvent]; + break; + } + case NSFlagsChanged: + { + TPKey key = [self _keyFromEventData:eventData pos:&pos]; + NSNumber * keyNum = @(key.keyCode); + + if([_modifierStates containsObject:keyNum]) { + [_modifierStates removeObject:keyNum]; + CGPostKeyboardEvent(0, key.keyCode, NO); + } + else { + [_modifierStates addObject:keyNum]; + CGPostKeyboardEvent(0, key.keyCode, YES); + } + break; + } + case NSScrollWheel: + { + int swappedDeltaX; + int swappedDeltaY; + + [eventData _readBytes:&swappedDeltaX withSize:sizeof(int) atPos:&pos]; + [eventData _readBytes:&swappedDeltaY withSize:sizeof(int) atPos:&pos]; + + int deltaX = NSSwapBigIntToHost(swappedDeltaX); + int deltaY = NSSwapBigIntToHost(swappedDeltaY); + + CGPostScrollWheelEvent(2, deltaY, deltaX); + break; + } + case NSKeyDown: + { + TPKey key = [self _keyFromEventData:eventData pos:&pos]; + CGPostKeyboardEvent(key.charCode, key.keyCode, YES); + break; + } + case NSKeyUp: + { + TPKey key = [self _keyFromEventData:eventData pos:&pos]; + CGPostKeyboardEvent(key.charCode, key.keyCode, NO); + break; + } + default: + break; + } +} + +- (void)warpMouseToPosition:(NSPoint)position +{ + [super warpMouseToPosition:position]; + + CGWarpMouseCursorPosition(_currentMouseLocation); +} + +- (void)mouseDownAtPosition:(NSPoint)position +{ + [super mouseDownAtPosition:position]; + + _buttonStates[0] = TRUE; + [self _postMouseEvent]; +} + +- (void)mouseUpAtPosition:(NSPoint)position +{ + [super mouseUpAtPosition:position]; + + _buttonStates[0] = FALSE; + [self _postMouseEvent]; +} + +@end diff --git a/TPScreen.h b/TPScreen.h new file mode 100644 index 0000000..41c5b61 --- /dev/null +++ b/TPScreen.h @@ -0,0 +1,21 @@ +// +// TPScreen.h +// teleport +// +// Created by Julien Robert on 17/06/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import + +@interface TPScreen : NSScreen +{ + NSRect _tpFrame; +} + +- (void)setFrame:(NSRect)frame; + ++ (NSString*)stringFromScreens:(NSArray*)screens; ++ (NSArray*)screensFromString:(NSString*)screensString; + +@end diff --git a/TPScreen.m b/TPScreen.m new file mode 100644 index 0000000..ef53501 --- /dev/null +++ b/TPScreen.m @@ -0,0 +1,73 @@ +// +// TPScreen.m +// teleport +// +// Created by Julien Robert on 17/06/09. +// Copyright 2009 Apple. All rights reserved. +// + +#import "TPScreen.h" + +@implementation TPScreen + +- (BOOL)isEqual:(NSScreen*)screen +{ + if(screen == nil || ![screen isKindOfClass:[NSScreen class]]) { + return NO; + } + else { + return NSEqualRects([self frame], [screen frame]); + } +} + +- (NSString*)description +{ + return [NSString stringWithFormat:@"Screen %@", NSStringFromRect([self frame])]; +} + +- (void)setFrame:(NSRect)frame +{ + _tpFrame = frame; +} + +- (NSRect)frame +{ + return _tpFrame; +} + ++ (NSString*)stringFromScreens:(NSArray*)screens +{ + NSMutableString * screenSizesString = [[NSMutableString alloc] init]; + NSEnumerator * screenEnum = [screens objectEnumerator]; + NSScreen * screen; + + while((screen = [screenEnum nextObject]) != nil) { + NSString * screenString = NSStringFromRect([screen frame]); + if(screen != [screens lastObject]) { + [screenSizesString appendFormat:@"%@;", screenString]; + } + else { + [screenSizesString appendFormat:@"%@", screenString]; + } + } + + return screenSizesString; +} + ++ (NSArray*)screensFromString:(NSString*)screensString +{ + NSMutableArray * screens = [[NSMutableArray alloc] init]; + NSArray * screenStrings = [screensString componentsSeparatedByString:@";"]; + NSEnumerator * screenStringsEnum = [screenStrings objectEnumerator]; + NSString * screenString; + + while((screenString = [screenStringsEnum nextObject]) != nil) { + TPScreen * screen = [[TPScreen alloc] init]; + [screen setFrame:NSRectFromString(screenString)]; + [screens addObject:screen]; + } + + return screens; +} + +@end diff --git a/TPServerController.h b/TPServerController.h new file mode 100644 index 0000000..e39b4a5 --- /dev/null +++ b/TPServerController.h @@ -0,0 +1,46 @@ +// +// TPServerController.h +// Teleport +// +// Created by JuL on Thu Dec 04 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +#import "TPConnectionController.h" + +typedef NS_ENUM(NSInteger, TPServerState) { + TPServerIdleState, + TPServerSharedState, + TPServerControlledState +} ; + +@class TPRemoteHost, TPHotBorder, TPMessage; + +@interface TPServerController : TPConnectionController +{ + TPServerState _state; + TPHotBorder * _clientHotBorder; + NSDictionary * _switchOptions; +} + ++ (TPServerController*)defaultController; + +- (void)startSharing; +- (void)stopSharing; + +- (void)requestedStartControlByHost:(TPRemoteHost*)host onConnection:(TPNetworkConnection*)connection withInfoDict:(NSDictionary*)infoDict; +- (void)startControlWithInfoDict:(NSDictionary*)infoDict; + +- (void)requestStopControlAtLocation:(NSPoint)location withDraggingInfo:(id)draggingInfo; +- (void)stopControlWithDisconnect:(BOOL)disconnect; + +- (void)setSwitchOptions:(NSDictionary*)switchOptions; + +@property (nonatomic) BOOL allowControl; +@property (nonatomic, getter=isControlled, readonly) BOOL controlled; + +//- (void)sendPong; + +@end diff --git a/TPServerController.m b/TPServerController.m new file mode 100644 index 0000000..683e577 --- /dev/null +++ b/TPServerController.m @@ -0,0 +1,390 @@ +// +// TPServerController.m +// Teleport +// +// Created by JuL on Thu Dec 04 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPServerController.h" +#import "TPMainController.h" +#import "TPClientController.h" +#import "TPAuthenticationManager.h" +#import "TPEventsController.h" +#import "TPNetworkConnection.h" +#import "TPTransfersManager.h" +#import "TPHotBorder.h" +#import "TPMessage.h" +#import "TPLocalHost.h" +#import "TPRemoteHost.h" +#import "TPStatusItemController.h" +#import "TPPreferencesManager.h" +#import "TPConnectionsManager.h" +#import "TPTCPSecureSocket.h" +#import "TPHostsManager.h" + +#import "TPBonjourController.h" +#import "TPPasteboardTransfer.h" +#import "TPBackgroundImageTransfer.h" + +static TPServerController * _defaultServerController = nil; + +@implementation TPServerController + ++ (TPServerController*)defaultController +{ + if(_defaultServerController == nil) + _defaultServerController = [[TPServerController alloc] init]; + + return _defaultServerController; +} + +- (instancetype) init +{ + self = [super init]; + + _state = TPServerIdleState; + + [self bind:@"allowControl" toPref:ALLOW_CONTROL]; + + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(connectionDisconnected:) name:NSWorkspaceSessionDidResignActiveNotification object:nil]; + + return self; +} + +- (void)dealloc +{ + if(_state == TPServerSharedState) + [self stopSharing]; + else if(_state == TPServerControlledState) + [self stopControl]; + + +} + + +#pragma mark - +#pragma mark Sharing + +- (void)startSharing +{ +#if DEBUG_GENERAL + DebugLog(@"server: startWaiting"); +#endif + int port = [[TPPreferencesManager sharedPreferencesManager] portForPref:COMMAND_PORT]; + + if([[TPConnectionsManager manager] startListeningWithDelegate:self onPort:&port]) { + DebugLog(@"listening on port %d", port); + [[TPBonjourController defaultController] publishWithPort:port]; + } +} + +- (void)stopSharing +{ +#if DEBUG_GENERAL + DebugLog(@"server: stopWaiting"); +#endif + + [self stopControl]; + [[TPBonjourController defaultController] unpublish]; + [[TPConnectionsManager manager] stopListening]; +} + + +#pragma mark - +#pragma mark Hot border + +- (TPHotBorder*)currentHotBorder +{ + return _clientHotBorder; +} + +- (void)setupHotBorder:(TPHotBorder*)hotBorder forHost:(TPRemoteHost*)host +{ + [hotBorder setDoubleTap:[[self optionForRemoteHost:host key:SWITCH_WITH_DOUBLE_TAP] boolValue]]; + [hotBorder setAcceptDrags:[[self optionForRemoteHost:host key:COPY_FILES] boolValue]]; +} + +- (float)hotBorderSwitchDelay:(TPHotBorder*)hotBorder +{ + if(![[self optionForRemoteHost:nil key:DELAYED_SWITCH] boolValue]) { + return 0.0; + } + else { + return [[self optionForRemoteHost:nil key:SWITCH_DELAY] floatValue]; + } +} + +- (BOOL)hotBorder:(TPHotBorder*)hotBorder canFireWithEvent:(NSEvent*)event +{ + NSAssert(hotBorder == _clientHotBorder, @"Hot border should be the client hot border"); + BOOL requireKey = [[self optionForRemoteHost:nil key:REQUIRE_KEY] boolValue]; + int keyTag = [[self optionForRemoteHost:nil key:SWITCH_KEY_TAG] intValue]; + return [[self eventsController] event:event hasRequiredKeyIfNeeded:requireKey withTag:keyTag]; +} + +- (BOOL)hotBorder:(TPHotBorder*)hotBorder firedAtLocation:(NSPoint)location withDraggingInfo:(id )draggingInfo +{ + //DebugLog(@"loc=%d", location); + [self requestStopControlAtLocation:location withDraggingInfo:draggingInfo]; + + return [super hotBorder:hotBorder firedAtLocation:location withDraggingInfo:draggingInfo]; +} + + +#pragma mark - +#pragma mark Switch options + +- (void)setSwitchOptions:(NSDictionary*)switchOptions +{ + if(switchOptions != _switchOptions) { + _switchOptions = switchOptions; + } +} + +- (id)optionForRemoteHost:(TPRemoteHost*)remoteHost key:(NSString*)key +{ + id option = (_switchOptions == nil) ? nil : _switchOptions[key]; + if(option == nil) { + option = [[TPPreferencesManager sharedPreferencesManager] valueForPref:key]; + } + return option; +} + + +#pragma mark - +#pragma mark Start control + +- (void)requestedStartControlByHost:(TPRemoteHost*)host onConnection:(TPNetworkConnection*)connection withInfoDict:(NSDictionary*)infoDict +{ +#if DEBUG_GENERAL + DebugLog(@"server: startControl"); +#endif + + BOOL connectionIsCurrent = (connection == [self currentConnection]); + + /* Reject if host is not controllable */ + if(![(TPMainController*)[NSApp delegate] canBeControlledByHostWithIdentifier:[host identifier]]) { + [connection sendMessage:[TPMessage messageWithType:TPControlFailureMsgType + andInfoDict:@{@"reason": NSLocalizedString(@"Host not controllable.", @"Reason for control failure")}]]; + if(connectionIsCurrent) + [self setCurrentConnection:nil]; + else + ; + DebugLog(@"Rejecting control: host not controllable"); + return; + } + + /* Reject if client not trusted */ + if(![[TPAuthenticationManager defaultManager] isHostTrusted:host]) { + [connection sendMessage:[TPMessage messageWithType:TPControlFailureMsgType + andInfoDict:@{@"reason": NSLocalizedString(@"Host not trusted.", @"Reason for control failure")}]]; + if(connectionIsCurrent) + [self setCurrentConnection:nil]; + else + ; + DebugLog(@"Rejecting control: remote host not trusted"); + return; + } + + /* Accept control */ + [connection sendMessage:[TPMessage messageWithType:TPControlSuccessMsgType]]; + + if(!connectionIsCurrent) { + [self setCurrentConnection:connection]; + } + + [self updateEventsController]; + + [self startControlWithInfoDict:infoDict]; +} + +- (void)startControlWithInfoDict:(NSDictionary*)infoDict +{ +#if DEBUG_GENERAL + DebugLog(@"server: startPlaying"); +#endif + + _state = TPServerControlledState; + + TPRemoteHost * host = [[self currentConnection] connectedHost]; + + /* Update status menu */ + [[TPStatusItemController defaultController] updateWithStatus:TPStatusControlled host:host]; + + /* Remove hot borders for controlling */ + [[TPClientController defaultController] updateTriggersAndShowVisualHint:NO]; + + /* Setup the hotborder for the return */ + NSRect screenPlacement = [infoDict[TPScreenPlacementKey] rectValue]; + int sharedScreenIndex = [infoDict[TPScreenIndexKey] intValue]; + NSRect sharedScreenFrame = [[[TPLocalHost localHost] screenAtIndex:sharedScreenIndex] frame]; + screenPlacement.origin.x += NSMinX(sharedScreenFrame); + screenPlacement.origin.y += NSMinY(sharedScreenFrame); + + _clientHotBorder = [[TPHotBorder alloc] initWithRepresentingRect:screenPlacement inRect:sharedScreenFrame]; + [self setupHotBorder:_clientHotBorder forHost:host]; + [_clientHotBorder setDelegate:self]; + [_clientHotBorder delayedActivate]; + + [[self eventsController] startPostingEvents]; + + /* Move mouse */ + BOOL shouldWarp = NO; + NSPoint mousePosition = [infoDict[TPMousePositionKey] pointValue]; + if(mousePosition.x >= 0.0 && mousePosition.y >= 0.0) { + shouldWarp = YES; + DebugLog(@"mousePosition=%@", NSStringFromPoint(mousePosition)); + mousePosition = [_clientHotBorder screenPointFromLocalPoint:mousePosition flipped:YES]; + } + + if([_clientHotBorder state] != TPHotBorderInactiveState) { + [_clientHotBorder deactivate]; + if(shouldWarp) { + [[self eventsController] warpMouseToPosition:mousePosition]; + } + [_clientHotBorder delayedActivate]; + } + else if(shouldWarp) { + [[self eventsController] warpMouseToPosition:mousePosition]; + } + + /* Get switch options */ + [self setSwitchOptions:infoDict[TPSwitchOptionsKey]]; + + /* Send the background image (low priority transfer) */ + if([[TPLocalHost localHost] hasCustomBackgroundImage]) + [[TPTransfersManager manager] beginTransfer:[TPOutgoingBackgroundImageTransfer transfer] usingConnection:[self currentConnection]]; + + /* Wake up display */ + [[TPLocalHost localHost] wakeUpScreen]; + + io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault, + "IOService:/IOResources/IODisplayWrangler"); + if (entry != MACH_PORT_NULL) { + IORegistryEntrySetCFProperty(entry, CFSTR("IORequestIdle"), kCFBooleanFalse); + IOObjectRelease(entry); + } + +// [eventsPlayer releaseAllKeys]; + + DebugLog(@"hotBorderRect=%@", NSStringFromRect([_clientHotBorder hotRect])); +} + + +#pragma mark - +#pragma mark Stop control + +- (void)requestStopControlAtLocation:(NSPoint)location withDraggingInfo:(id)draggingInfo +{ + NSMutableDictionary * infoDict = [NSMutableDictionary dictionaryWithObject:[NSData dataWithPoint:location] forKey:TPMousePositionKey]; + [self addDraggingInfo:draggingInfo toInfoDict:infoDict]; + [self beginTransfersWithInfoDict:infoDict]; + + TPMessage * message = [TPMessage messageWithType:TPControlStopMsgType + andInfoDict:infoDict]; + + [[self currentConnection] sendMessage:message]; + + /* Do the fake mouse up */ + if(draggingInfo != nil) + [[TPEventsController defaultController] mouseUpAtPosition:[NSEvent mouseLocation]]; +} + +- (void)stopControlWithDisconnect:(BOOL)disconnect +{ +#if DEBUG_GENERAL + DebugLog(@"server: stopPlaying"); +#endif + + [super stopControlWithDisconnect:disconnect]; + + if(_state == TPServerControlledState) { + _state = TPServerSharedState; + + if([[TPPreferencesManager sharedPreferencesManager] boolForPref:WRAP_ON_STOP_CONTROL]) + [[self eventsController] warpMouseToPosition:NSMakePoint(16.0, 16.0)]; + + [[self eventsController] stopPostingEvents]; + + [self takeDownHotBorder:_clientHotBorder]; + _clientHotBorder = nil; + + /* Update status menu */ + [[TPStatusItemController defaultController] updateWithStatus:TPStatusIdle host:nil]; + + /* Re-add hot borders for controlling */ + [[TPClientController defaultController] updateTriggersAndShowVisualHint:NO]; + } +} + + +#pragma mark - +#pragma mark Network connection delegate + +- (void)connectionFromClientAccepted:(TPNetworkConnection*)connection +{ + [connection setDelegate:self]; +} + +- (void)connection:(TPNetworkConnection*)connection receivedMessage:(TPMessage*)message +{ + TPMsgType type = [message msgType]; + +#if DEBUG_GENERAL + DebugLog(@"slave receive msg %ld", type); +#endif + + switch(type) { + case TPControlRequestMsgType: + { + [self requestedStartControlByHost:[connection connectedHost] onConnection:connection withInfoDict:[message infoDict]]; + break; + } + case TPControlStopMsgType: + { + [self stopControlWithDisconnect:DISCONNECT_WHEN_STOP_CONTROL]; + break; + } + case TPAuthenticationRequestMsgType: + { + [[TPAuthenticationManager defaultManager] authenticationRequestedFromHost:[connection connectedHost] onConnection:connection]; + break; + } + case TPEventMsgType: + [[self eventsController] postEventWithEventData:[message data]]; + break; + default: + [super connection:connection receivedMessage:message]; + } +} + + + + +#pragma mark - +#pragma mark Setters and getters + +- (void)setAllowControl:(BOOL)allowControl +{ + if(allowControl && _state != TPServerSharedState) { + [self startSharing]; + _state = TPServerSharedState; + } + else if(!allowControl && _state != TPServerIdleState) { + [self stopSharing]; + _state = TPServerIdleState; + } +} + +- (BOOL)allowControl +{ + return (_state != TPServerIdleState); +} + +- (BOOL)isControlled +{ + return (_state == TPServerControlledState); +} + +@end diff --git a/TPStatusItemController.h b/TPStatusItemController.h new file mode 100644 index 0000000..246a700 --- /dev/null +++ b/TPStatusItemController.h @@ -0,0 +1,29 @@ +// +// TPStatusItemController.h +// teleport +// +// Created by JuL on 20/05/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import +#import "TPRemoteHost.h" + +typedef NS_ENUM(NSInteger, TPStatus) { + TPStatusIdle, + TPStatusControlling, + TPStatusControlled +} ; + +@interface TPStatusItemController : NSObject +{ + NSStatusItem * _statusItem; +} + ++ (TPStatusItemController*)defaultController; + +@property (nonatomic) BOOL showStatusItem; + +- (void)updateWithStatus:(TPStatus)status host:(TPRemoteHost*)host; + +@end diff --git a/TPStatusItemController.m b/TPStatusItemController.m new file mode 100644 index 0000000..6d77f63 --- /dev/null +++ b/TPStatusItemController.m @@ -0,0 +1,152 @@ +// +// TPStatusItemController.m +// teleport +// +// Created by JuL on 20/05/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import "TPStatusItemController.h" + +#import "TPMainController.h" +#import "TPServerController.h" +#import "TPClientController.h" +#import "TPPreferencesManager.h" +#import "TPLocalHost.h" +#import "TPPreferencePane.h" + +static TPStatusItemController * _defaultController = nil; + +@interface NSStatusItem (AppKitPrivate) + +@property (nonatomic, readonly, strong) NSButton *_button; + +@end + +@implementation TPStatusItemController + ++ (TPStatusItemController*)defaultController +{ + if(_defaultController == nil) + _defaultController = [[TPStatusItemController alloc] init]; + return _defaultController; +} + +- (instancetype) init +{ + self = [super init]; + + _statusItem = nil; + + self.showStatusItem = YES; + + return self; +} + + +- (void)setShowStatusItem:(BOOL)showStatusItem +{ + if(showStatusItem && _statusItem == nil) { + _statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; + + [_statusItem setImage:[NSImage imageNamed:@"menuicon"]]; + [_statusItem setAlternateImage:[NSImage imageNamed:@"menuicon-white"]]; + [_statusItem setHighlightMode:YES]; + + [[[_statusItem _button] cell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]]; + + NSMenu * menu = [[NSMenu alloc] init]; + + NSMenuItem * sharedMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Share this Mac", nil) action:@selector(switchMenuItem:) keyEquivalent:@""]; + [sharedMenuItem setTarget:self]; + [sharedMenuItem setRepresentedObject:ALLOW_CONTROL]; + [sharedMenuItem bind:@"state" toObject:[TPPreferencesManager sharedPreferencesManager] withKeyPath:ALLOW_CONTROL options:nil]; + [menu addItem:sharedMenuItem]; + + NSMenuItem * syncPasteboardMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Synchronize pasteboard", nil) action:@selector(switchMenuItem:) keyEquivalent:@""]; + [syncPasteboardMenuItem setTarget:self]; + [syncPasteboardMenuItem setRepresentedObject:SHARE_PASTEBOARD]; + [syncPasteboardMenuItem bind:@"state" toObject:[TPPreferencesManager sharedPreferencesManager] withKeyPath:SHARE_PASTEBOARD options:nil]; + [menu addItem:syncPasteboardMenuItem]; + + NSMenuItem * dragDropFilesMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Drag & Drop files", nil) action:@selector(switchMenuItem:) keyEquivalent:@""]; + [dragDropFilesMenuItem setTarget:self]; + [dragDropFilesMenuItem setRepresentedObject:COPY_FILES]; + [dragDropFilesMenuItem bind:@"enabled" toObject:[TPLocalHost localHost] withKeyPath:@"supportDragNDrop" options:nil]; + [dragDropFilesMenuItem bind:@"state" toObject:[TPPreferencesManager sharedPreferencesManager] withKeyPath:COPY_FILES options:nil]; + [menu addItem:dragDropFilesMenuItem]; + + [menu addItem:[NSMenuItem separatorItem]]; + + NSMenuItem * openPrefPanelMenuItem = [menu addItemWithTitle:NSLocalizedString(@"Configure\\U2026", nil) action:@selector(openTeleportPanel) keyEquivalent:@""]; + [openPrefPanelMenuItem setTarget:self]; + + [menu addItem:[NSMenuItem separatorItem]]; + +#if DEBUG_BUILD + NSMenuItem * disconnectMenuItem = [menu addItemWithTitle:@"Disconnect" action:@selector(disconnect) keyEquivalent:@""]; + [disconnectMenuItem setTarget:self]; +#endif + + NSMenuItem * quitMenuItem = [menu addItemWithTitle:NSLocalizedString(@"Quit teleport", nil) action:@selector(quitApp) keyEquivalent:@""]; + [quitMenuItem setTarget:self]; + + [_statusItem setMenu:menu]; + } + else if(!showStatusItem && _statusItem != nil) { + [[NSStatusBar systemStatusBar] removeStatusItem:_statusItem]; + _statusItem = nil; + } +} + +- (BOOL)showStatusItem +{ + return (_statusItem != nil); +} + +- (void)updateWithStatus:(TPStatus)status host:(TPRemoteHost*)host +{ + if(_statusItem != nil && [[TPPreferencesManager sharedPreferencesManager] boolForPref:SHOW_TEXTUAL_STATUS]) { + NSString * title = nil; + + switch(status) { + case TPStatusIdle: + title = nil; + break; + case TPStatusControlled: + title = [NSString stringWithFormat:NSLocalizedString(@"controlled by %@", nil), [host computerName]]; + break; + case TPStatusControlling: + title = [NSString stringWithFormat:NSLocalizedString(@"controlling %@", nil), [host computerName]]; + break; + } + + NSDisableScreenUpdates(); + [_statusItem setTitle:title]; + NSEnableScreenUpdates(); + } +} + +#if DEBUG_BUILD +- (void)disconnect +{ + [[TPClientController defaultController] stopControl]; +} +#endif + +- (void)quitApp +{ + [NSApp terminate:self]; +} + +- (void)switchMenuItem:(id)sender +{ + [[TPPreferencesManager sharedPreferencesManager] setValue:[NSNumber numberWithBool:![sender state]] forKey:[sender representedObject]]; +} + +- (void)openTeleportPanel +{ + [[TPPreferencePane preferencePane] showWindow:nil]; +} + +@end diff --git a/TPTCPSecureSocket.h b/TPTCPSecureSocket.h new file mode 100644 index 0000000..a9d2353 --- /dev/null +++ b/TPTCPSecureSocket.h @@ -0,0 +1,40 @@ +// +// TPTCPSecureSocket.h +// SecureMessaging +// +// Created by JuL on 03/04/06. +// Copyright 2006 abyssoft. All rights reserved. +// + +#import "TPTCPSocket.h" + +typedef NS_ENUM(NSInteger, TPSecureSocketState) { + TPSecureSocketNormalState, + TPSecureSocketHandshakingState +} ; + +@interface TPTCPSecureSocket : TPTCPSocket +{ + BOOL _enableEncryption; + SSLContextRef _contextRef; + TPSecureSocketState _secureSocketState; + TPTCPSecureSocket * _parentSocket; + NSLock * _sslLock; + + CFMutableDataRef _encryptedDataRef; +} + +- (void)setEnableEncryption:(BOOL)enableEncryption; +@property (nonatomic, readonly) SecCertificateRef copyPeerCertificate; + +@end + +@interface NSObject (TPTCPSecureSocket_delegate) + +- (SecIdentityRef)tcpSecureSocketCopyIdentity:(TPTCPSecureSocket*)tcpSocket; +- (BOOL)tcpSecureSocketShouldEnableEncryption:(TPTCPSecureSocket*)tcpSocket; +- (void)tcpSocketSecureConnectionSucceeded:(TPTCPSecureSocket*)tcpSocket; +- (void)tcpSocketSecureConnectionFailed:(TPTCPSecureSocket*)tcpSocket; +- (void)tcpSocket:(TPTCPSecureSocket*)listenSocket secureConnectionAccepted:(TPTCPSecureSocket*)childSocket; + +@end diff --git a/TPTCPSecureSocket.m b/TPTCPSecureSocket.m new file mode 100644 index 0000000..5179021 --- /dev/null +++ b/TPTCPSecureSocket.m @@ -0,0 +1,551 @@ +// +// TPTCPSecureSocket.m +// SecureMessaging +// +// Created by JuL on 03/04/06. +// Copyright 2006 abyssoft. All rights reserved. +// + +#import "TPTCPSecureSocket.h" + +@interface TPTCPSocket (Private) + +- (void)_close:(BOOL)dropped; +- (OSStatus)_sendData:(CFDataRef)data; +- (void)_receivedData:(CFDataRef)data; + +@property (nonatomic, readonly, strong) Class _childSocketClass; +- (void)_connectionAcceptedWithNativeSocket:(int)nativeSocket; +- (void)_connectionAcceptedWithChildSocket:(TPTCPSocket*)childSocket; +- (void)_connectionFailed; +- (void)_connectionSucceeded; +- (void)_connectionClosed; + +@end + +@interface TPTCPSecureSocket () + +@property (nonatomic, strong) NSMutableSet * childSockets; + +- (BOOL)_activateSSLWithParentSocket:(TPTCPSecureSocket*)parentSocket; +- (BOOL)_setupSSLContextForServer:(BOOL)server; +@property (nonatomic, readonly) SSLSessionState _setupSSLSession; +@property (nonatomic, readonly) SSLSessionState _startSSLSession; + +@property (nonatomic, readonly) BOOL _isServer; + +- (void)_sslFailed; +- (void)_sslSucceeded; + +- (OSStatus)_sslReadToData:(void*)data length:(size_t*)dataLength; +- (OSStatus)_sslSendData:(const void*)data length:(size_t*)length; + +@end + +OSStatus _TPTCPSecureSocketRead(SSLConnectionRef connection, void *data, size_t *dataLength); +OSStatus _TPTCPSecureSocketWrite(SSLConnectionRef connection, const void *data, size_t *dataLength); + +@implementation TPTCPSecureSocket + +#pragma mark - +#pragma mark TPTCPSocket overdrives + +- (instancetype) initWithDelegate:(id)delegate +{ + self = [super initWithDelegate:delegate]; + + _secureSocketState = TPSecureSocketNormalState; + _encryptedDataRef = NULL; + _contextRef = NULL; + _enableEncryption = YES; + _sslLock = [[NSLock alloc] init]; + _childSockets = [[NSMutableSet alloc] init]; + + return self; +} + + +- (void)setEnableEncryption:(BOOL)enableEncryption +{ + _enableEncryption = enableEncryption; +} + +//- (BOOL)listenOnPort:(int*)port tries:(int)tries +//{ +// if(![self _activateSSLForServer:YES]) +// return NO; +// else +// return [super listenOnPort:port tries:tries]; +//} + +- (BOOL)_activateSSLWithParentSocket:(TPTCPSecureSocket*)parentSocket +{ + BOOL result = NO; + + _parentSocket = parentSocket; + + if(_contextRef == NULL) + result = [self _setupSSLContextForServer:[self _isServer]]; + else + result = YES; + + if(result && [self socketState] == TPSocketConnectedState) { + SSLSessionState sessionState = [self _setupSSLSession]; + result = (sessionState == kSSLConnected) || (sessionState == kSSLHandshake); + } + + return result; +} + +- (BOOL)_setupSSLContextForServer:(BOOL)server +{ + OSStatus err; + + // DebugLog(@"%p is %@", self, server?@"server":@"client"); + + [_sslLock lock]; + + if((err = SSLNewContext([self _isServer], &_contextRef)) != noErr) { + NSLog(@"Unable to create SSL context: %d", err); + [_sslLock unlock]; + return NO; + } + + if((err = SSLSetIOFuncs(_contextRef, _TPTCPSecureSocketRead, _TPTCPSecureSocketWrite)) != noErr) { + NSLog(@"Unable to set IO functions: %d", err); + [_sslLock unlock]; + return NO; + } + + if((err = SSLSetEnableCertVerify(_contextRef, false)) != noErr) { + NSLog(@"Unable to set off cert verify: %d", err); + [_sslLock unlock]; + return NO; + } + + if((err = SSLSetClientSideAuthenticate(_contextRef, kAlwaysAuthenticate)) != noErr) { + NSLog(@"Unable to set client side authenticate: %d", err); + [_sslLock unlock]; + return NO; + } + + [_sslLock unlock]; + + return YES; +} + +- (SSLSessionState)_setupSSLSession +{ + PRINT_ME_IF(DEBUG_SOCKET); + SSLSessionState sessionState = [self _startSSLSession]; + switch(sessionState) { + case kSSLConnected: + [self _sslSucceeded]; + break; + case kSSLHandshake: + _secureSocketState = TPSecureSocketHandshakingState; + break; + default: + [self _close:NO]; + break; + } + + return sessionState; +} + +- (SSLSessionState)_startSSLSession +{ + PRINT_ME_IF(DEBUG_SOCKET); + OSStatus err; + + [_sslLock lock]; + + if((err = SSLSetConnection(_contextRef, (__bridge SSLConnectionRef)self)) != noErr) { + NSLog(@"Unable to set SSL connection: %d", err); + [_sslLock unlock]; + return kSSLAborted; + } + + if(![_delegate respondsToSelector:@selector(tcpSecureSocketCopyIdentity:)]) { + NSLog(@"Unable to get identity from delegate %@", _delegate); + [_sslLock unlock]; + return kSSLAborted; + } + + SecIdentityRef identity = [_delegate tcpSecureSocketCopyIdentity:self]; + if(identity == NULL) { + NSLog(@"Got NULL identity from delegate %@", _delegate); + [_sslLock unlock]; + return kSSLAborted; + } + + CFArrayRef certificatesRef = CFArrayCreate(kCFAllocatorDefault, (const void**)&identity, 1, &kCFTypeArrayCallBacks); + CFRelease(identity); + + if((err = SSLSetCertificate(_contextRef, certificatesRef)) != noErr) { + NSLog(@"Unable to set certificate: %d", err); + CFRelease(certificatesRef); + [_sslLock unlock]; + return kSSLAborted; + } + + CFRelease(certificatesRef); + + _encryptedDataRef = CFDataCreateMutable(kCFAllocatorDefault, 0); + + err = SSLHandshake(_contextRef); +// DebugLog(@"%p start handshaking", self); + + [_sslLock unlock]; + + if(err == noErr) { + return kSSLConnected; + } + else if(err != errSSLWouldBlock) { + NSLog(@"Unable to handshake: %d", err); + return kSSLAborted; + } + else + return kSSLHandshake; +} + +- (BOOL)_isServer +{ + return (_parentSocket != nil); +} + +- (void)_connectionAcceptedWithChildSocket:(TPTCPSocket*)childSocket +{ + PRINT_ME_IF(DEBUG_SOCKET); + + TPTCPSecureSocket * secureChildSocket = (TPTCPSecureSocket*)childSocket; + BOOL enableEncryption = YES; + if([_delegate respondsToSelector:@selector(tcpSecureSocketShouldEnableEncryption:)]) { + enableEncryption = [_delegate tcpSecureSocketShouldEnableEncryption:secureChildSocket]; + } + + [secureChildSocket setEnableEncryption:enableEncryption]; + + if(enableEncryption) { + [_childSockets addObject:secureChildSocket]; + [secureChildSocket setDelegate:[self delegate]]; + [secureChildSocket _activateSSLWithParentSocket:self]; + // balanced with release in _sslSucceeded + } + else { + [super _connectionAcceptedWithChildSocket:childSocket]; + } +} + +- (void)_connectionSucceeded +{ + PRINT_ME_IF(DEBUG_SOCKET); + + if(_enableEncryption) { + _socketState = TPSocketConnectedState; + [self _activateSSLWithParentSocket:nil]; + } + else { + [super _connectionSucceeded]; + } +} + +- (void)_connectionClosed +{ + PRINT_ME_IF(DEBUG_SOCKET); + + if(_enableEncryption) { + if(_socketState == TPSocketDisconnectedState) + return; + + if(_secureSocketState == TPSecureSocketNormalState) + [super _connectionClosed]; + else + [self _sslFailed]; + } + else { + [super _connectionClosed]; + } +} + +- (void)_sslFailed +{ + PRINT_ME_IF(DEBUG_SOCKET); + + if([_delegate respondsToSelector:@selector(tcpSocketSecureConnectionFailed:)]) + [_delegate tcpSocketSecureConnectionFailed:self]; + + [self _close:NO]; + +} + +- (void)_sslSucceeded +{ + PRINT_ME_IF(DEBUG_SOCKET); + _secureSocketState = TPSecureSocketNormalState; + + if([self _isServer]) { + [_parentSocket->_childSockets removeObject:self]; + + if([_delegate respondsToSelector:@selector(tcpSocket:secureConnectionAccepted:)]) + [_delegate tcpSocket:_parentSocket secureConnectionAccepted:self]; + } + else { + if([_delegate respondsToSelector:@selector(tcpSocketSecureConnectionSucceeded:)]) + [_delegate tcpSocketSecureConnectionSucceeded:self]; + } + + if([self _isServer]) { + } +} + +- (BOOL)sendData:(NSData*)data +{ + PRINT_ME_IF(DEBUG_SOCKET); + + if(_enableEncryption) { + size_t dataLength = [data length]; + size_t dataProcessed = 0; + + while(dataProcessed < dataLength) { + size_t processed = 0; + OSStatus err; + [_sslLock lock]; + if((err = SSLWrite(_contextRef, ([data bytes] + dataProcessed), (dataLength - dataProcessed), &processed)) != noErr) { + NSLog(@"SSL unable to write data: %d", err); + [_sslLock unlock]; + return NO; + } + [_sslLock unlock]; + dataProcessed += processed; + } + + return YES; + } + else { + return [super sendData:data]; + } +} + +- (void)_receivedData:(CFDataRef)data +{ + PRINT_ME_IF(DEBUG_SOCKET); + + if(_enableEncryption) { + SSLSessionState sessionState; + [_sslLock lock]; + SSLGetSessionState(_contextRef, &sessionState); + [_sslLock unlock]; + + CFDataAppendBytes(_encryptedDataRef, CFDataGetBytePtr(data), CFDataGetLength(data)); + + // DebugLog(@"%p receivedData(%d) state=%d", self, CFDataGetLength(data), sessionState); + + switch(sessionState) { + case kSSLIdle: + case kSSLHandshake: + { + [_sslLock lock]; + OSStatus err = SSLHandshake(_contextRef); + [_sslLock unlock]; + + // DebugLog(@"%p handshake", self); + + if(err != noErr) { + if(err != errSSLWouldBlock) { + NSLog(@"Unable to handshake: %d", err); + [self _sslFailed]; + } + break; + } + else { + DebugLog(@"%p handshake success!", self); + switch(_secureSocketState) { + case TPSecureSocketNormalState: + NSLog(@"Handshaking but normal socket state!"); + break; + case TPSecureSocketHandshakingState: + [self _sslSucceeded]; + break; + } + } + } + case kSSLConnected: + { + CFMutableDataRef decryptedDataRef = CFDataCreateMutable(kCFAllocatorDefault, 0); + CFIndex index = CFDataGetLength(decryptedDataRef); + + OSStatus err = noErr; + while(err == noErr) { + size_t bufSize = 0; + + [_sslLock lock]; + if((err = SSLGetBufferedReadSize(_contextRef, &bufSize)) != noErr) { + NSLog(@"Unable to get bufSize: %d", err); + [_sslLock unlock]; + CFRelease(decryptedDataRef); + [self _sslFailed]; + return; + } + + if(bufSize == 0) { + bufSize = 4096; + } + + CFIndex dataLength = index + bufSize; + CFDataSetLength(decryptedDataRef, dataLength); + + UInt8 * buffer = CFDataGetMutableBytePtr(decryptedDataRef); + size_t processed = 0; + err = SSLRead(_contextRef, buffer + index, bufSize, &processed); + + [_sslLock unlock]; + + if(err != noErr && err != errSSLWouldBlock && err != errSSLClosedGraceful) { + NSLog(@"Unable to read: %d", err); + CFRelease(decryptedDataRef); + [self _sslFailed]; + return; + } + + if(processed < bufSize) { + dataLength = index + processed; + CFDataSetLength(decryptedDataRef, dataLength); + } + + if(processed > 0) { + index += processed; + } + } + + if(CFDataGetLength(decryptedDataRef) > 0) { + [super _receivedData:decryptedDataRef]; + } + CFRelease(decryptedDataRef); + break; + } + case kSSLClosed: + case kSSLAborted: + NSLog(@"SSL received data with invalid session state: %d", sessionState); + [self _sslFailed]; + break; + } + } + else { + [super _receivedData:data]; + } +} + +- (void)_close:(BOOL)dropped +{ + if(_enableEncryption) { + if(!dropped) { + [_sslLock lock]; + SSLClose(_contextRef); + [_sslLock unlock]; + } + + [super _close:dropped]; + + SSLDisposeContext(_contextRef); + _contextRef = NULL; + + if(_encryptedDataRef != NULL) { + CFRelease(_encryptedDataRef); + _encryptedDataRef = NULL; + } + } + else { + [super _close:dropped]; + } +} + + +#pragma mark - +#pragma mark Internal SSL Input/Output + +- (OSStatus)_sslReadToData:(void*)data length:(size_t*)length +{ + PRINT_ME_IF(DEBUG_SOCKET); + + size_t askedSize = *length; + *length = MIN(askedSize, CFDataGetLength(_encryptedDataRef)); + + if(*length == 0) { + return errSSLWouldBlock; + } + + CFRange bytesRange = CFRangeMake(0, *length); + CFDataGetBytes(_encryptedDataRef, bytesRange, data); + CFDataDeleteBytes(_encryptedDataRef, bytesRange); + + if(askedSize > *length) { + return errSSLWouldBlock; + } + + return noErr; +} + +- (OSStatus)_sslSendData:(const void*)data length:(size_t*)length +{ + PRINT_ME_IF(DEBUG_SOCKET); + if(*length == 0) return noErr; + CFDataRef dataRef = CFDataCreateWithBytesNoCopy(NULL, data, *length, kCFAllocatorNull); + if(dataRef == NULL) + return -1; + OSStatus err = [self _sendData:dataRef]; + CFRelease(dataRef); + return err; +} + + +#pragma mark - +#pragma mark SSL Stuff + +- (SecCertificateRef)copyPeerCertificate +{ + PRINT_ME_IF(DEBUG_SOCKET); + OSStatus err; + CFArrayRef certsRef; + + [_sslLock lock]; + +#if LEGACY_BUILD + err = SSLGetPeerCertificates(_contextRef, &certsRef); +#else + err = SSLCopyPeerCertificates(_contextRef, &certsRef); +#endif + + [_sslLock unlock]; + + if(err != noErr) { + NSLog(@"Unable to get peer certificate: %d", err); + return NULL; + } + + if(CFArrayGetCount(certsRef) < 1) { + NSLog(@"Unable to get peer certificate: %d", err); + return NULL; + } + + SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certsRef, 0); + CFRetain(cert); + CFRelease(certsRef); + + return cert; +} + +@end + +OSStatus _TPTCPSecureSocketRead(SSLConnectionRef connection, void *data, size_t *dataLength) +{ + TPTCPSecureSocket * socket = (__bridge TPTCPSecureSocket*)connection; + return [socket _sslReadToData:data length:dataLength]; +} + + +OSStatus _TPTCPSecureSocketWrite(SSLConnectionRef connection, const void *data, size_t *dataLength) +{ + TPTCPSecureSocket * socket = (__bridge TPTCPSecureSocket*)connection; + return [socket _sslSendData:data length:dataLength]; +} diff --git a/TPTCPSocket.h b/TPTCPSocket.h new file mode 100644 index 0000000..57892f6 --- /dev/null +++ b/TPTCPSocket.h @@ -0,0 +1,80 @@ +// +// TPTCPSocket.h +// teleport +// +// Created by JuL on Thu Jan 08 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +#include + +#define DEBUG_SOCKET 0 + +#ifndef DebugLog +#define DebugLog(logString, args...) NSLog(logString , ##args) +#endif + +typedef struct { + UInt8 bytes[kIOEthernetAddressSize]; +} IOEthernetAddress; + +typedef NS_ENUM(NSInteger, TPSocketState) { + TPSocketDisconnectedState, + TPSocketListeningState, + TPSocketConnectingState, + TPSocketConnectedState, + TPSocketErrorState +} ; + +@interface TPTCPSocket : NSObject +{ + id _delegate; + + CFSocketRef _cfSocket; + CFRunLoopRef _runLoopRef; + CFRunLoopSourceRef _sourceRef; + + int _maxSndBufferSize; + BOOL _noDelay; + NSLock * _lock; + + TPSocketState _socketState; +} + ++ (BOOL)wakeUpHostWithMACAddress:(IOEthernetAddress)macAddress; + +- (instancetype) initWithDelegate:(id)delegate NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, unsafe_unretained) id delegate; + +- (void)setNoDelay:(BOOL)noDelay; + +- (BOOL)listenOnPort:(int*)port tries:(int)tries; +- (BOOL)listenOnPort:(int)port; +- (BOOL)connectToHost:(NSString*)host onPort:(int)port; +- (void)setNativeSocket:(CFSocketNativeHandle)native; + +@property (nonatomic, readonly, copy) NSString *localAddress; +@property (nonatomic, readonly, copy) NSString *remoteAddress; +@property (nonatomic, readonly) CFSocketNativeHandle nativeHandle; + +- (BOOL)sendData:(NSData*)data; +@property (nonatomic, readonly) TPSocketState socketState; +- (void)close; + +- (void)_setRunLoop:(CFRunLoopRef)runLoop; + +@end + +@interface NSObject (TPTCPSocket_delegate) + +- (void)tcpSocket:(TPTCPSocket*)tcpSocket gotData:(NSData*)data; +- (void)tcpSocketDidSendData:(TPTCPSocket*)tcpSocket; +- (void)tcpSocket:(TPTCPSocket*)listenSocket connectionAccepted:(TPTCPSocket*)childSocket; +- (void)tcpSocketConnectionSucceeded:(TPTCPSocket*)tcpSocket; +- (void)tcpSocketConnectionFailed:(TPTCPSocket*)tcpSocket; +- (void)tcpSocketConnectionClosed:(TPTCPSocket*)tcpSocket; + +@end diff --git a/TPTCPSocket.m b/TPTCPSocket.m new file mode 100644 index 0000000..cdc4b1b --- /dev/null +++ b/TPTCPSocket.m @@ -0,0 +1,625 @@ +// +// TPTCPSocket.m +// teleport +// +// Created by JuL on Thu Jan 08 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPTCPSocket.h" + +#include +#include +#include +#include +#include +#include +#include + +#define CONNECTION_TIMEOUT 5.0 +#define SEND_TIMEOUT 2.0 + +static void _cfsocketCallback(CFSocketRef inCFSocketRef, CFSocketCallBackType inType, CFDataRef inAddress, const void* inData, void* inContext); + +@interface TPTCPSocket (Private) + +- (void)_close:(BOOL)dropped; +- (OSStatus)_sendData:(CFDataRef)data; +- (void)_receivedData:(CFDataRef)data; + +- (void)_connectionAcceptedWithNativeSocket:(int)nativeSocket; +- (void)_connectionAcceptedWithChildSocket:(TPTCPSocket*)childSocket; +- (void)_connectionFailed; +- (void)_connectionSucceeded; +- (void)_connectionClosed; + +@end + +@implementation TPTCPSocket + +#define DEFAULTTARGET "255.255.255.255" ++ (BOOL)wakeUpHostWithMACAddress:(IOEthernetAddress)macAddress +{ + int sock; + int optval = 1; + int i, j, rc; + char msg[1024]; + char *target = DEFAULTTARGET; + int msglen = 0; + struct sockaddr_in bcast; + struct hostent *he; + struct in_addr inaddr; + short bport = htons(32767); + + for (i = 0; i < 6; i++) { + msg[msglen++] = 0xff; + } + for (i = 0; i < 16; i++) { +// memcpy(msg + msglen, macAddress.bytes, kIOEthernetAddressSize); + for (j = 0; j < kIOEthernetAddressSize; j++) { + msg[msglen++] = macAddress.bytes[j]; + } + } + + if (!inet_aton(target, &inaddr)) { + he = gethostbyname(target); + inaddr = *(struct in_addr *)he->h_addr_list[0]; + } + + memset(&bcast, 0, sizeof(bcast)); + bcast.sin_family = AF_INET; + bcast.sin_addr.s_addr = inaddr.s_addr; + bcast.sin_port = bport; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) { + printf ("Can't allocate socket\n"); + return NO; + } + if ((rc=setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval))) < 0) { + printf ("Can't socket option SO_BROADCAST: rc = %d, errno=%s(%d)\n", rc, strerror(errno), errno); + return NO; + } + sendto(sock, &msg, msglen, 0, (struct sockaddr *)&bcast, sizeof(bcast)); + close(sock); + return YES; +} + +- (instancetype) initWithDelegate:(id)delegate +{ + self = [super init]; + + _delegate = delegate; + _cfSocket = NULL; + _runLoopRef = NULL; + _sourceRef = NULL; + _socketState = TPSocketDisconnectedState; + _noDelay = NO; + _lock = [[NSLock alloc] init]; + + return self; +} + +- (void)setDelegate:(id)delegate +{ + _delegate = delegate; +} + +- (id)delegate +{ + return _delegate; +} + +- (void)setNoDelay:(BOOL)noDelay +{ + if(_noDelay != noDelay) { + _noDelay = noDelay; + + [_lock lock]; + + /* Setup socket flags */ + int socketOptionFlag = noDelay?1:0; + int result = setsockopt(CFSocketGetNative(_cfSocket), IPPROTO_TCP, TCP_NODELAY, &socketOptionFlag, sizeof(int)); + if(result < 0) { + DebugLog(@"error setting no delay"); + [_lock unlock]; + return; + } + + if(noDelay) { + socketOptionFlag = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; + int result = setsockopt(CFSocketGetNative(_cfSocket), IPPROTO_IP, IP_TOS, &socketOptionFlag, sizeof(socketOptionFlag)); + if(result < 0) { + DebugLog(@"error setting low delay"); + [_lock unlock]; + return; + } + } + else { + /* Tweak for fast transfers */ +// int bufferSize = 32768; +// int result = setsockopt(CFSocketGetNative(_cfSocket), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(int)); +// if(result < 0) { +// DebugLog(@"error setting send buffer"); +// [_lock unlock]; +// return; +// } +// +// result = setsockopt(CFSocketGetNative(_cfSocket), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(int)); +// if(result < 0) { +// DebugLog(@"error setting receive buffer"); +// [_lock unlock]; +// return; +// } + } + + [_lock unlock]; + } +} + +- (BOOL)_setupSocket +{ + if(_cfSocket == NULL) { + DebugLog(@"cfSocket is NULL"); + return NO; + } + + /* Setup socket flags */ + int socketOptionFlag = 1; + int result = setsockopt(CFSocketGetNative(_cfSocket), SOL_SOCKET, SO_REUSEADDR, &socketOptionFlag, sizeof(int)); + if(result < 0) { + DebugLog(@"error setting reusability of address"); + return NO; + } + + [self _setRunLoop:CFRunLoopGetCurrent()]; + + return YES; +} + +- (BOOL)listenOnPort:(int*)port tries:(int)tries +{ + int currentPort = *port; + int currentTry = tries; + + while(--currentTry) { + if([self listenOnPort:currentPort]) + break; + else { + [self _close:NO]; + currentPort++; + } + } + + if(currentTry > 0) { + *port = currentPort; + return YES; + } + else + return NO; +} + +- (BOOL)listenOnPort:(int)port +{ + OSStatus err; + + /* Setup context */ + CFSocketContext socketContext; + bzero(&socketContext, sizeof(CFSocketContext)); + socketContext.info = (__bridge void *)(self); + + /* Create socket */ + _cfSocket = CFSocketCreate(kCFAllocatorDefault, + AF_INET, + SOCK_STREAM, + IPPROTO_TCP, + kCFSocketAcceptCallBack, + &_cfsocketCallback, + &socketContext + ); + + if(![self _setupSocket]) + return NO; + + /* Setup struct sockaddr */ + struct sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(INADDR_ANY); + address.sin_port = htons(port); + + CFDataRef addr = CFDataCreate(kCFAllocatorDefault, (void*)&address, sizeof(struct sockaddr_in)); + if((err = CFSocketSetAddress(_cfSocket, addr)) != kCFSocketSuccess) { + CFRelease(addr); + DebugLog(@"listen error: %d", errno); + return NO; + } + + CFRelease(addr); + +#if DEBUG_SOCKET + DebugLog(@"%@ listening on port %d", self, port); +#endif + + _socketState = TPSocketListeningState; + + return YES; +} + +- (BOOL)connectToHost:(NSString*)host onPort:(int)port +{ + DebugLog(@"connect to %@ on port %d", host, port); + + if(host == nil) { + NSLog(@"trying to connect to nil host"); + return NO; + } + + /* Setup context */ + CFSocketContext socketContext; + bzero(&socketContext, sizeof(CFSocketContext)); + socketContext.info = (__bridge void *)(self); + + /* Create socket */ + _cfSocket = CFSocketCreate(kCFAllocatorDefault, + PF_INET, + SOCK_STREAM, + IPPROTO_TCP, + kCFSocketDataCallBack|kCFSocketConnectCallBack|kCFSocketWriteCallBack, + &_cfsocketCallback, + &socketContext + ); + + if(![self _setupSocket]) + return NO; + + /* Auto re-enable callbacks */ + CFOptionFlags socketFlags = CFSocketGetSocketFlags(_cfSocket); + socketFlags |= kCFSocketAutomaticallyReenableDataCallBack; + CFSocketSetSocketFlags(_cfSocket, socketFlags); + + /* Setup struct sockaddr */ + struct sockaddr_in address; + address.sin_family = AF_INET; +#if LEGACY_BUILD + struct hostent * hp = gethostbyname([host cString]); +#else + struct hostent * hp = gethostbyname([host cStringUsingEncoding:NSASCIIStringEncoding]); +#endif + if(hp == 0) + return NO; + bcopy((char *)hp->h_addr,(char *)&address.sin_addr, hp->h_length); + address.sin_port = htons(port); + + /* Connect */ + CFDataRef addr = CFDataCreate(kCFAllocatorDefault, (void*)&address, sizeof(struct sockaddr_in)); + if(CFSocketConnectToAddress(_cfSocket, addr, CONNECTION_TIMEOUT) != kCFSocketSuccess) { + if(addr != NULL) { + CFRelease(addr); + } + DebugLog(@"error connecting"); + return NO; + } + + if(addr != NULL) { + CFRelease(addr); + } + +#if DEBUG_SOCKET + DebugLog(@"%@ connecting to host %@ port %d", self, host, port); +#endif + + _socketState = TPSocketConnectingState; + + return YES; +} + +- (void)setNativeSocket:(CFSocketNativeHandle)native +{ + /* Setup context */ + CFSocketContext socketContext; + bzero(&socketContext, sizeof(CFSocketContext)); + socketContext.info = (__bridge void *)(self); + + /* Create socket from native */ + _cfSocket = CFSocketCreateWithNative(kCFAllocatorDefault, + native, + kCFSocketDataCallBack|kCFSocketWriteCallBack, + &_cfsocketCallback, + &socketContext + ); + + if(![self _setupSocket]) + return; + + /* Auto re-enable callbacks */ + CFOptionFlags socketFlags = CFSocketGetSocketFlags(_cfSocket); + socketFlags |= kCFSocketAutomaticallyReenableDataCallBack; + CFSocketSetSocketFlags(_cfSocket, socketFlags); + + _socketState = TPSocketConnectedState; +} + +- (void)_setRunLoop:(CFRunLoopRef)runLoopRef +{ + [_lock lock]; + + //DebugLog(@"socket: %@ _setRunLoop: %@ _runLoop: %@ _sourceRef: %@ current: %@", self, runLoopRef, _runLoopRef, _sourceRef, CFRunLoopGetCurrent()); + + if(runLoopRef != NULL && _sourceRef == NULL && _cfSocket != NULL) + _sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _cfSocket, 0); + else if(runLoopRef != _runLoopRef && _runLoopRef != NULL && _sourceRef != NULL) { + //DebugLog(@"willRemoveSource"); + CFRunLoopRemoveSource(_runLoopRef, _sourceRef, kCFRunLoopCommonModes); + //DebugLog(@"didRemoveSource"); + } + + //DebugLog(@"_sourceRef: %@", _sourceRef); + + _runLoopRef = runLoopRef; + + if(runLoopRef != NULL && _sourceRef != NULL) { + CFRunLoopAddSource(runLoopRef, _sourceRef, kCFRunLoopCommonModes); + //DebugLog(@"adding source %@ to runloop %@ in mode %@", _sourceRef, runLoopRef, kCFRunLoopCommonModes); + } + else if(runLoopRef == NULL && _sourceRef != NULL) { + CFRelease(_sourceRef); + _sourceRef = NULL; + } + + [_lock unlock]; +} + +- (void)dealloc +{ + [self close]; + DebugLog(@"socket %@ dealloc", self); +} + +- (NSString*)_stringAddressFromCFData:(CFDataRef)dataRef +{ + char buffer[256]; + + if(dataRef == NULL) + return nil; + + struct sockaddr * socketAddress = (struct sockaddr *)CFDataGetBytePtr(dataRef); + + if(socketAddress->sa_family != AF_INET) + NSLog(@"warning: address not AF_INET"); + + if(inet_ntop(AF_INET, &((struct sockaddr_in *)socketAddress)->sin_addr, buffer, sizeof(buffer))) { +#if LEGACY_BUILD + return [NSString stringWithCString:buffer]; +#else + return @(buffer); +#endif + } + + return nil; +} + +- (NSString*)localAddress +{ + [_lock lock]; + CFDataRef dataRef = CFSocketCopyAddress(_cfSocket); + [_lock unlock]; + + if(dataRef != NULL) { + NSString * localAddress = [self _stringAddressFromCFData:dataRef]; + CFRelease(dataRef); + return localAddress; + } + else + return nil; +} + +- (NSString*)remoteAddress +{ + [_lock lock]; + CFDataRef dataRef = CFSocketCopyPeerAddress(_cfSocket); + [_lock unlock]; + + if(dataRef != NULL) { + NSString * localAddress = [self _stringAddressFromCFData:dataRef]; + CFRelease(dataRef); + return localAddress; + } + else + return nil; +} + +- (CFSocketNativeHandle)nativeHandle +{ + return CFSocketGetNative(_cfSocket); +} + +- (BOOL)sendData:(NSData*)data +{ +#if DEBUG_SOCKET + DebugLog(@"%@ sending data (%d)", self, [data length]); +#endif + + BOOL result = ([self _sendData:(CFDataRef)data] == noErr); + +#if DEBUG_SOCKET + DebugLog(@"%@ end sending data (%d): %d", self, [data length], result); +#endif + + return result; +} + +- (OSStatus)_sendData:(CFDataRef)data +{ + [_lock lock]; + + if(_cfSocket == NULL || !CFSocketIsValid(_cfSocket)) { + [_lock unlock]; + return kCFSocketError; + } + + CFSocketEnableCallBacks(_cfSocket, kCFSocketWriteCallBack); + + int retries = 10; + SInt32 size = 0; + CFSocketNativeHandle sock = CFSocketGetNative(_cfSocket); + const uint8_t * dataptr = CFDataGetBytePtr(data); + SInt32 datalen = CFDataGetLength(data); + + size = send(sock, dataptr, datalen, 0); + while(size < datalen && retries--) { + DebugLog(@"retrying send, sent size: %d", size); + if(size > 0) { + dataptr += size; + datalen -= size; + } + size = send(sock, dataptr, datalen, 0); + DebugLog(@"after retry size: %d", size); + } + + [_lock unlock]; + + if(retries <= 0) { + NSLog(@"Unable to send data: %@", self); + return kCFSocketError; + } + + return noErr; +} + +- (TPSocketState)socketState +{ + return _socketState; +} + +- (void)close +{ + [self setDelegate:nil]; + + if(_socketState == TPSocketDisconnectedState) + return; + + [self _close:NO]; +} + +- (void)_close:(BOOL)dropped +{ + _socketState = TPSocketDisconnectedState; + + DebugLog(@"socket %@ %@", self, dropped?@"dropped":@"closed"); + + if(_sourceRef != NULL) { + [self _setRunLoop:NULL]; + } + if(_cfSocket != NULL) { + [_lock lock]; + CFSocketInvalidate(_cfSocket); + CFRelease(_cfSocket); + _cfSocket = NULL; + [_lock unlock]; + } +} + +- (void)_receivedData:(CFDataRef)data +{ + if([_delegate respondsToSelector:@selector(tcpSocket:gotData:)]) + [_delegate tcpSocket:self gotData:(__bridge NSData*)data]; +} + +- (void)_didSendData +{ +#if DEBUG_SOCKET + DebugLog(@"%@ data sent", self); +#endif + + if([_delegate respondsToSelector:@selector(tcpSocketDidSendData:)]) + [_delegate tcpSocketDidSendData:self]; +} + +- (void)_connectionAcceptedWithNativeSocket:(int)nativeSocket +{ + TPTCPSocket * childSocket = [[[self class] alloc] initWithDelegate:nil]; + [childSocket setNativeSocket:nativeSocket]; + [self _connectionAcceptedWithChildSocket:childSocket]; +} + +- (void)_connectionAcceptedWithChildSocket:(TPTCPSocket*)childSocket +{ + if([_delegate respondsToSelector:@selector(tcpSocket:connectionAccepted:)]) + [_delegate tcpSocket:self connectionAccepted:childSocket]; +} + +- (void)_connectionFailed +{ + [self _close:NO]; + + if([_delegate respondsToSelector:@selector(tcpSocketConnectionFailed:)]) + [_delegate tcpSocketConnectionFailed:self]; +} + +- (void)_connectionSucceeded +{ + PRINT_ME; + _socketState = TPSocketConnectedState; + + if([_delegate respondsToSelector:@selector(tcpSocketConnectionSucceeded:)]) + [_delegate tcpSocketConnectionSucceeded:self]; +} + +- (void)_connectionClosed +{ + if(_socketState == TPSocketDisconnectedState) + return; + + [self _close:YES]; + + if([_delegate respondsToSelector:@selector(tcpSocketConnectionClosed:)]) + [_delegate tcpSocketConnectionClosed:self]; +} + +@end + +void _cfsocketCallback(CFSocketRef inCFSocketRef, CFSocketCallBackType inType, CFDataRef inAddress, const void* inData, void* inContext) +{ + TPTCPSocket * tcpSocket = (__bridge TPTCPSocket*)inContext; + + if(!tcpSocket) + return; + + switch(inType) + { + case kCFSocketDataCallBack: + { + if(inData == NULL || CFDataGetLength((CFDataRef)inData) == 0) + [tcpSocket _connectionClosed]; + else + [tcpSocket _receivedData:(CFDataRef)inData]; + break; + } + case kCFSocketAcceptCallBack: + { + int native = *((int*)inData); + [tcpSocket _connectionAcceptedWithNativeSocket:native]; + break; + } + case kCFSocketConnectCallBack: + { + if(inData == NULL) + [tcpSocket _connectionSucceeded]; + else + [tcpSocket _connectionFailed]; + break; + } + case kCFSocketWriteCallBack: + { + [tcpSocket _didSendData]; + break; + } +// case kCFSocketWriteCallBack: +// { +// [tcpSocket _becameWritable]; +// } + default: + break; + } +} + diff --git a/TPTableView.h b/TPTableView.h new file mode 100644 index 0000000..d51576d --- /dev/null +++ b/TPTableView.h @@ -0,0 +1,20 @@ +// +// TPTableView.h +// teleport +// +// Created by JuL on 12/05/06. +// Copyright 2006 abyssoft. All rights reserved. +// + +#import + + +@interface TPTableView : NSTableView + +@end + +@interface NSObject (TPTableViewDelegate) + +- (BOOL)tableView:(NSTableView*)tableView handleKeyDown:(NSEvent*)event; + +@end diff --git a/TPTableView.m b/TPTableView.m new file mode 100644 index 0000000..018a624 --- /dev/null +++ b/TPTableView.m @@ -0,0 +1,24 @@ +// +// TPTableView.m +// teleport +// +// Created by JuL on 12/05/06. +// Copyright 2006 abyssoft. All rights reserved. +// + +#import "TPTableView.h" + + +@implementation TPTableView + +- (void)keyDown:(NSEvent*)event +{ + if(_delegate && [_delegate respondsToSelector:@selector(tableView:handleKeyDown:)]) { + if([_delegate tableView:self handleKeyDown:event]) + return; + } + + [super keyDown:event]; +} + +@end diff --git a/TPTestsController.h b/TPTestsController.h new file mode 100644 index 0000000..40f5e45 --- /dev/null +++ b/TPTestsController.h @@ -0,0 +1,17 @@ +// +// TPTestsController.h +// teleport +// +// Created by JuL on 09/11/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import + + +@interface TPTestsController : NSObject + ++ (void)runTests; ++ (void)runFakeHostsTest; + +@end diff --git a/TPTestsController.m b/TPTestsController.m new file mode 100644 index 0000000..ac7f00c --- /dev/null +++ b/TPTestsController.m @@ -0,0 +1,69 @@ +// +// TPTestsController.m +// teleport +// +// Created by JuL on 09/11/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import "TPTestsController.h" + +#import "TPFakeHostsGenerator.h" + +#import "TPLocalHost.h" +#import "TPRemoteHost.h" +#import "TPEventTapsController.h" +#import "TPMessage.h" + +@interface TPTestsController (Tests) + ++ (void)_testArchiving; ++ (void)_testEventTaps; ++ (void)_testFolderSize; + +@end + + +@implementation TPTestsController + ++ (void)runTests +{ + NSLog(@"===== RUNING TESTS ====="); +// [TPTestSocket test]; +// [[TPSecurityManager defaultManager] testSecurity]; + [self _testArchiving]; + [self _testFolderSize]; +// [self _testEventTaps]; +} + ++ (void)runFakeHostsTest +{ + TPFakeHostsGenerator * generator = [[TPFakeHostsGenerator alloc] init]; + [generator run]; +} + ++ (void)_testArchiving +{ + +} + ++ (void)_testEventTaps +{ + [[TPEventTapsController defaultController] startGettingEventsForListener:nil onScreen:[[TPLocalHost localHost] mainScreen]]; +} + ++ (void)_testSizeOfPath:(NSString*)path +{ + BOOL smaller = [[NSFileManager defaultManager] isTotalSizeOfItemAtPath:path smallerThan:1*1024*1024*1024]; + NSLog(@"path: %@ smaller: %d", path, smaller); +} + ++ (void)_testFolderSize +{ + NSString * desktopPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Desktop"]; + [self _testSizeOfPath:desktopPath]; + [self _testSizeOfPath:[desktopPath stringByAppendingPathComponent:@"IT Crowd"]]; + [self _testSizeOfPath:[desktopPath stringByAppendingPathComponent:@"dts-demo-dvd9.jpg"]]; +} + +@end diff --git a/TPTransfer.h b/TPTransfer.h new file mode 100644 index 0000000..eedd7a7 --- /dev/null +++ b/TPTransfer.h @@ -0,0 +1,93 @@ +// +// TPTransfer.h +// teleport +// +// Created by JuL on 13/02/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import + +#import "TPTransfersManager.h" + +typedef NS_ENUM(NSInteger, TPTransferPriority) { + TPTransferHighPriority, + TPTransferMediumPriority, + TPTransferLowPriority +} ; + +extern NSString * TPTransferUIDKey; +extern NSString * TPTransferTypeKey; +extern NSString * TPTransferDataLengthKey; +extern NSString * TPTransferPortKey; + +@class TPTCPSecureSocket, TPRemoteHost/*, TPNetworkConnection*/; + +@interface TPTransfer : NSObject +{ + TPTCPSecureSocket * _listenSocket; + TPTCPSecureSocket * _socket; +// TPNetworkConnection * _connection; + NSLock * _lock; + + NSString * _uid; + NSMutableData * _data; + TPDataLength _totalDataLength; + TPDataLength _receivedDataLength; + + +// id _delegate; + TPTransfersManager * _manager; +} + +@property (nonatomic, readonly, copy) NSString *type; +@property (nonatomic, readonly, copy) NSString *uid; +@property (nonatomic, readonly) TPTransferPriority priority; + +//- (void)setDelegate:(id)delegate; +//- (id)delegate; + +- (void)setManager:(TPTransfersManager*)manager; + +@property (nonatomic, getter=isIncoming, readonly) BOOL incoming; +@property (nonatomic, readonly) BOOL hasFeedback; +@property (nonatomic, readonly, copy) NSString *completionMessage; +@property (nonatomic, readonly, copy) NSString *errorMessage; + +//- (void)setConnection:(TPNetworkConnection*)connection; + +@end + +@interface TPOutgoingTransfer : TPTransfer +{ +} + ++ (TPOutgoingTransfer*)transfer; + +- (void)_beginTransfer; + +@property (nonatomic, readonly) BOOL shouldBeEncrypted; + +@property (nonatomic, readonly, copy) NSData *dataToTransfer; +@property (nonatomic, readonly) TPDataLength totalDataLength; +@property (nonatomic, readonly, copy) NSDictionary *infoDict; + +@property (nonatomic, readonly) BOOL prepareToSendData; +- (void)setSocket:(TPTCPSocket*)socket; + +@end + +@interface TPIncomingTransfer : TPTransfer +{ + BOOL _shouldEncrypt; +} + ++ (TPIncomingTransfer*)transferOfType:(NSString*)type withUID:(NSString*)uid; + +@property (nonatomic, readonly) BOOL requireTrustedHost; + +- (void)_stopListening; + +- (BOOL)prepareToReceiveDataWithInfoDict:(NSDictionary*)infoDict fromHost:(TPRemoteHost*)host onPort:(int*)port delegate:(id)delegate; + +@end diff --git a/TPTransfer.m b/TPTransfer.m new file mode 100644 index 0000000..212c338 --- /dev/null +++ b/TPTransfer.m @@ -0,0 +1,600 @@ +// +// TPTransfer.m +// teleport +// +// Created by JuL on 13/02/05. +// Copyright 2003-2005 abyssoft. All rights reserved. +// + +#import "TPTransfer.h" +#import "TPTransfer_Private.h" + +#import "TPConnectionsManager.h" +#import "TPPreferencesManager.h" +#import "TPTCPSecureSocket.h" + +#import "TPLocalHost.h" +#import "TPRemoteHost.h" + +#include + +#define REFRESH_RATE 0.1 +#define BLOCK_SIZE 256*1024 +#define SLEEP_LENGTH 20000 + +NSString * TPTransferUIDKey = @"TPTransferUID"; +NSString * TPTransferTypeKey = @"TPTransferType"; +NSString * TPTransferDataLengthKey = @"TPTransferDataLength"; +NSString * TPTransferPortKey = @"TPTransferPort"; +NSString * TPTransferShouldEncryptKey = @"TPTransferShouldEncrypt"; + +@interface TPTransfer (Internal) + +@property (nonatomic, readonly) BOOL _listen; + +- (void)_beginTransfer; + +- (void)_receivedData:(NSData*)data; + +@end + +@implementation TPTransfer + +- (instancetype) init +{ + self = [super init]; + + _data = nil; + _manager = nil; + _socket = nil; + _listenSocket = nil; + _lock = [[NSLock alloc] init]; + + return self; +} + +- (void)dealloc +{ + [_socket setDelegate:nil]; +} + + +#pragma mark - +#pragma mark Attributes + +- (NSString*)type +{ + return @"TPIncomingTransfer"; +} + +- (NSString*)uid +{ + return _uid; +} + +- (TPTransferPriority)priority +{ + return TPTransferLowPriority; +} + +//- (void)setDelegate:(id)delegate +//{ +// _delegate = delegate; +//} +// +//- (id)delegate +//{ +// return _delegate; +//} + +- (void)setManager:(TPTransfersManager*)manager +{ + _manager = manager; +} + +- (BOOL)isIncoming +{ + return NO; +} + +- (BOOL)hasFeedback +{ + return NO; +} + +- (NSString*)completionMessage +{ + return nil; +} + +- (NSString*)errorMessage +{ + return nil; +} + + +#pragma mark - +#pragma mark Status + +- (void)_beginTransfer +{ +} + +- (void)_notifyProgress:(NSNumber*)transferedNumber +{ + TPDataLength transferedDataLength = [transferedNumber unsignedLongLongValue]; + float progress = (double)transferedDataLength/(double)_totalDataLength; + [_manager transfer:self didProgress:progress]; +} + +- (void)_transferDone +{ + DebugLog(@"tranfer %@ done", self); + + [_lock lock]; + + [_socket setDelegate:nil]; + [_socket close]; + _socket = nil; + + _data = nil; + + [_lock unlock]; +} + +- (void)_receivedData:(NSData*)data +{ + DebugLog(@"%@ _receivedData %@", self, data); + +} + + +#pragma mark - +#pragma mark TCP Socket delegate + +- (void)tcpSocket:(TPTCPSecureSocket*)listenSocket secureConnectionAccepted:(TPTCPSecureSocket*)childSocket +{ + [self tcpSocket:listenSocket connectionAccepted:childSocket]; +} + +- (void)tcpSocketSecureConnectionSucceeded:(TPTCPSecureSocket*)tcpSocket +{ + [self tcpSocketConnectionSucceeded:tcpSocket]; +} + +- (void)tcpSocketSecureConnectionFailed:(TPTCPSecureSocket*)tcpSocket +{ + [self tcpSocketConnectionFailed:tcpSocket]; +} + +- (void)tcpSocket:(TPTCPSocket*)tcpSocket gotData:(NSData*)data +{ +#if DEBUG_TRANSFERS + DebugLog(@"transfer socket %@ got data (length %d)", tcpSocket, (int)[data length]); +#endif + + [self _receivedData:data]; +} + +- (void)tcpSocketDidSendData:(TPTCPSocket*)tcpSocket +{ +#if DEBUG_TRANSFERS + DebugLog(@"transfer socket %@ did send data on thread %p", tcpSocket, [NSThread currentThread]); +#endif +} + +- (void)tcpSocketConnectionClosed:(TPTCPSocket*)tcpSocket +{ +#if DEBUG_TRANSFERS + DebugLog(@"transfer socket %@ connection closed", tcpSocket); +#endif +} + +- (SecIdentityRef)tcpSecureSocketCopyIdentity:(TPTCPSecureSocket*)tcpSocket +{ + return [[TPConnectionsManager manager] tcpSecureSocketCopyIdentity:tcpSocket]; +} + +- (BOOL)tcpSecureSocketShouldEnableEncryption:(TPTCPSecureSocket*)tcpSocket +{ + return NO; +} + +- (NSString*)description +{ + return [NSString stringWithFormat:@"%@<%p> uid:%@", [self class], self, [self uid]]; +} + +@end + +@implementation TPOutgoingTransfer + ++ (TPOutgoingTransfer*)transfer +{ + TPOutgoingTransfer * transfer = [[self alloc] init]; + transfer->_uid = [[[NSProcessInfo processInfo] globallyUniqueString] copy]; + return transfer; +} + +- (BOOL)shouldBeEncrypted +{ + return NO; +} + +- (BOOL)prepareToSendData +{ +#if DEBUG_TRANSFERS + DebugLog(@"prepareToSendData"); +#endif + + NSData * dataToTransfer = [self dataToTransfer]; + if(dataToTransfer == nil) + return NO; + + + _data = [dataToTransfer mutableCopy]; + _totalDataLength = [_data length]; + + return YES; +} + +- (void)_beginTransfer +{ + if(_totalDataLength > 0) { + [_socket _setRunLoop:NULL]; + [NSThread detachNewThreadSelector:@selector(_sendDataThread) toTarget:self withObject:nil]; + } +} + +- (void)_sendDataThread +{ + @autoreleasepool { + SInt32 ret = kCFRunLoopRunTimedOut; + [_socket _setRunLoop:CFRunLoopGetCurrent()]; + + if(_totalDataLength > 0) { + NSDate * lastDate = [[NSDate alloc] init]; + NSRange sendRange = NSMakeRange(0, MIN(BLOCK_SIZE, _totalDataLength)); + + while(_data != nil && sendRange.length > 0) { + if(-[lastDate timeIntervalSinceNow] >= REFRESH_RATE) { + [self performSelectorOnMainThread:@selector(_notifyProgress:) withObject:[NSNumber numberWithUnsignedLongLong:sendRange.location] waitUntilDone:NO]; + lastDate = [[NSDate alloc] init]; + } + +#if DEBUG_TRANSFERS + DebugLog(@"send data of length %d on thread %p", (int)sendRange.length, [NSThread currentThread]); +#endif + + [_lock lock]; + NSData * sendData = [_data subdataWithRange:sendRange]; + BOOL sent = [_socket sendData:sendData]; + [_lock unlock]; + + if(!sent) { + break; + } + else { + sendRange.location += sendRange.length; + sendRange.length = MIN(BLOCK_SIZE, _totalDataLength - sendRange.location); + } + } + + + if(sendRange.location == _totalDataLength) + [self performSelectorOnMainThread:@selector(_senderDataTransferCompleted) withObject:nil waitUntilDone:YES]; + else + [self performSelectorOnMainThread:@selector(_senderDataTransferAborted) withObject:nil waitUntilDone:YES]; + } + else { + while(_data != (id)[NSNull null]) { + if(_data != nil) { + [_lock lock]; + BOOL sent = [_socket sendData:_data]; + _data = nil; + [_lock unlock]; + + ret = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false); + + if(!sent) { + break; + } + } + } + + if(_data == (id)[NSNull null]) + [self performSelectorOnMainThread:@selector(_senderDataTransferCompleted) withObject:nil waitUntilDone:YES]; + else + [self performSelectorOnMainThread:@selector(_senderDataTransferAborted) withObject:nil waitUntilDone:YES]; + + } + + } +} + +- (void)_senderDataTransferCompleted +{ + [self _transferDone]; + [_manager transferDidComplete:self]; +} + +- (void)_senderDataTransferFailed +{ + [self _transferDone]; + [_manager transferDidCancel:self]; +} + +- (void)_senderDataTransferAborted +{ + [self _transferDone]; + [_manager transferDidCancel:self]; +} + +- (void)setSocket:(TPTCPSocket*)socket +{ + _socket = (TPTCPSecureSocket*)socket; + [_socket setDelegate:self]; +} + +- (NSData*)dataToTransfer +{ + return [NSData data]; +} + +- (TPDataLength)totalDataLength +{ + return _totalDataLength; +} + +- (NSDictionary*)infoDict +{ + return @{TPTransferUIDKey: [self uid], + TPTransferTypeKey: [self type], + TPTransferDataLengthKey: @([self totalDataLength]), + TPTransferShouldEncryptKey: @([self shouldBeEncrypted])}; +} + + +#pragma mark - +#pragma mark Socket delegate + +- (BOOL)tcpSecureSocketShouldEnableEncryption:(TPTCPSecureSocket*)tcpSocket +{ + return [self shouldBeEncrypted] && [[TPConnectionsManager manager] tcpSecureSocketShouldEnableEncryption:tcpSocket]; +} + +- (void)tcpSocketConnectionSucceeded:(TPTCPSocket*)tcpSocket +{ + DebugLog(@"transfer %@ socket %@ succeeded to %@", self, tcpSocket, [_socket remoteAddress]); + + [_manager transferDidStart:self]; + [self _beginTransfer]; +} + +- (void)tcpSocketConnectionFailed:(TPTCPSocket*)tcpSocket +{ + DebugLog(@"transfer %@ socket %@ connection failed", self, tcpSocket); + + [self _senderDataTransferFailed]; +} + +@end + +#pragma mark - + +@implementation TPIncomingTransfer + ++ (TPIncomingTransfer*)transferOfType:(NSString*)type withUID:(NSString*)uid +{ + Class transferClass = NSClassFromString(type); + if(transferClass == Nil) { + return nil; + } + + TPIncomingTransfer * transfer = [[transferClass alloc] init]; + transfer->_uid = uid; + return transfer; +} + +- (void)dealloc +{ + [self _stopListening]; +} + +- (BOOL)_listenOnPort:(int*)port forHost:(TPRemoteHost*)host encrypt:(BOOL)shouldEncrypt +{ + if(_listenSocket == nil) { + _listenSocket = [[TPTCPSecureSocket alloc] initWithDelegate:self]; + + BOOL enableEncryption = shouldEncrypt && [[TPLocalHost localHost] pairWithHost:host hasCapability:TPHostEncryptionCapability]; + [_listenSocket setEnableEncryption:enableEncryption]; + + return [_listenSocket listenOnPort:port tries:50]; + } + else + return YES; +} + +- (void)_stopListening +{ + if(_listenSocket != nil) { + [_listenSocket setDelegate:nil]; + [_listenSocket close]; + _listenSocket = nil; + } +} + +- (BOOL)isIncoming +{ + return YES; +} + +- (BOOL)requireTrustedHost +{ + return NO; +} + +- (BOOL)prepareToReceiveDataWithInfoDict:(NSDictionary*)infoDict fromHost:(TPRemoteHost*)host onPort:(int*)port delegate:(id)delegate +{ +#if DEBUG_TRANSFERS + DebugLog(@"prepareToReceiveData"); +#endif + + _totalDataLength = [infoDict[TPTransferDataLengthKey] longLongValue]; + _shouldEncrypt = [infoDict[TPTransferShouldEncryptKey] boolValue]; + + /* Setup buffer for data transfer */ + if(_totalDataLength > 0) { + _data = [[NSMutableData alloc] initWithCapacity:_totalDataLength]; + [_data setLength:_totalDataLength]; + } + + /* Listen to sender */ + return [self _listenOnPort:port forHost:host encrypt:_shouldEncrypt]; +} + +- (void)_beginTransfer +{ + [_socket _setRunLoop:NULL]; // prevents it from receiving stuff while the thread starts + + //if(_totalDataLength > 0) { + [NSThread detachNewThreadSelector:@selector(_receiveDataThread) toTarget:self withObject:nil]; + //} +} + +- (void)_notifyProgress:(NSNumber*)transferedNumber +{ + [super _notifyProgress:transferedNumber]; + + TPDataLength transferedDataLength = [transferedNumber unsignedLongLongValue]; + float progress = (double)transferedDataLength/(double)_totalDataLength; + [self _transferDidProgress:progress]; +} + +- (void)_transferDidProgress:(float)progress +{ +} + +- (void)_receivedData:(NSData*)data +{ +#if DEBUG_TRANSFERS + DebugLog(@"tranfer %@ receivedData (%d) in thread %@", self, (int)[data length], [NSThread currentThread]); +#endif + + unsigned dataLength = [data length]; + [_lock lock]; + [_data replaceBytesInRange:NSMakeRange(_receivedDataLength, dataLength) withBytes:[data bytes] length:dataLength]; + [_lock unlock]; + _receivedDataLength += dataLength; +} + +- (void)_receiveDataThread +{ + @autoreleasepool { + SInt32 ret = kCFRunLoopRunTimedOut; + + [_socket _setRunLoop:CFRunLoopGetCurrent()]; + + if(_totalDataLength > 0) { + _receivedDataLength = 0; + + while(ret != kCFRunLoopRunFinished && _data != nil && _receivedDataLength < _totalDataLength) { + @autoreleasepool { + + // DebugLog(@"running runloop %@ in mode %@", CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + ret = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false); + + NSDate * lastDate = [[NSDate alloc] init]; + if(-[lastDate timeIntervalSinceNow] >= REFRESH_RATE) { + [self performSelectorOnMainThread:@selector(_notifyProgress:) withObject:@(_receivedDataLength) waitUntilDone:NO]; + lastDate = [[NSDate alloc] init]; + } + } + } + + [_socket _setRunLoop:NULL]; + + if(_receivedDataLength == _totalDataLength) + [self performSelectorOnMainThread:@selector(_receiverDataTransferCompleted) withObject:nil waitUntilDone:YES]; + else + [self performSelectorOnMainThread:@selector(_receiverDataTransferAborted) withObject:nil waitUntilDone:YES]; + } + else { + while(ret != kCFRunLoopRunFinished && _totalDataLength != -1) { + @autoreleasepool { + + ret = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false); + //NSLog(@"run"); + } + } + + [_socket _setRunLoop:NULL]; + + [self performSelectorOnMainThread:@selector(_receiverDataTransferCompleted) withObject:nil waitUntilDone:YES]; + + } + + } +} + +- (void)_receiverDataTransferCompleted +{ + [self _transferDone]; + [_manager transferDidComplete:self]; +} + +- (void)_receiverDataTransferFailed +{ + [self _transferDone]; + [_manager transferDidCancel:self]; +} + +- (void)_receiverDataTransferAborted +{ + [self _transferDone]; + [_manager transferDidCancel:self]; +} + + +#pragma mark - +#pragma mark Socket delegate + +- (BOOL)tcpSecureSocketShouldEnableEncryption:(TPTCPSecureSocket*)tcpSocket +{ + return _shouldEncrypt && [[TPConnectionsManager manager] tcpSecureSocketShouldEnableEncryption:tcpSocket]; +} + +- (void)tcpSocket:(TPTCPSocket*)listenSocket connectionAccepted:(TPTCPSocket*)childSocket +{ + DebugLog(@"transfer %@ socket accepted from %@ on %@", self, listenSocket, childSocket); + + if(_socket != nil) { + [_socket close]; + } + _socket = (TPTCPSecureSocket*)childSocket; + [_socket setNoDelay:NO]; + [_socket setDelegate:self]; + + [self _stopListening]; + + [_manager transferDidStart:self]; + + [self _beginTransfer]; +} + +- (void)tcpSocketConnectionFailed:(TPTCPSocket*)tcpSocket +{ + DebugLog(@"transfer %@ socket %@ connection failed", self, tcpSocket); + + [self _receiverDataTransferFailed]; +} + +- (void)tcpSocketConnectionClosed:(TPTCPSocket*)tcpSocket +{ + if(_totalDataLength == 0) { + _totalDataLength = -1; +// [self _receiverDataTransferCompleted]; + } +} + +@end diff --git a/TPTransfer_Private.h b/TPTransfer_Private.h new file mode 100644 index 0000000..337f735 --- /dev/null +++ b/TPTransfer_Private.h @@ -0,0 +1,27 @@ +/* + * TPTransfer_Private.h + * teleport + * + * Created by JuL on 19/04/06. + * Copyright 2006 abyssoft. All rights reserved. + * + */ + +#import "TPTransfer.h" + +@interface TPTransfer (Private) + +- (void)_sendData; + +- (void)_transferDidProgress:(float)progress; +- (void)_transferDone; + +- (void)_senderDataTransferCompleted; +- (void)_senderDataTransferFailed; +- (void)_senderDataTransferAborted; + +- (void)_receiverDataTransferCompleted; +- (void)_receiverDataTransferFailed; +- (void)_receiverDataTransferAborted; + +@end diff --git a/TPTransfersManager.h b/TPTransfersManager.h new file mode 100644 index 0000000..a1cf6a5 --- /dev/null +++ b/TPTransfersManager.h @@ -0,0 +1,56 @@ +// +// TPTransfersManager.h +// teleport +// +// Created by JuL on 26/07/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import + +@class TPTCPSocket; +@class TPNetworkConnection; +@class TPTransfer; +@class TPOutgoingTransfer; + +@interface TPTransfersManager : NSObject +{ + NSMutableDictionary * _outgoingTransfers; + NSMutableDictionary * _incomingTransfers; + + NSMutableDictionary * _outgoingConnections; + NSMutableArray * _transferRequests; + + BOOL _requestInProcess; + BOOL _lastTransferWasCancelled; + + id _delegate; +} + ++ (TPTransfersManager*)manager; + +- (void)setDelegate:(id)delegate; + +- (void)beginTransfer:(TPOutgoingTransfer*)transfer usingConnection:(TPNetworkConnection*)connection; +- (void)startTransferWithUID:(NSString*)transferUID usingConnection:(TPNetworkConnection*)connection onPort:(int)port; +- (void)abortTransferWithUID:(NSString*)transferUID; + +- (void)abortAllTransferRequests; + +- (void)receiveTransferRequestWithInfoDict:(NSDictionary*)infoDict onConnection:(TPNetworkConnection*)connection isClient:(BOOL)isClient; + +- (void)transferDidStart:(TPTransfer*)transfer; +- (void)transfer:(TPTransfer*)transfer didProgress:(float)progress; +- (void)transferDidComplete:(TPTransfer*)transfer; +- (void)transferDidCancel:(TPTransfer*)transfer; + +@end + +@interface NSObject (TPTransfersManager_delegate) + +- (void)transfersManager:(TPTransfersManager*)transfersManager beginNewTransfer:(TPTransfer*)transfer; +- (void)transfersManager:(TPTransfersManager*)transfersManager transfer:(TPTransfer*)transfer didProgress:(float)progress; +- (void)transfersManager:(TPTransfersManager*)transfersManager completeTransfer:(TPTransfer*)transfer; +- (void)transfersManager:(TPTransfersManager*)transfersManager cancelTransfer:(TPTransfer*)transfer; + +@end \ No newline at end of file diff --git a/TPTransfersManager.m b/TPTransfersManager.m new file mode 100644 index 0000000..386049a --- /dev/null +++ b/TPTransfersManager.m @@ -0,0 +1,275 @@ +// +// TPTransfersManager.m +// teleport +// +// Created by JuL on 26/07/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import "TPTransfersManager.h" + +#import "TPTCPSecureSocket.h" +#import "TPTransfer.h" +#import "TPMessage.h" +#import "TPLocalHost.h" +#import "TPRemoteHost.h" +#import "TPNetworkConnection.h" +#import "TPPreferencesManager.h" +#import "TPAuthenticationManager.h" + +static NSString * TPTransferRequestTransferKey = @"TPTransferRequestTransfer"; +static NSString * TPTransferRequestInfoDictKey = @"TPTransferRequestInfoDict"; +static NSString * TPTransferRequestConnectionKey = @"TPTransferRequestConnection"; + +static TPTransfersManager * _transfersManager = nil; + +@interface TPTransfersManager (Internal) + +- (void)_processNextTransferRequestIfPossible; + +@end + +@implementation TPTransfersManager + ++ (TPTransfersManager*)manager +{ + if(_transfersManager == nil) + _transfersManager = [[TPTransfersManager alloc] init]; + return _transfersManager; +} + +- (instancetype) init +{ + self = [super init]; + + _outgoingTransfers = [[NSMutableDictionary alloc] init]; + _incomingTransfers = [[NSMutableDictionary alloc] init]; + _outgoingConnections = [[NSMutableDictionary alloc] init]; + _transferRequests = [[NSMutableArray alloc] init]; + _lastTransferWasCancelled = NO; + _requestInProcess = NO; + + return self; +} + + +- (void)setDelegate:(id)delegate +{ + _delegate = delegate; +} + +- (void)beginTransfer:(TPOutgoingTransfer*)transfer usingConnection:(TPNetworkConnection*)connection +{ + BOOL transferIsOK = [transfer prepareToSendData]; + _lastTransferWasCancelled = NO; + + [transfer setManager:self]; + _outgoingTransfers[[transfer uid]] = transfer; + + if(!transferIsOK) { + DebugLog(@"cancelled transfer %@ because there's no data", transfer); + [self transferDidCancel:transfer]; + } + else { + DebugLog(@"begin transfer %@", transfer); + + NSDictionary * infoDict = [transfer infoDict]; + TPMessage * message = [TPMessage messageWithType:TPTransferRequestMsgType + andInfoDict:infoDict]; + + _outgoingConnections[[transfer uid]] = connection; // just to retain the connection until the transfer link is established + + if(![connection sendMessage:message]) { + DebugLog(@"can't send transfer request for %@", transfer); + [self transferDidCancel:transfer]; + } + } +} + +- (void)startTransferWithUID:(NSString*)transferUID usingConnection:(TPNetworkConnection*)connection onPort:(int)port +{ + TPOutgoingTransfer * transfer = _outgoingTransfers[transferUID]; + + DebugLog(@"start transfer %@", transfer); + + if(transfer != nil) { + if(_delegate && [_delegate respondsToSelector:@selector(transfersManager:beginNewTransfer:)]) + [_delegate transfersManager:self beginNewTransfer:transfer]; + + TPRemoteHost * host = [connection connectedHost]; + BOOL enableEncryption = [transfer shouldBeEncrypted] && [[TPLocalHost localHost] pairWithHost:host hasCapability:TPHostEncryptionCapability]; + TPTCPSecureSocket * socket = [[TPTCPSecureSocket alloc] initWithDelegate:transfer]; + + [socket setEnableEncryption:enableEncryption]; + [socket setNoDelay:NO]; + [socket connectToHost:[host address] onPort:port]; + + [transfer setSocket:socket]; + } +} + +- (void)abortTransferWithUID:(NSString*)transferUID +{ + DebugLog(@"transfer with UID %@ aborted", transferUID); + [_outgoingConnections removeObjectForKey:transferUID]; + [_outgoingTransfers removeObjectForKey:transferUID]; + [_incomingTransfers removeObjectForKey:transferUID]; +} + +- (void)receiveTransferRequestWithInfoDict:(NSDictionary*)infoDict onConnection:(TPNetworkConnection*)connection isClient:(BOOL)isClient +{ + NSString * transferType = infoDict[TPTransferTypeKey]; + NSString * uid = infoDict[TPTransferUIDKey]; + NSNumber * dataLengthValue = infoDict[TPTransferDataLengthKey]; + TPDataLength dataLength = [dataLengthValue longLongValue]; + + DebugLog(@"receive transfer of type %@ and length %lld", transferType, dataLength); + + TPIncomingTransfer * transfer = [TPIncomingTransfer transferOfType:transferType withUID:uid]; + + if(transfer != nil) { + if(!isClient && [transfer requireTrustedHost] && ![[TPAuthenticationManager defaultManager] isHostTrusted:[connection connectedHost]]) { + DebugLog(@"transfer requires trusted host but this one is not!"); + [connection sendMessage:[TPMessage messageWithType:TPTransferFailureMsgType + andString:[transfer uid]]]; + } +// else if(_lastTransferWasCancelled) { +// DebugLog(@"last transfer was cancelled: do not accept to receive this!"); +// +// [connection sendMessage:[TPMessage messageWithType:TPTransferFailureMsgType +// andString:[transfer uid]]]; +// } + else { + NSDictionary * transferRequest = [[NSDictionary alloc] initWithObjectsAndKeys: + transfer, TPTransferRequestTransferKey, + infoDict, TPTransferRequestInfoDictKey, + connection, TPTransferRequestConnectionKey, + nil]; + + [_transferRequests addObject:transferRequest]; + + [self _processNextTransferRequestIfPossible]; + } + } + else + DebugLog(@"error creating transfer of type <%@>", transferType); +} + +- (void)abortAllTransferRequests +{ + NSEnumerator * transferRequestsEnum = [_transferRequests objectEnumerator]; + NSDictionary * transferRequest; + + while((transferRequest = [transferRequestsEnum nextObject]) != nil) { + TPTransfer * transfer = transferRequest[TPTransferRequestTransferKey]; + [self abortTransferWithUID:[transfer uid]]; + } + + [_transferRequests removeAllObjects]; + _requestInProcess = NO; +} + +- (void)_processNextTransferRequestIfPossible +{ + DebugLog(@"processing next transfer request in %@", _transferRequests); + + if([_transferRequests count] < 1 || _requestInProcess) + return; + + DebugLog(@"process now"); + + _requestInProcess = YES; + + NSDictionary * transferRequest = _transferRequests[0]; + TPIncomingTransfer * transfer = transferRequest[TPTransferRequestTransferKey]; + NSDictionary * infoDict = transferRequest[TPTransferRequestInfoDictKey]; + TPNetworkConnection * connection = transferRequest[TPTransferRequestConnectionKey]; + TPRemoteHost * host = [connection connectedHost]; + int port = [[TPPreferencesManager sharedPreferencesManager] portForPref:TRANSFER_PORT]; + + if([transfer prepareToReceiveDataWithInfoDict:infoDict fromHost:host onPort:&port delegate:[connection delegate]]) { + if(_delegate && [_delegate respondsToSelector:@selector(transfersManager:beginNewTransfer:)]) + [_delegate transfersManager:self beginNewTransfer:transfer]; + + [transfer setManager:self]; + _incomingTransfers[[transfer uid]] = transfer; + + DebugLog(@"receive transfer %@", transfer); + + BOOL sent = [connection sendMessage:[TPMessage messageWithType:TPTransferSuccessMsgType + andInfoDict:@{TPTransferUIDKey: [transfer uid], + TPTransferPortKey: @(port)}]]; + + if(!sent) { + NSLog(@"can't send transfer start message %@", transfer); + _requestInProcess = NO; + [self _processNextTransferRequestIfPossible]; + } + + } + else { + DebugLog(@"prepare failed for transfer %@", transfer); + + [connection sendMessage:[TPMessage messageWithType:TPTransferFailureMsgType + andString:[transfer uid]]]; + + _requestInProcess = NO; + [self _processNextTransferRequestIfPossible]; + } +} + +- (void)_removeTransfer:(TPTransfer*)transfer +{ + NSString * transferUID = [transfer uid]; + [_outgoingTransfers removeObjectForKey:transferUID]; + [_incomingTransfers removeObjectForKey:transferUID]; + [_outgoingConnections removeObjectForKey:transferUID]; +} + +- (void)transferDidStart:(TPTransfer*)transfer +{ + DebugLog(@"transfer %@ did start", transfer); + + if([transfer isIncoming]) { + if(_requestInProcess && [_transferRequests count] > 0) { + [_transferRequests removeObjectAtIndex:0]; + _requestInProcess = NO; + [self _processNextTransferRequestIfPossible]; + } + } + else { + [_outgoingConnections removeObjectForKey:[transfer uid]]; + } +} + +- (void)transfer:(TPTransfer*)transfer didProgress:(float)progress +{ + DebugLog(@"transfer %@ did progress to %f", transfer, progress); + + if(_delegate && [_delegate respondsToSelector:@selector(transfersManager:transfer:didProgress:)] && [transfer hasFeedback]) + [_delegate transfersManager:self transfer:transfer didProgress:progress]; +} + +- (void)transferDidComplete:(TPTransfer*)transfer +{ + DebugLog(@"transfer %@ did complete", transfer); + + if(_delegate && [_delegate respondsToSelector:@selector(transfersManager:completeTransfer:)] && [transfer hasFeedback]) + [_delegate transfersManager:self completeTransfer:transfer]; + + [self _removeTransfer:transfer]; +} + +- (void)transferDidCancel:(TPTransfer*)transfer +{ + DebugLog(@"transfer %@ did cancel", transfer); + + if(_delegate && [_delegate respondsToSelector:@selector(transfersManager:cancelTransfer:)] && [transfer hasFeedback]) + [_delegate transfersManager:self cancelTransfer:transfer]; + + [self _removeTransfer:transfer]; + + _lastTransferWasCancelled = YES; +} + +@end diff --git a/TPUDPSocket.h b/TPUDPSocket.h new file mode 100644 index 0000000..69b213d --- /dev/null +++ b/TPUDPSocket.h @@ -0,0 +1,33 @@ +// +// TPUDPSocket.h +// teleport +// +// Created by JuL on Sat Jan 03 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import +#include + +@interface TPUDPSocket : NSObject +{ + id delegate; + BOOL connected; + CFSocketRef cfSocket; +} + ++ (TPUDPSocket*)udpSocketWithPort:(int)port; +- initWithPort:(int)port; + +- (void)setDelegate:(id)delegate; + +- (BOOL)sendData:(NSData*)data to:(CFDataRef)address; +- (void)close; + +@end + +@interface NSObject (TPUDPSocket_delegate) + +- (void)udpSocket:(TPUDPSocket*)udpSocket gotData:(NSData*)data from:(CFDataRef)address; + +@end diff --git a/TPUDPSocket.m b/TPUDPSocket.m new file mode 100644 index 0000000..c874cb6 --- /dev/null +++ b/TPUDPSocket.m @@ -0,0 +1,124 @@ +// +// TPUDPSocket.m +// teleport +// +// Created by JuL on Sat Jan 03 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPUDPSocket.h" + +#import +#include +#include +#include +#include + +static void _cfsocketCallback(CFSocketRef inCFSocketRef, CFSocketCallBackType inType, CFDataRef inAddress, const void* inData, void* inContext); + +@interface TPUDPSocket (Private) + +- (void)_receivedData:(CFDataRef)data from:(CFDataRef)inAddress; + +@end + +@implementation TPUDPSocket + ++ (TPUDPSocket*)udpSocketWithPort:(int)port +{ + TPUDPSocket * udpSocket = [[TPUDPSocket alloc] initWithPort:port]; + return [udpSocket autorelease]; +} + +- initWithPort:(int)port +{ + self = [super init]; + + /* Setup context */ + CFSocketContext socketContext; + bzero(&socketContext, sizeof(socketContext)); + socketContext.info = self; + + /* Setup struct sockaddr */ + struct sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + + /* Create socket */ + CFDataRef d = CFDataCreate(NULL, (UInt8 *)&address, sizeof(struct sockaddr_in)); + CFSocketSignature signature = {PF_INET, SOCK_DGRAM, IPPROTO_UDP, d}; + cfSocket = CFSocketCreateWithSocketSignature(kCFAllocatorDefault, + &signature, + kCFSocketDataCallBack, + &_cfsocketCallback, + &socketContext); + + /* Add to the run loop */ + CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes); + + return self; +} + +- (void)setDelegate:(id)pDelegate +{ + delegate = pDelegate; +} + +- (BOOL)sendData:(NSData*)data to:(CFDataRef)address +{ + return (CFSocketSendData(cfSocket, [host cfAddress], (CFDataRef)data, -1) == kCFSocketSuccess); +} + +- (void)close +{ + CFSocketInvalidate(cfSocket); + CFRelease(cfSocket); +} + +- (void)_receivedData:(CFDataRef)data from:(CFDataRef)inAddress +{ + if([delegate respondsToSelector:@selector(udpSocket:gotData:from:)]) + [delegate udpSocket:self gotData:(NSData*)data from:inAddress]; +} + +- (NSString*)remoteAddress +{ + CFSocketNativeHandle nativeSocket; + struct sockaddr_in address; + int addressLength = sizeof(address); + + nativeSocket = CFSocketGetNative(cfSocket); + if(nativeSocket < 0) + return nil; + + if(getpeername(nativeSocket, (struct sockaddr*)&address, &addressLength) < 0) + return nil; + + char * cIp = inet_ntoa(address.sin_addr); + return [NSString stringWithCString:cIp]; +} + + +@end + +void +_cfsocketCallback( CFSocketRef inCFSocketRef, CFSocketCallBackType inType, CFDataRef inAddress, const void* inData, void* inContext ) +{ + TPUDPSocket * udpSocket; + + udpSocket = (TPUDPSocket*)inContext; + if(!udpSocket) + return; + + switch(inType) + { + case kCFSocketDataCallBack: + [udpSocket _receivedData:(CFDataRef)inData from:inAddress]; + break; + + default: + break; + } +} diff --git a/TPUtils.h b/TPUtils.h new file mode 100644 index 0000000..2300819 --- /dev/null +++ b/TPUtils.h @@ -0,0 +1,59 @@ +// +// TPUtils.h +// teleport +// +// Created by Julien Robert on 11/10/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import + +@interface NSData (TPUtils) + +- (void)_readBytes:(void*)bytes withSize:(unsigned int)size atPos:(int*)pos; + ++ (NSData*)dataWithRect:(NSRect)rect; +@property (nonatomic, readonly) NSRect rectValue; + ++ (NSData*)dataWithPoint:(NSPoint)point; +@property (nonatomic, readonly) NSPoint pointValue; + ++ (NSData*)dataWithSize:(NSSize)size; +@property (nonatomic, readonly) NSSize sizeValue; + ++ (NSData*)dataWithString:(NSString*)string; +@property (nonatomic, readonly, copy) NSString *stringValue; + ++ (NSData*)dataWithInt:(int)i; +@property (nonatomic, readonly) int intValue; + ++ (NSData*)dataWithBool:(BOOL)b; +@property (nonatomic, readonly) BOOL boolValue; + +@end + +@interface NSString (TPAdditions) + ++ (NSString*)sizeStringForSize:(TPDataLength)size; + ++ (NSString*)stringWithInt:(int)i; ++ (NSString*)stringWithBool:(BOOL)b; +@property (nonatomic, readonly, copy) NSString *stringValue; +@property (nonatomic, readonly) BOOL boolValue; + +@end + +@interface NSWorkspace (TPAdditions) + +- (NSDictionary*)typeDictForPath:(NSString*)path; + +@property (nonatomic, readonly, copy) NSString *computerIdentifier; + +@end + + +@interface NSFileManager (TPAdditions) + +- (BOOL)isTotalSizeOfItemAtPath:(NSString*)path smallerThan:(unsigned long long)maxSize; + +@end diff --git a/TPUtils.m b/TPUtils.m new file mode 100644 index 0000000..72a5d79 --- /dev/null +++ b/TPUtils.m @@ -0,0 +1,350 @@ +// +// TPUtils.m +// teleport +// +// Created by Julien Robert on 11/10/05. +// Copyright 2005 abyssoft. All rights reserved. +// + +#import "TPUtils.h" + +#include + +#include +#include +#include +#include + +#import + +@interface NSObject (AdminPrivate) + ++ (BOOL)createFileWithContents:(NSData *)inData path:(NSString *)inPath attributes:(NSDictionary *)inAttributes; ++ (BOOL)removeFileAtPath:(NSString *)inPath; + ++ (id)sharedAuthenticator; +- (BOOL)authenticateUsingAuthorizationSync:(SFAuthorization*)auth; + +@end + +@implementation NSData (TPUtils) + +- (void)_readBytes:(void*)bytes withSize:(unsigned int)size atPos:(int*)pos +{ + [self getBytes:bytes range:NSMakeRange(*pos, size)]; + *pos += size; +} + ++ (NSData*)dataWithRect:(NSRect)rect +{ + NSMutableData * data = [[NSMutableData alloc] init]; + + [data appendData:[self dataWithPoint:rect.origin]]; + [data appendData:[self dataWithSize:rect.size]]; + + return data; +} + +- (NSRect)rectValue +{ + int pos = 0; + + NSSwappedFloat originX; + NSSwappedFloat originY; + NSSwappedFloat sizeWidth; + NSSwappedFloat sizeHeight; + + [self _readBytes:&originX withSize:sizeof(NSSwappedFloat) atPos:&pos]; + [self _readBytes:&originY withSize:sizeof(NSSwappedFloat) atPos:&pos]; + [self _readBytes:&sizeWidth withSize:sizeof(NSSwappedFloat) atPos:&pos]; + [self _readBytes:&sizeHeight withSize:sizeof(NSSwappedFloat) atPos:&pos]; + + return NSMakeRect(NSSwapBigFloatToHost(originX), NSSwapBigFloatToHost(originY), NSSwapBigFloatToHost(sizeWidth), NSSwapBigFloatToHost(sizeHeight)); +} + ++ (NSData*)dataWithPoint:(NSPoint)point +{ + NSMutableData * data = [[NSMutableData alloc] init]; + + NSSwappedFloat x = NSSwapHostFloatToBig(point.x); + NSSwappedFloat y = NSSwapHostFloatToBig(point.y); + + [data appendData:[NSData dataWithBytes:&x length:sizeof(NSSwappedFloat)]]; + [data appendData:[NSData dataWithBytes:&y length:sizeof(NSSwappedFloat)]]; + + return data; +} + +- (NSPoint)pointValue +{ + int pos = 0; + + NSSwappedFloat x; + NSSwappedFloat y; + + [self _readBytes:&x withSize:sizeof(NSSwappedFloat) atPos:&pos]; + [self _readBytes:&y withSize:sizeof(NSSwappedFloat) atPos:&pos]; + + return NSMakePoint(NSSwapBigFloatToHost(x), NSSwapBigFloatToHost(y)); +} + ++ (NSData*)dataWithSize:(NSSize)size +{ + NSMutableData * data = [[NSMutableData alloc] init]; + + NSSwappedFloat width = NSSwapHostFloatToBig(size.width); + NSSwappedFloat height = NSSwapHostFloatToBig(size.height); + + [data appendData:[NSData dataWithBytes:&width length:sizeof(NSSwappedFloat)]]; + [data appendData:[NSData dataWithBytes:&height length:sizeof(NSSwappedFloat)]]; + + return data; +} + +- (NSSize)sizeValue +{ + int pos = 0; + + NSSwappedFloat width; + NSSwappedFloat height; + + [self _readBytes:&width withSize:sizeof(NSSwappedFloat) atPos:&pos]; + [self _readBytes:&height withSize:sizeof(NSSwappedFloat) atPos:&pos]; + + return NSMakeSize(NSSwapBigFloatToHost(width), NSSwapBigFloatToHost(height)); +} + ++ (NSData*)dataWithString:(NSString*)string +{ + return [string dataUsingEncoding:NSUTF8StringEncoding]; +} + +- (NSString*)stringValue +{ + NSString * string = [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding]; + return string; +} + ++ (NSData*)dataWithInt:(int)i +{ + return [self dataWithString:[NSString stringWithInt:i]]; +} + +- (int)intValue +{ + return [[self stringValue] intValue]; +} + ++ (NSData*)dataWithBool:(BOOL)b +{ + return [self dataWithString:[NSString stringWithBool:b]]; +} + +- (BOOL)boolValue +{ + return [[self stringValue] boolValue]; +} + +@end + +@implementation NSString (TPAdditions) + +#define KILO (1024) +#define MEGA (KILO*KILO) +#define GIGA (KILO*MEGA) + ++ (NSString*)sizeStringForSize:(TPDataLength)size +{ + if(size == 0) + return NSLocalizedString(@"zero", nil); + + double s; + NSString * sizeString; + + if(size >= GIGA) { + s = (10*(double)size)/GIGA; + sizeString = [NSString stringWithFormat:NSLocalizedString(@"%.1fG", nil), s/10]; + } + else if(size >= MEGA) { + s = (10*(double)size)/MEGA; + sizeString = [NSString stringWithFormat:NSLocalizedString(@"%.1fM", nil), s/10]; + } + else if(size >= KILO) { + s = (10*(double)size)/KILO; + sizeString = [NSString stringWithFormat:NSLocalizedString(@"%.1fK", nil), s/10]; + } + else + sizeString = NSLocalizedString(@"< 1K", nil); + + return sizeString; +} + ++ (NSString*)stringWithInt:(int)i +{ + return [NSString stringWithFormat:@"%d", i]; +} + ++ (NSString*)stringWithBool:(BOOL)b +{ + return b?@"true":@"false"; +} + +- (NSString*)stringValue +{ + return self; +} + +- (BOOL)boolValue +{ + return [[self lowercaseString] isEqualToString:@"true"]; +} + +@end + +@implementation NSWorkspace (TPAdditions) + +- (NSDictionary*)typeDictForPath:(NSString*)path +{ + char fspath[PATH_MAX]; + FSRef fsRef; + LSItemInfoRecord itemInfo; + Boolean isDirectory; + + if(path == nil) + return nil; + + if(!CFStringGetFileSystemRepresentation((CFStringRef)path, fspath, PATH_MAX)) + return nil; + + FSPathMakeRef((const UInt8 *)fspath, &fsRef, &isDirectory); + LSCopyItemInfoForRef(&fsRef, kLSRequestAllInfo, &itemInfo); + + if(isDirectory) { + if(itemInfo.flags & kLSItemInfoIsApplication) + itemInfo.filetype = kGenericApplicationIcon; + else if(itemInfo.flags & kLSItemInfoIsPackage) + itemInfo.filetype = kGenericComponentIcon; + else if(itemInfo.flags & kLSItemInfoIsVolume) + itemInfo.filetype = kGenericRemovableMediaIcon; + else + itemInfo.filetype = kGenericFolderIcon; + } + + NSMutableDictionary * typeDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedLong:itemInfo.filetype], NSFileHFSTypeCode, + [NSNumber numberWithUnsignedLong:itemInfo.creator], NSFileHFSCreatorCode, + nil]; + + if(itemInfo.extension != NULL) { + typeDict[NSFileType] = (__bridge NSString*)itemInfo.extension; + CFRelease(itemInfo.extension); + } + + return typeDict; +} + +#define MAX_NUMBER 5 +- (NSString*)computerIdentifier +{ + NSString * computerIdentifier = nil; + kern_return_t kernResult = KERN_FAILURE; + + NSString * const interfaceNameFormat = @"en%d"; + int interfaceNumber = 0; + + while((computerIdentifier == nil) && (interfaceNumber < MAX_NUMBER)) { + NSString * interfaceName = [NSString stringWithFormat:interfaceNameFormat, interfaceNumber]; + const char * interfaceNameString = NULL; + +#if LEGACY_BUILD + interfaceNameString = [interfaceName cString]; +#else + interfaceNameString = [interfaceName cStringUsingEncoding:NSASCIIStringEncoding]; +#endif + + CFMutableDictionaryRef matchingDict = IOBSDNameMatching(kIOMasterPortDefault, 0, interfaceNameString); + if(matchingDict == NULL) { + continue; + } + + io_iterator_t iterator; + io_object_t service; + + kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iterator); + + if(kernResult != KERN_SUCCESS) { + continue; + } + + while((service = IOIteratorNext(iterator)) != 0) { + io_object_t controllerService; + + kernResult = IORegistryEntryGetParentEntry(service, kIOServicePlane, &controllerService); + + if(kernResult != KERN_SUCCESS) { + break; + } + else { + CFTypeRef MACData = IORegistryEntryCreateCFProperty(controllerService, CFSTR(kIOMACAddress), kCFAllocatorDefault, 0); + if(MACData != NULL) { + UInt8 MACAddress[kIOEthernetAddressSize]; + CFDataGetBytes(MACData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress); + CFRelease(MACData); + + computerIdentifier = [NSString stringWithFormat:@"%02x-%02x-%02x-%02x-%02x-%02x", MACAddress[0], MACAddress[1], MACAddress[2], MACAddress[3], MACAddress[4], MACAddress[5]]; + } + + IOObjectRelease(controllerService); + } + + IOObjectRelease(service); + } + + interfaceNumber++; + } + + return computerIdentifier; +} + +- (void)_executeThreadedScript:(NSAppleScript*)script +{ + @autoreleasepool { + [script executeAndReturnError:NULL]; + } +} + +@end + +@implementation NSFileManager (TPAdditions) + +- (BOOL)isTotalSizeOfItemAtPath:(NSString*)path smallerThan:(unsigned long long)maxSize +{ + DebugLog(@"checking that size of %@ is smaller than %lld", path, maxSize); + BOOL directory; + if([self fileExistsAtPath:path isDirectory:&directory]) { + if(directory) { + unsigned long long folderSize = 0; + NSDirectoryEnumerator * dirEnum = [self enumeratorAtPath:path]; + + NSString * file; + while((file = [dirEnum nextObject]) != nil) { + NSDictionary * attributes = [dirEnum fileAttributes]; + if([[attributes fileType] isEqualToString:NSFileTypeRegular]) { + folderSize += [attributes fileSize]; + if(folderSize > maxSize) + return NO; + } + } + + return YES; + } + else { + NSDictionary * attributes = [self fileAttributesAtPath:path traverseLink:NO]; + return ([attributes fileSize] <= maxSize); + } + } + else + return NO; +} + +@end diff --git a/TPVersionTextField.h b/TPVersionTextField.h new file mode 100644 index 0000000..737b43c --- /dev/null +++ b/TPVersionTextField.h @@ -0,0 +1,22 @@ +// +// TPVersionTextField.h +// teleport +// +// Created by JuL on Sat Feb 28 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + + +@interface TPVersionTextField : NSTextField +{ + NSArray * _versions; + int _currentVersionIndex; +} + +- (void)setVersions:(NSArray*)inVersions; +- (void)mouseDown:(NSEvent*)event; +- (void)changeVersion; + +@end diff --git a/TPVersionTextField.m b/TPVersionTextField.m new file mode 100644 index 0000000..e40927c --- /dev/null +++ b/TPVersionTextField.m @@ -0,0 +1,65 @@ +// +// TPVersionTextField.m +// teleport +// +// Created by JuL on Sat Feb 28 2004. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import "TPVersionTextField.h" + + +@implementation TPVersionTextField + +- (void)_commonInit +{ + _currentVersionIndex = -1; + _versions = nil; +} + +- (instancetype) initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + + [self _commonInit]; + + return self; +} + +- (instancetype) initWithCoder:(NSCoder*)coder +{ + self = [super initWithCoder:coder]; + + [self _commonInit]; + + return self; +} + + +- (void)setVersions:(NSArray*)versions +{ + if(versions != _versions) { + _versions = versions; + [self changeVersion]; + } +} + +- (void)mouseDown:(NSEvent*)event +{ + [self changeVersion]; +} + +- (void)changeVersion +{ + if(_versions == nil || [_versions count] == 0) + return; + + if(++_currentVersionIndex == [_versions count]) + _currentVersionIndex = 0; + + NSString * version = _versions[_currentVersionIndex]; + + [self setStringValue:version]; +} + +@end diff --git a/TPVerticalView.h b/TPVerticalView.h new file mode 100644 index 0000000..cf7cf44 --- /dev/null +++ b/TPVerticalView.h @@ -0,0 +1,20 @@ +/* TPVerticalView */ + +#import +#import "TPView.h" + +#define TPControlDragType @"TPControlDragType" + +@class TPRemoteHost; + +@interface TPVerticalView : TPView +{ + TPRemoteHost * draggingHost; + NSArray * dataSource; + IBOutlet id delegate; +} + +- (void)setDataSource:(NSArray*)pDataSource; +- (NSPoint)dragPointForHost:(TPRemoteHost*)pHost; + +@end diff --git a/TPVerticalView.m b/TPVerticalView.m new file mode 100644 index 0000000..b3485e4 --- /dev/null +++ b/TPVerticalView.m @@ -0,0 +1,40 @@ +#import "TPVerticalView.h" +#import "TPRemoteHost.h" +#import "TPLayoutHost.h" +#import "TPPreferencePane.h" + +#define HOST_MARGIN 4 + +@implementation TPVerticalView + +- (void)drawRect:(NSRect)rect +{ + [super drawBackground]; + + NSEnumerator * hostsEnum = [dataSource objectEnumerator]; + TPRemoteHost * host; + NSPoint drawPoint = NSMakePoint(HOST_MARGIN, HOST_MARGIN); + while(host = [hostsEnum nextObject]) { + NSSize drawSize = [host drawSize]; + [host drawHostAtPoint:drawPoint usingMode:SLAVE_HOST_MODE]; + drawPoint.x += drawSize.width + HOST_MARGIN; + } + + //NSLog(@"draw"); + /* Draw text */ + [super drawString:toloc(@"Controllable hosts")]; +} + +- (void)setDataSource:(NSArray*)pDataSource +{ + dataSource = [pDataSource retain]; +} + + + +- (BOOL)isFlipped +{ + return NO; +} + +@end diff --git a/common.h b/common.h new file mode 100644 index 0000000..f34ebe7 --- /dev/null +++ b/common.h @@ -0,0 +1,28 @@ +/* + * common.h + * Teleport + * + * Created by JuL on Thu Dec 04 2003. + * Copyright (c) 2003-2005 abyssoft. All rights reserved. + * + */ + +/* Debug */ +#define DEBUG_GENERAL 0 +#define DEBUG_TRANSFERS 0 + +#define DebugLog(logString, args...) \ +if([[NSUserDefaults standardUserDefaults] boolForKey:@"enableLogging"]) \ +NSLog(logString , ##args) + +/* Macros */ +#define DELETE(obj) if(obj) {[obj release]; obj=nil;} +#define PRINT_ME DebugLog(@"%@: %s", self, __PRETTY_FUNCTION__); +#define PRINT_ME_IF(cond) do{if(cond) DebugLog(@"%@: %s", self, __PRETTY_FUNCTION__);}while(0) + +/* Constants */ +#define RV_SERVICE @"_teleport._tcp" + +#define LEFT_MOUSE_BUTTON 0 +#define RIGHT_MOUSE_BUTTON 1 +#define OTHER_MOUSE_BUTTON 2 diff --git a/desktop-pictures/10.2-Jaguar.jpg b/desktop-pictures/10.2-Jaguar.jpg new file mode 100644 index 0000000..dab45b0 Binary files /dev/null and b/desktop-pictures/10.2-Jaguar.jpg differ diff --git a/desktop-pictures/10.3-Panther.jpg b/desktop-pictures/10.3-Panther.jpg new file mode 100644 index 0000000..63b7e4a Binary files /dev/null and b/desktop-pictures/10.3-Panther.jpg differ diff --git a/desktop-pictures/10.4-Tiger.jpg b/desktop-pictures/10.4-Tiger.jpg new file mode 100644 index 0000000..98bbfde Binary files /dev/null and b/desktop-pictures/10.4-Tiger.jpg differ diff --git a/desktop-pictures/10.5-Leopard.jpg b/desktop-pictures/10.5-Leopard.jpg new file mode 100644 index 0000000..213e341 Binary files /dev/null and b/desktop-pictures/10.5-Leopard.jpg differ diff --git a/desktop-pictures/10.6-SnowLeopard.jpg b/desktop-pictures/10.6-SnowLeopard.jpg new file mode 100644 index 0000000..805c1ef Binary files /dev/null and b/desktop-pictures/10.6-SnowLeopard.jpg differ diff --git a/desktop-pictures/10.7-Lion.jpg b/desktop-pictures/10.7-Lion.jpg new file mode 100644 index 0000000..7ee6f16 Binary files /dev/null and b/desktop-pictures/10.7-Lion.jpg differ diff --git a/halo.png b/halo.png new file mode 100644 index 0000000..cb6b8b4 Binary files /dev/null and b/halo.png differ diff --git a/icon-about.png b/icon-about.png new file mode 100644 index 0000000..11235e5 Binary files /dev/null and b/icon-about.png differ diff --git a/main.m b/main.m new file mode 100644 index 0000000..48e71ce --- /dev/null +++ b/main.m @@ -0,0 +1,14 @@ +// +// main.m +// Teleport +// +// Created by JuL on Wed Dec 03 2003. +// Copyright (c) 2003-2005 abyssoft. All rights reserved. +// + +#import + +int main(int argc, const char *argv[]) +{ + return NSApplicationMain(argc, argv); +} diff --git a/menuicon-black.png b/menuicon-black.png new file mode 100644 index 0000000..f213cd3 Binary files /dev/null and b/menuicon-black.png differ diff --git a/menuicon-white.png b/menuicon-white.png new file mode 100644 index 0000000..585ff76 Binary files /dev/null and b/menuicon-white.png differ diff --git a/menuicon.png b/menuicon.png new file mode 100644 index 0000000..df93d81 Binary files /dev/null and b/menuicon.png differ diff --git a/remove.tiff b/remove.tiff new file mode 100644 index 0000000..7741d10 Binary files /dev/null and b/remove.tiff differ diff --git a/sharedscreen-on.tiff b/sharedscreen-on.tiff new file mode 100644 index 0000000..e0fe4bb Binary files /dev/null and b/sharedscreen-on.tiff differ diff --git a/sharedscreen.tiff b/sharedscreen.tiff new file mode 100644 index 0000000..220c975 Binary files /dev/null and b/sharedscreen.tiff differ diff --git a/switch-sound.wav b/switch-sound.wav new file mode 100644 index 0000000..e09b1e8 Binary files /dev/null and b/switch-sound.wav differ diff --git a/teleport.icns b/teleport.icns new file mode 100644 index 0000000..7afc5b3 Binary files /dev/null and b/teleport.icns differ diff --git a/teleport.xcodeproj/project.pbxproj b/teleport.xcodeproj/project.pbxproj new file mode 100644 index 0000000..cd2b5e5 --- /dev/null +++ b/teleport.xcodeproj/project.pbxproj @@ -0,0 +1,1219 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + A700510707AD3BAB0086D029 /* TPHostsManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A700510507AD3BAB0086D029 /* TPHostsManager.h */; }; + A700510807AD3BAB0086D029 /* TPHostsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A700510607AD3BAB0086D029 /* TPHostsManager.m */; }; + A700510B07AD3C5C0086D029 /* TPBonjourController.h in Headers */ = {isa = PBXBuildFile; fileRef = A700510907AD3C5C0086D029 /* TPBonjourController.h */; }; + A700510C07AD3C5C0086D029 /* TPBonjourController.m in Sources */ = {isa = PBXBuildFile; fileRef = A700510A07AD3C5C0086D029 /* TPBonjourController.m */; }; + A702485615332CD800B9F965 /* 10.2-Jaguar.jpg in Resources */ = {isa = PBXBuildFile; fileRef = A702484A1533265000B9F965 /* 10.2-Jaguar.jpg */; }; + A702485715332CD800B9F965 /* 10.3-Panther.jpg in Resources */ = {isa = PBXBuildFile; fileRef = A702484B1533265000B9F965 /* 10.3-Panther.jpg */; }; + A702485815332CD800B9F965 /* 10.4-Tiger.jpg in Resources */ = {isa = PBXBuildFile; fileRef = A702484C1533265000B9F965 /* 10.4-Tiger.jpg */; }; + A702485915332CD800B9F965 /* 10.5-Leopard.jpg in Resources */ = {isa = PBXBuildFile; fileRef = A702484D1533265000B9F965 /* 10.5-Leopard.jpg */; }; + A702485A15332CD800B9F965 /* 10.6-SnowLeopard.jpg in Resources */ = {isa = PBXBuildFile; fileRef = A702484E1533265000B9F965 /* 10.6-SnowLeopard.jpg */; }; + A702485B15332CD800B9F965 /* 10.7-Lion.jpg in Resources */ = {isa = PBXBuildFile; fileRef = A702484F1533265000B9F965 /* 10.7-Lion.jpg */; }; + A703972A0F609EAA006B8621 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A76426760E11BD9F00F60EA1 /* QuartzCore.framework */; }; + A71221CD1159899E00DCD93B /* MultipleFiles.icns in Resources */ = {isa = PBXBuildFile; fileRef = A71221CC1159899E00DCD93B /* MultipleFiles.icns */; }; + A715B6BA1AA191B200BC2EDE /* TPRemoteHost.m in Sources */ = {isa = PBXBuildFile; fileRef = A7E6ADD305EEB234001D7D35 /* TPRemoteHost.m */; }; + A71E0B911AB4210E00911ACB /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A71E0B901AB4210E00911ACB /* Sparkle.framework */; }; + A71E0B921AB4213800911ACB /* Sparkle.framework in Copy frameworks */ = {isa = PBXBuildFile; fileRef = A71E0B901AB4210E00911ACB /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + A71EA8C80A12688400805CC6 /* TPBackgroundImageTransfer.h in Headers */ = {isa = PBXBuildFile; fileRef = A71EA8C60A12688400805CC6 /* TPBackgroundImageTransfer.h */; }; + A71EA8C90A12688400805CC6 /* TPBackgroundImageTransfer.m in Sources */ = {isa = PBXBuildFile; fileRef = A71EA8C70A12688400805CC6 /* TPBackgroundImageTransfer.m */; }; + A72A1C5A07A464ED0012563F /* TPNetworkConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = A760369C05A0826D00D20845 /* TPNetworkConnection.h */; }; + A72A1C5B07A464EE0012563F /* TPNetworkConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = A760369D05A0826D00D20845 /* TPNetworkConnection.m */; }; + A736CF220923A4420008AB32 /* TPUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = A736CF200923A4420008AB32 /* TPUtils.h */; }; + A736CF230923A4420008AB32 /* TPUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = A736CF210923A4420008AB32 /* TPUtils.m */; }; + A743A23008B2B6CD006B9FB2 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = A7C9DF5B059F4BDA00E67CB3 /* MainMenu.nib */; }; + A744D58305FBE08E0011E56C /* TPApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = A744D58105FBE08E0011E56C /* TPApplication.m */; }; + A744D58405FBE08E0011E56C /* TPApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = A744D58205FBE08E0011E56C /* TPApplication.h */; }; + A745F8E207C00C5F00B295FD /* TPTransfer.h in Headers */ = {isa = PBXBuildFile; fileRef = A745F8E007C00C5F00B295FD /* TPTransfer.h */; }; + A745F8E307C00C5F00B295FD /* TPTransfer.m in Sources */ = {isa = PBXBuildFile; fileRef = A745F8E107C00C5F00B295FD /* TPTransfer.m */; }; + A745F90F07C0389600B295FD /* TPPasteboardTransfer.h in Headers */ = {isa = PBXBuildFile; fileRef = A745F90D07C0389600B295FD /* TPPasteboardTransfer.h */; }; + A745F91007C0389600B295FD /* TPPasteboardTransfer.m in Sources */ = {isa = PBXBuildFile; fileRef = A745F90E07C0389600B295FD /* TPPasteboardTransfer.m */; }; + A745F91F07C038E000B295FD /* TPFileTransfer.h in Headers */ = {isa = PBXBuildFile; fileRef = A745F91D07C038E000B295FD /* TPFileTransfer.h */; }; + A745F92007C038E000B295FD /* TPFileTransfer.m in Sources */ = {isa = PBXBuildFile; fileRef = A745F91E07C038E000B295FD /* TPFileTransfer.m */; }; + A746D70508B1733600CE2B89 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A746D70308B1733600CE2B89 /* Localizable.strings */; }; + A75752B605ADE74000D78303 /* TPTCPSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = A75752B405ADE74000D78303 /* TPTCPSocket.h */; }; + A75752B705ADE74000D78303 /* TPTCPSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = A75752B505ADE74000D78303 /* TPTCPSocket.m */; }; + A75752D305ADF9B900D78303 /* TPConnectionController.h in Headers */ = {isa = PBXBuildFile; fileRef = A75752D105ADF9B900D78303 /* TPConnectionController.h */; }; + A75752D405ADF9B900D78303 /* TPConnectionController.m in Sources */ = {isa = PBXBuildFile; fileRef = A75752D205ADF9B900D78303 /* TPConnectionController.m */; }; + A75ED6CC09A3FDCF00AFDD7D /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A75ED6B709A3FDCF00AFDD7D /* Carbon.framework */; }; + A7609656107ED026008572E5 /* TPFileSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = A7609654107ED026008572E5 /* TPFileSerialization.h */; }; + A7609657107ED026008572E5 /* TPFileSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = A7609655107ED026008572E5 /* TPFileSerialization.m */; }; + A7614BEE0FE850210043C401 /* TPScreen.h in Headers */ = {isa = PBXBuildFile; fileRef = A7614BEC0FE850210043C401 /* TPScreen.h */; }; + A7614BEF0FE850210043C401 /* TPScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = A7614BED0FE850210043C401 /* TPScreen.m */; }; + A7642F4A09F6DF1F00789DC0 /* TPTransfer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = A7642F4909F6DF1F00789DC0 /* TPTransfer_Private.h */; }; + A7697D710DB25C8C0037F0A5 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7697D700DB25C8C0037F0A5 /* SystemConfiguration.framework */; }; + A7697D750DB25CCE0037F0A5 /* TPNetworkConfigurationWatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = A7697D730DB25CCE0037F0A5 /* TPNetworkConfigurationWatcher.h */; }; + A7697D760DB25CCE0037F0A5 /* TPNetworkConfigurationWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = A7697D740DB25CCE0037F0A5 /* TPNetworkConfigurationWatcher.m */; }; + A76BE04905CF016600FF0EA4 /* Protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = A76BE04705CF016600FF0EA4 /* Protocol.h */; }; + A777B8010832B65B00C4D824 /* TPHostSnapping.h in Headers */ = {isa = PBXBuildFile; fileRef = A777B7FF0832B65B00C4D824 /* TPHostSnapping.h */; }; + A777B8020832B65B00C4D824 /* TPHostSnapping.m in Sources */ = {isa = PBXBuildFile; fileRef = A777B8000832B65B00C4D824 /* TPHostSnapping.m */; }; + A77812120CAF331300EEC97E /* halo.png in Resources */ = {isa = PBXBuildFile; fileRef = A77812110CAF331300EEC97E /* halo.png */; }; + A782102C05C0D10D00A555DC /* whitelogo.png in Resources */ = {isa = PBXBuildFile; fileRef = A782102B05C0D10D00A555DC /* whitelogo.png */; }; + A78539B809E48D9A005C3446 /* TPTCPSecureSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = A78539B609E48D9A005C3446 /* TPTCPSecureSocket.h */; }; + A78539B909E48D9A005C3446 /* TPTCPSecureSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = A78539B709E48D9A005C3446 /* TPTCPSecureSocket.m */; }; + A7853BE509E4967C005C3446 /* TPAuthenticationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A7853BE109E4967C005C3446 /* TPAuthenticationManager.m */; }; + A7853BE609E4967C005C3446 /* TPAuthenticationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A7853BE209E4967C005C3446 /* TPAuthenticationManager.h */; }; + A7853BE709E4967C005C3446 /* TPAuthenticationRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A7853BE309E4967C005C3446 /* TPAuthenticationRequest.h */; }; + A7853BE809E4967C005C3446 /* TPAuthenticationRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = A7853BE409E4967C005C3446 /* TPAuthenticationRequest.m */; }; + A78569340921675F00301B93 /* TPTestsController.h in Headers */ = {isa = PBXBuildFile; fileRef = A78569320921675800301B93 /* TPTestsController.h */; }; + A78569350921675F00301B93 /* TPTestsController.m in Sources */ = {isa = PBXBuildFile; fileRef = A78569330921675800301B93 /* TPTestsController.m */; }; + A78569FE0921719100301B93 /* TPEventsController.h in Headers */ = {isa = PBXBuildFile; fileRef = A78569FC0921719100301B93 /* TPEventsController.h */; }; + A78569FF0921719100301B93 /* TPEventsController.m in Sources */ = {isa = PBXBuildFile; fileRef = A78569FD0921719100301B93 /* TPEventsController.m */; }; + A7856A28092171C300301B93 /* TPEventTapsController.h in Headers */ = {isa = PBXBuildFile; fileRef = A7856A26092171C300301B93 /* TPEventTapsController.h */; }; + A7856A29092171C300301B93 /* TPEventTapsController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7856A27092171C300301B93 /* TPEventTapsController.m */; }; + A786DB3208BE71C400BF1E8B /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A786DB3108BE71C400BF1E8B /* Security.framework */; }; + A786E22F1A19E06E00E66B43 /* TPPreferencePane.m in Sources */ = {isa = PBXBuildFile; fileRef = F506C03D013D9D7901CA16C8 /* TPPreferencePane.m */; }; + A786E2301A19E07D00E66B43 /* TPTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = A7E475960A14DD7900DCEA57 /* TPTableView.m */; }; + A786E2311A19E08100E66B43 /* TPClosingWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = A7D9BB2705C5CEB8003DD65C /* TPClosingWindow.m */; }; + A786E2321A19E08400E66B43 /* TPVersionTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = A7B5932C05F0F42000F7CA57 /* TPVersionTextField.m */; }; + A786E2331A19E08700E66B43 /* TPLinkTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = A7389ECC08808A29000D0DCB /* TPLinkTextField.m */; }; + A786E2341A19E09200E66B43 /* TPLayoutView.m in Sources */ = {isa = PBXBuildFile; fileRef = A7E6ADA305EEAE2C001D7D35 /* TPLayoutView.m */; }; + A786E2351A19E09200E66B43 /* TPKeyComboView.m in Sources */ = {isa = PBXBuildFile; fileRef = A7BEF7740F8C286C00C54411 /* TPKeyComboView.m */; }; + A786E2361A19E09200E66B43 /* TPOptionsController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7A50B070F844F9900C5B878 /* TPOptionsController.m */; }; + A786E2371A19E09200E66B43 /* TPOptionsView.m in Sources */ = {isa = PBXBuildFile; fileRef = A7D6388C0F213F6B000C5DB2 /* TPOptionsView.m */; }; + A786E2381A19E09200E66B43 /* TPAnimationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A7D638FC0F21446A000C5DB2 /* TPAnimationManager.m */; }; + A786E2391A19E09200E66B43 /* TPLayoutHostView.m in Sources */ = {isa = PBXBuildFile; fileRef = A7B5934B05F17DFB00F7CA57 /* TPLayoutHostView.m */; }; + A786E23A1A19E09200E66B43 /* TPLayoutRemoteHostView.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C9DF20059F4B0500E67CB3 /* TPLayoutRemoteHostView.m */; }; + A786E23B1A19E09200E66B43 /* TPLayoutLocalHostView.m in Sources */ = {isa = PBXBuildFile; fileRef = A7E6ADDA05EEB47D001D7D35 /* TPLayoutLocalHostView.m */; }; + A786E23C1A19E0D900E66B43 /* SecurityInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7E612E609EDC0210024712F /* SecurityInterface.framework */; }; + A788AA7C09F5BFCA00570E96 /* Lock_White.tif in Resources */ = {isa = PBXBuildFile; fileRef = A788AA7B09F5BFCA00570E96 /* Lock_White.tif */; }; + A78C04AA0E13287D00755C6D /* PTHotKey.h in Headers */ = {isa = PBXBuildFile; fileRef = A78C048C0E13287D00755C6D /* PTHotKey.h */; }; + A78C04AB0E13287D00755C6D /* PTHotKey.m in Sources */ = {isa = PBXBuildFile; fileRef = A78C048D0E13287D00755C6D /* PTHotKey.m */; }; + A78C04AC0E13287D00755C6D /* PTHotKeyCenter.h in Headers */ = {isa = PBXBuildFile; fileRef = A78C048E0E13287D00755C6D /* PTHotKeyCenter.h */; }; + A78C04AD0E13287D00755C6D /* PTHotKeyCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = A78C048F0E13287D00755C6D /* PTHotKeyCenter.m */; }; + A78C04AE0E13287D00755C6D /* PTKeyBroadcaster.h in Headers */ = {isa = PBXBuildFile; fileRef = A78C04900E13287D00755C6D /* PTKeyBroadcaster.h */; }; + A78C04AF0E13287D00755C6D /* PTKeyBroadcaster.m in Sources */ = {isa = PBXBuildFile; fileRef = A78C04910E13287D00755C6D /* PTKeyBroadcaster.m */; }; + A78C04B00E13287D00755C6D /* PTKeyCodeTranslator.h in Headers */ = {isa = PBXBuildFile; fileRef = A78C04920E13287D00755C6D /* PTKeyCodeTranslator.h */; }; + A78C04B10E13287D00755C6D /* PTKeyCodeTranslator.m in Sources */ = {isa = PBXBuildFile; fileRef = A78C04930E13287D00755C6D /* PTKeyCodeTranslator.m */; }; + A78C04B20E13287D00755C6D /* PTKeyCombo.h in Headers */ = {isa = PBXBuildFile; fileRef = A78C04940E13287D00755C6D /* PTKeyCombo.h */; }; + A78C04B30E13287D00755C6D /* PTKeyCombo.m in Sources */ = {isa = PBXBuildFile; fileRef = A78C04950E13287D00755C6D /* PTKeyCombo.m */; }; + A78C04B40E13287D00755C6D /* PTKeyComboPanel.h in Headers */ = {isa = PBXBuildFile; fileRef = A78C04960E13287D00755C6D /* PTKeyComboPanel.h */; }; + A78C04B50E13287D00755C6D /* PTKeyComboPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = A78C04970E13287D00755C6D /* PTKeyComboPanel.m */; }; + A78FD1AB0CB31B99006AC28C /* SecurityFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A78FD1AA0CB31B99006AC28C /* SecurityFoundation.framework */; }; + A793B81A08AACC3B0078CAA7 /* TPPreferencesManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A793B81808AACC3B0078CAA7 /* TPPreferencesManager.h */; }; + A793B81B08AACC3B0078CAA7 /* TPPreferencesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A793B81908AACC3B0078CAA7 /* TPPreferencesManager.m */; }; + A79D9D4009AEA25F00DCE620 /* TPConnectionsManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A79D9D3E09AEA25F00DCE620 /* TPConnectionsManager.h */; }; + A79D9D4109AEA25F00DCE620 /* TPConnectionsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A79D9D3F09AEA25F00DCE620 /* TPConnectionsManager.m */; }; + A79ED91B08959FAC00DC4A6A /* TPTransfersManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A79ED91908959FAC00DC4A6A /* TPTransfersManager.h */; }; + A79ED91C08959FAC00DC4A6A /* TPTransfersManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A79ED91A08959FAC00DC4A6A /* TPTransfersManager.m */; }; + A79EDD4F0897D69700DC4A6A /* TPBezelController.h in Headers */ = {isa = PBXBuildFile; fileRef = A79EDD4D0897D69700DC4A6A /* TPBezelController.h */; }; + A79EDD500897D69700DC4A6A /* TPBezelController.m in Sources */ = {isa = PBXBuildFile; fileRef = A79EDD4E0897D69700DC4A6A /* TPBezelController.m */; }; + A79F8E95142AAAF700ED1D19 /* KeyCodes.plist in Resources */ = {isa = PBXBuildFile; fileRef = A79F8E94142AAAF700ED1D19 /* KeyCodes.plist */; }; + A7A8194E0C60042800DE5CD4 /* TPDirectEventTapsController.h in Headers */ = {isa = PBXBuildFile; fileRef = A7A819490C60030700DE5CD4 /* TPDirectEventTapsController.h */; }; + A7A8194F0C60042900DE5CD4 /* TPDirectEventTapsController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7A8194A0C60030700DE5CD4 /* TPDirectEventTapsController.m */; }; + A7A819520C60047D00DE5CD4 /* TPEventTapsController_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = A7A819510C60047D00DE5CD4 /* TPEventTapsController_Internal.h */; }; + A7B08FAB0DCA10A3008CAFF2 /* TPFakeHostsGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = A7B08FA90DCA10A3008CAFF2 /* TPFakeHostsGenerator.h */; }; + A7B08FAC0DCA10A3008CAFF2 /* TPFakeHostsGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = A7B08FAA0DCA10A3008CAFF2 /* TPFakeHostsGenerator.m */; }; + A7B799E707B8304C00C717E7 /* TPHotBorderView.h in Headers */ = {isa = PBXBuildFile; fileRef = A7B799E507B8304C00C717E7 /* TPHotBorderView.h */; }; + A7B799E807B8304C00C717E7 /* TPHotBorderView.m in Sources */ = {isa = PBXBuildFile; fileRef = A7B799E607B8304C00C717E7 /* TPHotBorderView.m */; }; + A7BB55E10A1BD00E007044EA /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7BB550E0A1BD00D007044EA /* IOKit.framework */; }; + A7C4711C0FA903C300282EF2 /* switch-sound.wav in Resources */ = {isa = PBXBuildFile; fileRef = A7C4711B0FA903C300282EF2 /* switch-sound.wav */; }; + A7C9DED1059F4A7400E67CB3 /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C9DECF059F4A7400E67CB3 /* common.h */; }; + A7C9DF00059F4AA100E67CB3 /* TPBezelView.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C9DEE5059F4AA100E67CB3 /* TPBezelView.h */; }; + A7C9DF01059F4AA100E67CB3 /* TPBezelView.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C9DEE6059F4AA100E67CB3 /* TPBezelView.m */; }; + A7C9DF02059F4AA100E67CB3 /* TPBezelWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C9DEE7059F4AA100E67CB3 /* TPBezelWindow.h */; }; + A7C9DF03059F4AA100E67CB3 /* TPBezelWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C9DEE8059F4AA100E67CB3 /* TPBezelWindow.m */; }; + A7C9DF04059F4AA100E67CB3 /* TPRemoteOperationsController.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C9DEE9059F4AA100E67CB3 /* TPRemoteOperationsController.h */; }; + A7C9DF05059F4AA100E67CB3 /* TPRemoteOperationsController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C9DEEA059F4AA100E67CB3 /* TPRemoteOperationsController.m */; }; + A7C9DF17059F4AE900E67CB3 /* TPHost.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C9DF15059F4AE900E67CB3 /* TPHost.h */; }; + A7C9DF18059F4AE900E67CB3 /* TPHost.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C9DF16059F4AE900E67CB3 /* TPHost.m */; }; + A7C9DF1D059F4AF600E67CB3 /* TPHotBorder.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C9DF1B059F4AF600E67CB3 /* TPHotBorder.h */; }; + A7C9DF1E059F4AF600E67CB3 /* TPHotBorder.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C9DF1C059F4AF600E67CB3 /* TPHotBorder.m */; }; + A7C9DF2F059F4B1300E67CB3 /* TPMainController.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C9DF27059F4B1300E67CB3 /* TPMainController.h */; }; + A7C9DF30059F4B1300E67CB3 /* TPMainController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C9DF28059F4B1300E67CB3 /* TPMainController.m */; }; + A7C9DF31059F4B1300E67CB3 /* TPClientController.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C9DF29059F4B1300E67CB3 /* TPClientController.h */; }; + A7C9DF32059F4B1300E67CB3 /* TPClientController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C9DF2A059F4B1300E67CB3 /* TPClientController.m */; }; + A7C9DF33059F4B1300E67CB3 /* TPMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C9DF2B059F4B1300E67CB3 /* TPMessage.h */; }; + A7C9DF34059F4B1300E67CB3 /* TPMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C9DF2C059F4B1300E67CB3 /* TPMessage.m */; }; + A7C9DF35059F4B1300E67CB3 /* TPServerController.h in Headers */ = {isa = PBXBuildFile; fileRef = A7C9DF2D059F4B1300E67CB3 /* TPServerController.h */; }; + A7C9DF36059F4B1300E67CB3 /* TPServerController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C9DF2E059F4B1300E67CB3 /* TPServerController.m */; }; + A7C9DF5F059F4C2500E67CB3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A7C9DF5E059F4C2500E67CB3 /* main.m */; }; + A7C9DF61059F4C5100E67CB3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; }; + A7D173360F535F0E00CC6946 /* TPHostAnimationController.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D173340F535F0E00CC6946 /* TPHostAnimationController.h */; }; + A7D173370F535F0E00CC6946 /* TPHostAnimationController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7D173350F535F0E00CC6946 /* TPHostAnimationController.m */; }; + A7DD0BE2083D5F9E00DD477C /* TPStatusItemController.h in Headers */ = {isa = PBXBuildFile; fileRef = A7DD0BDE083D5F9E00DD477C /* TPStatusItemController.h */; }; + A7DD0BE3083D5F9E00DD477C /* TPStatusItemController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7DD0BDF083D5F9E00DD477C /* TPStatusItemController.m */; }; + A7E61DA509F176340024712F /* teleport.icns in Resources */ = {isa = PBXBuildFile; fileRef = A7E61DA409F176340024712F /* teleport.icns */; }; + A7E6351E083F7AD200BC143F /* menuicon.png in Resources */ = {isa = PBXBuildFile; fileRef = A7E6351D083F7AD200BC143F /* menuicon.png */; }; + A7E635300840CAEF00BC143F /* menuicon-white.png in Resources */ = {isa = PBXBuildFile; fileRef = A7E6352E0840CAEF00BC143F /* menuicon-white.png */; }; + A7E635310840CAEF00BC143F /* menuicon-black.png in Resources */ = {isa = PBXBuildFile; fileRef = A7E6352F0840CAEF00BC143F /* menuicon-black.png */; }; + A7E6ADD005EEB223001D7D35 /* TPLocalHost.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E6ADCC05EEB223001D7D35 /* TPLocalHost.h */; }; + A7E6ADD105EEB223001D7D35 /* TPLocalHost.m in Sources */ = {isa = PBXBuildFile; fileRef = A7E6ADCD05EEB223001D7D35 /* TPLocalHost.m */; }; + A7E6ADD605EEB234001D7D35 /* TPRemoteHost.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E6ADD205EEB234001D7D35 /* TPRemoteHost.h */; }; + A7EC1BD51A19E6CC00CDEC7B /* teleportPref.xib in Resources */ = {isa = PBXBuildFile; fileRef = A7F1E84D0CAB155000D18B0C /* teleportPref.xib */; }; + A7EC1BD61A19E82E00CDEC7B /* icon-about.png in Resources */ = {isa = PBXBuildFile; fileRef = A70901850CD007B6006576A6 /* icon-about.png */; }; + A7EC1BD71A19E83A00CDEC7B /* remove.tiff in Resources */ = {isa = PBXBuildFile; fileRef = A7033E250CF0AB8C004AFBEA /* remove.tiff */; }; + A7EC1BD81A19E83D00CDEC7B /* sharedscreen.tiff in Resources */ = {isa = PBXBuildFile; fileRef = A7033F3D0CF10E6C004AFBEA /* sharedscreen.tiff */; }; + A7EC1BD91A19E84000CDEC7B /* sharedscreen-on.tiff in Resources */ = {isa = PBXBuildFile; fileRef = A7BD4E860CF1D26B005B3BED /* sharedscreen-on.tiff */; }; + A7EF8B0705AB6CA900E9BC56 /* TPBezierPath.m in Sources */ = {isa = PBXBuildFile; fileRef = A7EF8B0505AB6CA900E9BC56 /* TPBezierPath.m */; }; + A7EF8B0805AB6CA900E9BC56 /* TPBezierPath.h in Headers */ = {isa = PBXBuildFile; fileRef = A7EF8B0605AB6CA900E9BC56 /* TPBezierPath.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + A731475715CEE635000D7ED4 /* Copy frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + A71E0B921AB4213800911ACB /* Sparkle.framework in Copy frameworks */, + ); + name = "Copy frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + A700510507AD3BAB0086D029 /* TPHostsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPHostsManager.h; sourceTree = ""; }; + A700510607AD3BAB0086D029 /* TPHostsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPHostsManager.m; sourceTree = ""; }; + A700510907AD3C5C0086D029 /* TPBonjourController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPBonjourController.h; sourceTree = ""; }; + A700510A07AD3C5C0086D029 /* TPBonjourController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPBonjourController.m; sourceTree = ""; }; + A702484A1533265000B9F965 /* 10.2-Jaguar.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "10.2-Jaguar.jpg"; sourceTree = ""; }; + A702484B1533265000B9F965 /* 10.3-Panther.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "10.3-Panther.jpg"; sourceTree = ""; }; + A702484C1533265000B9F965 /* 10.4-Tiger.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "10.4-Tiger.jpg"; sourceTree = ""; }; + A702484D1533265000B9F965 /* 10.5-Leopard.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "10.5-Leopard.jpg"; sourceTree = ""; }; + A702484E1533265000B9F965 /* 10.6-SnowLeopard.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "10.6-SnowLeopard.jpg"; sourceTree = ""; }; + A702484F1533265000B9F965 /* 10.7-Lion.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "10.7-Lion.jpg"; sourceTree = ""; }; + A7033E250CF0AB8C004AFBEA /* remove.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = remove.tiff; sourceTree = ""; }; + A7033F3D0CF10E6C004AFBEA /* sharedscreen.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = sharedscreen.tiff; sourceTree = ""; }; + A70901850CD007B6006576A6 /* icon-about.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-about.png"; sourceTree = ""; }; + A70AE0D714674AD100A76119 /* teleport.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = teleport.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A71221CC1159899E00DCD93B /* MultipleFiles.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = MultipleFiles.icns; sourceTree = ""; }; + A71E0B901AB4210E00911ACB /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Sparkle.framework; sourceTree = ""; }; + A71EA8C60A12688400805CC6 /* TPBackgroundImageTransfer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPBackgroundImageTransfer.h; sourceTree = ""; }; + A71EA8C70A12688400805CC6 /* TPBackgroundImageTransfer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPBackgroundImageTransfer.m; sourceTree = ""; }; + A73510E505A65AA90049E8CD /* TPUDPSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPUDPSocket.h; sourceTree = ""; }; + A73510E605A65AA90049E8CD /* TPUDPSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPUDPSocket.m; sourceTree = ""; }; + A736CF200923A4420008AB32 /* TPUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPUtils.h; sourceTree = ""; }; + A736CF210923A4420008AB32 /* TPUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPUtils.m; sourceTree = ""; }; + A7389ECB08808A29000D0DCB /* TPLinkTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPLinkTextField.h; sourceTree = ""; }; + A7389ECC08808A29000D0DCB /* TPLinkTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPLinkTextField.m; sourceTree = ""; }; + A744D58105FBE08E0011E56C /* TPApplication.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPApplication.m; sourceTree = ""; }; + A744D58205FBE08E0011E56C /* TPApplication.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPApplication.h; sourceTree = ""; }; + A745F8E007C00C5F00B295FD /* TPTransfer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPTransfer.h; sourceTree = ""; }; + A745F8E107C00C5F00B295FD /* TPTransfer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPTransfer.m; sourceTree = ""; }; + A745F90D07C0389600B295FD /* TPPasteboardTransfer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPPasteboardTransfer.h; sourceTree = ""; }; + A745F90E07C0389600B295FD /* TPPasteboardTransfer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPPasteboardTransfer.m; sourceTree = ""; }; + A745F91D07C038E000B295FD /* TPFileTransfer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPFileTransfer.h; sourceTree = ""; }; + A745F91E07C038E000B295FD /* TPFileTransfer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPFileTransfer.m; sourceTree = ""; }; + A746D49B08B14AA100CE2B89 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A75752B405ADE74000D78303 /* TPTCPSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPTCPSocket.h; sourceTree = ""; }; + A75752B505ADE74000D78303 /* TPTCPSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPTCPSocket.m; sourceTree = ""; }; + A75752D105ADF9B900D78303 /* TPConnectionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPConnectionController.h; sourceTree = ""; }; + A75752D205ADF9B900D78303 /* TPConnectionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPConnectionController.m; sourceTree = ""; }; + A75ED6B709A3FDCF00AFDD7D /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; + A760369C05A0826D00D20845 /* TPNetworkConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPNetworkConnection.h; sourceTree = ""; }; + A760369D05A0826D00D20845 /* TPNetworkConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPNetworkConnection.m; sourceTree = ""; }; + A7609654107ED026008572E5 /* TPFileSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPFileSerialization.h; sourceTree = ""; }; + A7609655107ED026008572E5 /* TPFileSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPFileSerialization.m; sourceTree = ""; }; + A7614BEC0FE850210043C401 /* TPScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPScreen.h; sourceTree = ""; }; + A7614BED0FE850210043C401 /* TPScreen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPScreen.m; sourceTree = ""; }; + A76426760E11BD9F00F60EA1 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = ""; }; + A7642F4909F6DF1F00789DC0 /* TPTransfer_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPTransfer_Private.h; sourceTree = ""; }; + A7697D700DB25C8C0037F0A5 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = ""; }; + A7697D730DB25CCE0037F0A5 /* TPNetworkConfigurationWatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPNetworkConfigurationWatcher.h; sourceTree = ""; }; + A7697D740DB25CCE0037F0A5 /* TPNetworkConfigurationWatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPNetworkConfigurationWatcher.m; sourceTree = ""; }; + A76BE04705CF016600FF0EA4 /* Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Protocol.h; sourceTree = ""; }; + A77425610E1C41820059395D /* key.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = key.png; sourceTree = ""; }; + A777B7FF0832B65B00C4D824 /* TPHostSnapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPHostSnapping.h; sourceTree = ""; }; + A777B8000832B65B00C4D824 /* TPHostSnapping.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPHostSnapping.m; sourceTree = ""; }; + A77812110CAF331300EEC97E /* halo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = halo.png; sourceTree = ""; }; + A782102B05C0D10D00A555DC /* whitelogo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = whitelogo.png; sourceTree = ""; }; + A78539B609E48D9A005C3446 /* TPTCPSecureSocket.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPTCPSecureSocket.h; sourceTree = ""; }; + A78539B709E48D9A005C3446 /* TPTCPSecureSocket.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPTCPSecureSocket.m; sourceTree = ""; }; + A7853BE109E4967C005C3446 /* TPAuthenticationManager.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPAuthenticationManager.m; sourceTree = ""; }; + A7853BE209E4967C005C3446 /* TPAuthenticationManager.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPAuthenticationManager.h; sourceTree = ""; }; + A7853BE309E4967C005C3446 /* TPAuthenticationRequest.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPAuthenticationRequest.h; sourceTree = ""; }; + A7853BE409E4967C005C3446 /* TPAuthenticationRequest.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPAuthenticationRequest.m; sourceTree = ""; }; + A78569320921675800301B93 /* TPTestsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPTestsController.h; sourceTree = ""; }; + A78569330921675800301B93 /* TPTestsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPTestsController.m; sourceTree = ""; }; + A78569FC0921719100301B93 /* TPEventsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPEventsController.h; sourceTree = ""; }; + A78569FD0921719100301B93 /* TPEventsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPEventsController.m; sourceTree = ""; }; + A7856A26092171C300301B93 /* TPEventTapsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPEventTapsController.h; sourceTree = ""; }; + A7856A27092171C300301B93 /* TPEventTapsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPEventTapsController.m; sourceTree = ""; }; + A786DB3108BE71C400BF1E8B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = ""; }; + A786E22D1A19E00300E66B43 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/teleportPref.xib; sourceTree = ""; }; + A788AA7B09F5BFCA00570E96 /* Lock_White.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Lock_White.tif; sourceTree = ""; }; + A78C048A0E13287D00755C6D /* PTKeyboardIcon.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = PTKeyboardIcon.tiff; sourceTree = ""; }; + A78C048C0E13287D00755C6D /* PTHotKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PTHotKey.h; sourceTree = ""; }; + A78C048D0E13287D00755C6D /* PTHotKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PTHotKey.m; sourceTree = ""; }; + A78C048E0E13287D00755C6D /* PTHotKeyCenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PTHotKeyCenter.h; sourceTree = ""; }; + A78C048F0E13287D00755C6D /* PTHotKeyCenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PTHotKeyCenter.m; sourceTree = ""; }; + A78C04900E13287D00755C6D /* PTKeyBroadcaster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PTKeyBroadcaster.h; sourceTree = ""; }; + A78C04910E13287D00755C6D /* PTKeyBroadcaster.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PTKeyBroadcaster.m; sourceTree = ""; }; + A78C04920E13287D00755C6D /* PTKeyCodeTranslator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PTKeyCodeTranslator.h; sourceTree = ""; }; + A78C04930E13287D00755C6D /* PTKeyCodeTranslator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PTKeyCodeTranslator.m; sourceTree = ""; }; + A78C04940E13287D00755C6D /* PTKeyCombo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PTKeyCombo.h; sourceTree = ""; }; + A78C04950E13287D00755C6D /* PTKeyCombo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PTKeyCombo.m; sourceTree = ""; }; + A78C04960E13287D00755C6D /* PTKeyComboPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PTKeyComboPanel.h; sourceTree = ""; }; + A78C04970E13287D00755C6D /* PTKeyComboPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PTKeyComboPanel.m; sourceTree = ""; }; + A78FD1AA0CB31B99006AC28C /* SecurityFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SecurityFoundation.framework; path = /System/Library/Frameworks/SecurityFoundation.framework; sourceTree = ""; }; + A793B81808AACC3B0078CAA7 /* TPPreferencesManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPPreferencesManager.h; sourceTree = ""; }; + A793B81908AACC3B0078CAA7 /* TPPreferencesManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPPreferencesManager.m; sourceTree = ""; }; + A79C91D406DCF86A0044D87C /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/Localizable.strings; sourceTree = ""; }; + A79D9D3E09AEA25F00DCE620 /* TPConnectionsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPConnectionsManager.h; sourceTree = ""; }; + A79D9D3F09AEA25F00DCE620 /* TPConnectionsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPConnectionsManager.m; sourceTree = ""; }; + A79ED91908959FAC00DC4A6A /* TPTransfersManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPTransfersManager.h; sourceTree = ""; }; + A79ED91A08959FAC00DC4A6A /* TPTransfersManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPTransfersManager.m; sourceTree = ""; }; + A79EDD4D0897D69700DC4A6A /* TPBezelController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPBezelController.h; sourceTree = ""; }; + A79EDD4E0897D69700DC4A6A /* TPBezelController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPBezelController.m; sourceTree = ""; }; + A79F8E94142AAAF700ED1D19 /* KeyCodes.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = KeyCodes.plist; sourceTree = ""; }; + A7A50B060F844F9900C5B878 /* TPOptionsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPOptionsController.h; sourceTree = ""; }; + A7A50B070F844F9900C5B878 /* TPOptionsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPOptionsController.m; sourceTree = ""; }; + A7A819490C60030700DE5CD4 /* TPDirectEventTapsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPDirectEventTapsController.h; sourceTree = ""; }; + A7A8194A0C60030700DE5CD4 /* TPDirectEventTapsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPDirectEventTapsController.m; sourceTree = ""; }; + A7A819510C60047D00DE5CD4 /* TPEventTapsController_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPEventTapsController_Internal.h; sourceTree = ""; }; + A7B08FA90DCA10A3008CAFF2 /* TPFakeHostsGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPFakeHostsGenerator.h; sourceTree = ""; }; + A7B08FAA0DCA10A3008CAFF2 /* TPFakeHostsGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPFakeHostsGenerator.m; sourceTree = ""; }; + A7B5932B05F0F42000F7CA57 /* TPVersionTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPVersionTextField.h; sourceTree = ""; }; + A7B5932C05F0F42000F7CA57 /* TPVersionTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPVersionTextField.m; sourceTree = ""; }; + A7B5934A05F17DFB00F7CA57 /* TPLayoutHostView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPLayoutHostView.h; sourceTree = ""; }; + A7B5934B05F17DFB00F7CA57 /* TPLayoutHostView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPLayoutHostView.m; sourceTree = ""; }; + A7B799E507B8304C00C717E7 /* TPHotBorderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPHotBorderView.h; sourceTree = ""; }; + A7B799E607B8304C00C717E7 /* TPHotBorderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPHotBorderView.m; sourceTree = ""; }; + A7BB550E0A1BD00D007044EA /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; + A7BD4E860CF1D26B005B3BED /* sharedscreen-on.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "sharedscreen-on.tiff"; sourceTree = ""; }; + A7BEF7730F8C286C00C54411 /* TPKeyComboView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPKeyComboView.h; sourceTree = ""; }; + A7BEF7740F8C286C00C54411 /* TPKeyComboView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPKeyComboView.m; sourceTree = ""; }; + A7C4711B0FA903C300282EF2 /* switch-sound.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = "switch-sound.wav"; sourceTree = ""; }; + A7C9DECF059F4A7400E67CB3 /* common.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = SOURCE_ROOT; }; + A7C9DEE5059F4AA100E67CB3 /* TPBezelView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPBezelView.h; sourceTree = ""; }; + A7C9DEE6059F4AA100E67CB3 /* TPBezelView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPBezelView.m; sourceTree = ""; }; + A7C9DEE7059F4AA100E67CB3 /* TPBezelWindow.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPBezelWindow.h; sourceTree = ""; }; + A7C9DEE8059F4AA100E67CB3 /* TPBezelWindow.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPBezelWindow.m; sourceTree = ""; }; + A7C9DEE9059F4AA100E67CB3 /* TPRemoteOperationsController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPRemoteOperationsController.h; sourceTree = ""; }; + A7C9DEEA059F4AA100E67CB3 /* TPRemoteOperationsController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPRemoteOperationsController.m; sourceTree = ""; }; + A7C9DF15059F4AE900E67CB3 /* TPHost.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPHost.h; sourceTree = SOURCE_ROOT; }; + A7C9DF16059F4AE900E67CB3 /* TPHost.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPHost.m; sourceTree = SOURCE_ROOT; }; + A7C9DF1B059F4AF600E67CB3 /* TPHotBorder.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPHotBorder.h; sourceTree = ""; }; + A7C9DF1C059F4AF600E67CB3 /* TPHotBorder.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPHotBorder.m; sourceTree = ""; }; + A7C9DF1F059F4B0500E67CB3 /* TPLayoutRemoteHostView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPLayoutRemoteHostView.h; sourceTree = ""; }; + A7C9DF20059F4B0500E67CB3 /* TPLayoutRemoteHostView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPLayoutRemoteHostView.m; sourceTree = ""; }; + A7C9DF27059F4B1300E67CB3 /* TPMainController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPMainController.h; sourceTree = ""; }; + A7C9DF28059F4B1300E67CB3 /* TPMainController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPMainController.m; sourceTree = ""; }; + A7C9DF29059F4B1300E67CB3 /* TPClientController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPClientController.h; sourceTree = ""; }; + A7C9DF2A059F4B1300E67CB3 /* TPClientController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPClientController.m; sourceTree = ""; }; + A7C9DF2B059F4B1300E67CB3 /* TPMessage.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPMessage.h; sourceTree = SOURCE_ROOT; }; + A7C9DF2C059F4B1300E67CB3 /* TPMessage.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPMessage.m; sourceTree = SOURCE_ROOT; }; + A7C9DF2D059F4B1300E67CB3 /* TPServerController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPServerController.h; sourceTree = ""; }; + A7C9DF2E059F4B1300E67CB3 /* TPServerController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPServerController.m; sourceTree = ""; }; + A7C9DF5B059F4BDA00E67CB3 /* MainMenu.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; path = MainMenu.nib; sourceTree = ""; }; + A7C9DF5E059F4C2500E67CB3 /* main.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = SOURCE_ROOT; }; + A7C9DF8B059F4EA700E67CB3 /* teleport_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = teleport_Prefix.pch; sourceTree = ""; }; + A7D173340F535F0E00CC6946 /* TPHostAnimationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPHostAnimationController.h; sourceTree = ""; }; + A7D173350F535F0E00CC6946 /* TPHostAnimationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPHostAnimationController.m; sourceTree = ""; }; + A7D6388B0F213F6B000C5DB2 /* TPOptionsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPOptionsView.h; sourceTree = ""; }; + A7D6388C0F213F6B000C5DB2 /* TPOptionsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPOptionsView.m; sourceTree = ""; }; + A7D638FB0F21446A000C5DB2 /* TPAnimationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPAnimationManager.h; sourceTree = ""; }; + A7D638FC0F21446A000C5DB2 /* TPAnimationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPAnimationManager.m; sourceTree = ""; }; + A7D9BB2605C5CEB8003DD65C /* TPClosingWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPClosingWindow.h; sourceTree = ""; }; + A7D9BB2705C5CEB8003DD65C /* TPClosingWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPClosingWindow.m; sourceTree = ""; }; + A7DD0BDE083D5F9E00DD477C /* TPStatusItemController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPStatusItemController.h; sourceTree = ""; }; + A7DD0BDF083D5F9E00DD477C /* TPStatusItemController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPStatusItemController.m; sourceTree = ""; }; + A7E475950A14DD7900DCEA57 /* TPTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPTableView.h; sourceTree = ""; }; + A7E475960A14DD7900DCEA57 /* TPTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPTableView.m; sourceTree = ""; }; + A7E612E609EDC0210024712F /* SecurityInterface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SecurityInterface.framework; path = /System/Library/Frameworks/SecurityInterface.framework; sourceTree = ""; }; + A7E61DA409F176340024712F /* teleport.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = teleport.icns; sourceTree = ""; }; + A7E6351D083F7AD200BC143F /* menuicon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menuicon.png; sourceTree = ""; }; + A7E6352E0840CAEF00BC143F /* menuicon-white.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menuicon-white.png"; sourceTree = ""; }; + A7E6352F0840CAEF00BC143F /* menuicon-black.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menuicon-black.png"; sourceTree = ""; }; + A7E6ADA205EEAE2C001D7D35 /* TPLayoutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPLayoutView.h; sourceTree = ""; }; + A7E6ADA305EEAE2C001D7D35 /* TPLayoutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPLayoutView.m; sourceTree = ""; }; + A7E6ADCC05EEB223001D7D35 /* TPLocalHost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPLocalHost.h; sourceTree = ""; }; + A7E6ADCD05EEB223001D7D35 /* TPLocalHost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPLocalHost.m; sourceTree = ""; }; + A7E6ADD205EEB234001D7D35 /* TPRemoteHost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPRemoteHost.h; sourceTree = ""; }; + A7E6ADD305EEB234001D7D35 /* TPRemoteHost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPRemoteHost.m; sourceTree = ""; }; + A7E6ADD905EEB47D001D7D35 /* TPLayoutLocalHostView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPLayoutLocalHostView.h; sourceTree = ""; }; + A7E6ADDA05EEB47D001D7D35 /* TPLayoutLocalHostView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPLayoutLocalHostView.m; sourceTree = ""; }; + A7EF8B0505AB6CA900E9BC56 /* TPBezierPath.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = TPBezierPath.m; sourceTree = ""; }; + A7EF8B0605AB6CA900E9BC56 /* TPBezierPath.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = TPBezierPath.h; sourceTree = ""; }; + F506C035013D953901CA16C8 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = /System/Library/Frameworks/PreferencePanes.framework; sourceTree = ""; }; + F506C03C013D9D7901CA16C8 /* TPPreferencePane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPPreferencePane.h; sourceTree = ""; }; + F506C03D013D9D7901CA16C8 /* TPPreferencePane.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TPPreferencePane.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A7C9DEC7059F496500E67CB3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A786E23C1A19E0D900E66B43 /* SecurityInterface.framework in Frameworks */, + A7C9DF61059F4C5100E67CB3 /* Cocoa.framework in Frameworks */, + A786DB3208BE71C400BF1E8B /* Security.framework in Frameworks */, + A75ED6CC09A3FDCF00AFDD7D /* Carbon.framework in Frameworks */, + A71E0B911AB4210E00911ACB /* Sparkle.framework in Frameworks */, + A7BB55E10A1BD00E007044EA /* IOKit.framework in Frameworks */, + A78FD1AB0CB31B99006AC28C /* SecurityFoundation.framework in Frameworks */, + A7697D710DB25C8C0037F0A5 /* SystemConfiguration.framework in Frameworks */, + A703972A0F609EAA006B8621 /* QuartzCore.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 089C166AFE841209C02AAC07 /* teleport */ = { + isa = PBXGroup; + children = ( + 08FB77AFFE84173DC02AAC07 /* Sources */, + 32DBCFA10370C40200C91783 /* Other Sources */, + 089C167CFE841241C02AAC07 /* Resources */, + 089C1671FE841209C02AAC07 /* Frameworks and Libraries */, + A70AE0D814674AD100A76119 /* Products */, + ); + name = teleport; + sourceTree = ""; + usesTabs = 1; + }; + 089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */, + 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */, + ); + name = "Frameworks and Libraries"; + sourceTree = ""; + }; + 089C167CFE841241C02AAC07 /* Resources */ = { + isa = PBXGroup; + children = ( + A746D49B08B14AA100CE2B89 /* Info.plist */, + A7E61DA409F176340024712F /* teleport.icns */, + A70901850CD007B6006576A6 /* icon-about.png */, + A782102B05C0D10D00A555DC /* whitelogo.png */, + A77812110CAF331300EEC97E /* halo.png */, + A7E6351D083F7AD200BC143F /* menuicon.png */, + A7E6352E0840CAEF00BC143F /* menuicon-white.png */, + A7E6352F0840CAEF00BC143F /* menuicon-black.png */, + A7033E250CF0AB8C004AFBEA /* remove.tiff */, + A7033F3D0CF10E6C004AFBEA /* sharedscreen.tiff */, + A7BD4E860CF1D26B005B3BED /* sharedscreen-on.tiff */, + A788AA7B09F5BFCA00570E96 /* Lock_White.tif */, + A71221CC1159899E00DCD93B /* MultipleFiles.icns */, + A7C4711B0FA903C300282EF2 /* switch-sound.wav */, + A70901890CD00952006576A6 /* InfoPlist.strings */, + A7C9DF5B059F4BDA00E67CB3 /* MainMenu.nib */, + A7F1E84D0CAB155000D18B0C /* teleportPref.xib */, + A746D70308B1733600CE2B89 /* Localizable.strings */, + ); + name = Resources; + sourceTree = ""; + }; + 08FB77AFFE84173DC02AAC07 /* Sources */ = { + isa = PBXGroup; + children = ( + A7C9DECF059F4A7400E67CB3 /* common.h */, + A777B7FF0832B65B00C4D824 /* TPHostSnapping.h */, + A777B8000832B65B00C4D824 /* TPHostSnapping.m */, + A793B81808AACC3B0078CAA7 /* TPPreferencesManager.h */, + A793B81908AACC3B0078CAA7 /* TPPreferencesManager.m */, + A78C047E0E13287D00755C6D /* PTHotKeysLib */, + A7E6ADD805EEB2BA001D7D35 /* Hosts */, + A7D173380F535F3200CC6946 /* Host animation */, + A78568B709215B0C00301B93 /* Controllers */, + A78568B509215A7E00301B93 /* Back End */, + A78568B409215A7100301B93 /* Front End */, + A78569310921674500301B93 /* Debug */, + A7C9DECC059F4A4100E67CB3 /* Preferences */, + ); + name = Sources; + sourceTree = ""; + }; + 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + A71E0B901AB4210E00911ACB /* Sparkle.framework */, + 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */, + A75ED6B709A3FDCF00AFDD7D /* Carbon.framework */, + F506C035013D953901CA16C8 /* PreferencePanes.framework */, + A786DB3108BE71C400BF1E8B /* Security.framework */, + A7E612E609EDC0210024712F /* SecurityInterface.framework */, + A76426760E11BD9F00F60EA1 /* QuartzCore.framework */, + A7BB550E0A1BD00D007044EA /* IOKit.framework */, + A78FD1AA0CB31B99006AC28C /* SecurityFoundation.framework */, + A7697D700DB25C8C0037F0A5 /* SystemConfiguration.framework */, + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 089C1672FE841209C02AAC07 /* Foundation.framework */, + 089C167FFE841241C02AAC07 /* AppKit.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 32DBCFA10370C40200C91783 /* Other Sources */ = { + isa = PBXGroup; + children = ( + A7C9DF5E059F4C2500E67CB3 /* main.m */, + A7C9DF8B059F4EA700E67CB3 /* teleport_Prefix.pch */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + A70248491533265000B9F965 /* desktop-pictures */ = { + isa = PBXGroup; + children = ( + A702484A1533265000B9F965 /* 10.2-Jaguar.jpg */, + A702484B1533265000B9F965 /* 10.3-Panther.jpg */, + A702484C1533265000B9F965 /* 10.4-Tiger.jpg */, + A702484D1533265000B9F965 /* 10.5-Leopard.jpg */, + A702484E1533265000B9F965 /* 10.6-SnowLeopard.jpg */, + A702484F1533265000B9F965 /* 10.7-Lion.jpg */, + ); + path = "desktop-pictures"; + sourceTree = ""; + }; + A70AE0D814674AD100A76119 /* Products */ = { + isa = PBXGroup; + children = ( + A70AE0D714674AD100A76119 /* teleport.app */, + ); + name = Products; + sourceTree = ""; + }; + A736CF1F0923A4310008AB32 /* Utils */ = { + isa = PBXGroup; + children = ( + A736CF200923A4420008AB32 /* TPUtils.h */, + A736CF210923A4420008AB32 /* TPUtils.m */, + ); + name = Utils; + sourceTree = ""; + }; + A745F91107C038A800B295FD /* Transfers */ = { + isa = PBXGroup; + children = ( + A79ED91908959FAC00DC4A6A /* TPTransfersManager.h */, + A79ED91A08959FAC00DC4A6A /* TPTransfersManager.m */, + A745F8E007C00C5F00B295FD /* TPTransfer.h */, + A7642F4909F6DF1F00789DC0 /* TPTransfer_Private.h */, + A745F8E107C00C5F00B295FD /* TPTransfer.m */, + A745F90D07C0389600B295FD /* TPPasteboardTransfer.h */, + A745F90E07C0389600B295FD /* TPPasteboardTransfer.m */, + A745F91D07C038E000B295FD /* TPFileTransfer.h */, + A745F91E07C038E000B295FD /* TPFileTransfer.m */, + A7609654107ED026008572E5 /* TPFileSerialization.h */, + A7609655107ED026008572E5 /* TPFileSerialization.m */, + A71EA8C60A12688400805CC6 /* TPBackgroundImageTransfer.h */, + A71EA8C70A12688400805CC6 /* TPBackgroundImageTransfer.m */, + ); + name = Transfers; + sourceTree = ""; + }; + A76BE04A05CF019900FF0EA4 /* Connections */ = { + isa = PBXGroup; + children = ( + A79D9D3E09AEA25F00DCE620 /* TPConnectionsManager.h */, + A79D9D3F09AEA25F00DCE620 /* TPConnectionsManager.m */, + A760369C05A0826D00D20845 /* TPNetworkConnection.h */, + A760369D05A0826D00D20845 /* TPNetworkConnection.m */, + A73510E505A65AA90049E8CD /* TPUDPSocket.h */, + A73510E605A65AA90049E8CD /* TPUDPSocket.m */, + A75752B405ADE74000D78303 /* TPTCPSocket.h */, + A75752B505ADE74000D78303 /* TPTCPSocket.m */, + A78539B609E48D9A005C3446 /* TPTCPSecureSocket.h */, + A78539B709E48D9A005C3446 /* TPTCPSecureSocket.m */, + ); + name = Connections; + sourceTree = ""; + }; + A76BE04B05CF01A700FF0EA4 /* Protocol */ = { + isa = PBXGroup; + children = ( + A76BE04705CF016600FF0EA4 /* Protocol.h */, + A7C9DF2B059F4B1300E67CB3 /* TPMessage.h */, + A7C9DF2C059F4B1300E67CB3 /* TPMessage.m */, + ); + name = Protocol; + sourceTree = ""; + }; + A7853BDC09E4960F005C3446 /* Authentication */ = { + isa = PBXGroup; + children = ( + A7853BE209E4967C005C3446 /* TPAuthenticationManager.h */, + A7853BE109E4967C005C3446 /* TPAuthenticationManager.m */, + A7853BE309E4967C005C3446 /* TPAuthenticationRequest.h */, + A7853BE409E4967C005C3446 /* TPAuthenticationRequest.m */, + ); + name = Authentication; + sourceTree = ""; + }; + A78568B409215A7100301B93 /* Front End */ = { + isa = PBXGroup; + children = ( + A7B799CB07B82E5500C717E7 /* Hot border */, + A79EDD4A0897D64500DC4A6A /* Bezel */, + A7DD0BDD083D5F7D00DD477C /* Status Item */, + A7EF8B0605AB6CA900E9BC56 /* TPBezierPath.h */, + A7EF8B0505AB6CA900E9BC56 /* TPBezierPath.m */, + ); + name = "Front End"; + sourceTree = ""; + }; + A78568B509215A7E00301B93 /* Back End */ = { + isa = PBXGroup; + children = ( + A700510507AD3BAB0086D029 /* TPHostsManager.h */, + A700510607AD3BAB0086D029 /* TPHostsManager.m */, + A744D58205FBE08E0011E56C /* TPApplication.h */, + A744D58105FBE08E0011E56C /* TPApplication.m */, + A736CF1F0923A4310008AB32 /* Utils */, + A7C9DF3A059F4B6300E67CB3 /* Network */, + A7C9DF39059F4B5700E67CB3 /* Remote Control */, + ); + name = "Back End"; + sourceTree = ""; + }; + A78568B709215B0C00301B93 /* Controllers */ = { + isa = PBXGroup; + children = ( + A7C9DF27059F4B1300E67CB3 /* TPMainController.h */, + A7C9DF28059F4B1300E67CB3 /* TPMainController.m */, + A75752D105ADF9B900D78303 /* TPConnectionController.h */, + A75752D205ADF9B900D78303 /* TPConnectionController.m */, + A7C9DF29059F4B1300E67CB3 /* TPClientController.h */, + A7C9DF2A059F4B1300E67CB3 /* TPClientController.m */, + A7C9DF2D059F4B1300E67CB3 /* TPServerController.h */, + A7C9DF2E059F4B1300E67CB3 /* TPServerController.m */, + A7697D730DB25CCE0037F0A5 /* TPNetworkConfigurationWatcher.h */, + A7697D740DB25CCE0037F0A5 /* TPNetworkConfigurationWatcher.m */, + ); + name = Controllers; + sourceTree = ""; + }; + A78569310921674500301B93 /* Debug */ = { + isa = PBXGroup; + children = ( + A78569320921675800301B93 /* TPTestsController.h */, + A78569330921675800301B93 /* TPTestsController.m */, + A7B08FA90DCA10A3008CAFF2 /* TPFakeHostsGenerator.h */, + A7B08FAA0DCA10A3008CAFF2 /* TPFakeHostsGenerator.m */, + ); + name = Debug; + sourceTree = ""; + }; + A7856A000921719B00301B93 /* Remote Operations */ = { + isa = PBXGroup; + children = ( + A7C9DEE9059F4AA100E67CB3 /* TPRemoteOperationsController.h */, + A7C9DEEA059F4AA100E67CB3 /* TPRemoteOperationsController.m */, + ); + name = "Remote Operations"; + sourceTree = ""; + }; + A7856A01092171A500301B93 /* Event Taps */ = { + isa = PBXGroup; + children = ( + A7856A26092171C300301B93 /* TPEventTapsController.h */, + A7A819510C60047D00DE5CD4 /* TPEventTapsController_Internal.h */, + A7856A27092171C300301B93 /* TPEventTapsController.m */, + A7A819490C60030700DE5CD4 /* TPDirectEventTapsController.h */, + A7A8194A0C60030700DE5CD4 /* TPDirectEventTapsController.m */, + ); + name = "Event Taps"; + sourceTree = ""; + }; + A78C047E0E13287D00755C6D /* PTHotKeysLib */ = { + isa = PBXGroup; + children = ( + A78C048B0E13287D00755C6D /* src */, + A78C047F0E13287D00755C6D /* res */, + ); + path = PTHotKeysLib; + sourceTree = ""; + }; + A78C047F0E13287D00755C6D /* res */ = { + isa = PBXGroup; + children = ( + A79F8E94142AAAF700ED1D19 /* KeyCodes.plist */, + A78C048A0E13287D00755C6D /* PTKeyboardIcon.tiff */, + A77425610E1C41820059395D /* key.png */, + ); + path = res; + sourceTree = ""; + }; + A78C048B0E13287D00755C6D /* src */ = { + isa = PBXGroup; + children = ( + A78C048C0E13287D00755C6D /* PTHotKey.h */, + A78C048D0E13287D00755C6D /* PTHotKey.m */, + A78C048E0E13287D00755C6D /* PTHotKeyCenter.h */, + A78C048F0E13287D00755C6D /* PTHotKeyCenter.m */, + A78C04900E13287D00755C6D /* PTKeyBroadcaster.h */, + A78C04910E13287D00755C6D /* PTKeyBroadcaster.m */, + A78C04920E13287D00755C6D /* PTKeyCodeTranslator.h */, + A78C04930E13287D00755C6D /* PTKeyCodeTranslator.m */, + A78C04940E13287D00755C6D /* PTKeyCombo.h */, + A78C04950E13287D00755C6D /* PTKeyCombo.m */, + A78C04960E13287D00755C6D /* PTKeyComboPanel.h */, + A78C04970E13287D00755C6D /* PTKeyComboPanel.m */, + ); + path = src; + sourceTree = ""; + }; + A7944ACC07CE62F700EE66B6 /* Layout */ = { + isa = PBXGroup; + children = ( + A7E6ADA205EEAE2C001D7D35 /* TPLayoutView.h */, + A7E6ADA305EEAE2C001D7D35 /* TPLayoutView.m */, + A7BEF7730F8C286C00C54411 /* TPKeyComboView.h */, + A7BEF7740F8C286C00C54411 /* TPKeyComboView.m */, + A7A50B060F844F9900C5B878 /* TPOptionsController.h */, + A7A50B070F844F9900C5B878 /* TPOptionsController.m */, + A7D6388B0F213F6B000C5DB2 /* TPOptionsView.h */, + A7D6388C0F213F6B000C5DB2 /* TPOptionsView.m */, + A7D638FB0F21446A000C5DB2 /* TPAnimationManager.h */, + A7D638FC0F21446A000C5DB2 /* TPAnimationManager.m */, + A7E6ADE205EEB4AD001D7D35 /* Layout hosts */, + ); + name = Layout; + sourceTree = ""; + }; + A79EDD4A0897D64500DC4A6A /* Bezel */ = { + isa = PBXGroup; + children = ( + A79EDD4D0897D69700DC4A6A /* TPBezelController.h */, + A79EDD4E0897D69700DC4A6A /* TPBezelController.m */, + A7C9DEE5059F4AA100E67CB3 /* TPBezelView.h */, + A7C9DEE6059F4AA100E67CB3 /* TPBezelView.m */, + A7C9DEE7059F4AA100E67CB3 /* TPBezelWindow.h */, + A7C9DEE8059F4AA100E67CB3 /* TPBezelWindow.m */, + ); + name = Bezel; + sourceTree = ""; + }; + A7B799CB07B82E5500C717E7 /* Hot border */ = { + isa = PBXGroup; + children = ( + A7C9DF1B059F4AF600E67CB3 /* TPHotBorder.h */, + A7C9DF1C059F4AF600E67CB3 /* TPHotBorder.m */, + A7B799E507B8304C00C717E7 /* TPHotBorderView.h */, + A7B799E607B8304C00C717E7 /* TPHotBorderView.m */, + ); + name = "Hot border"; + sourceTree = ""; + }; + A7C9DECC059F4A4100E67CB3 /* Preferences */ = { + isa = PBXGroup; + children = ( + F506C03C013D9D7901CA16C8 /* TPPreferencePane.h */, + F506C03D013D9D7901CA16C8 /* TPPreferencePane.m */, + A7E475950A14DD7900DCEA57 /* TPTableView.h */, + A7E475960A14DD7900DCEA57 /* TPTableView.m */, + A7D9BB2605C5CEB8003DD65C /* TPClosingWindow.h */, + A7D9BB2705C5CEB8003DD65C /* TPClosingWindow.m */, + A7B5932B05F0F42000F7CA57 /* TPVersionTextField.h */, + A7B5932C05F0F42000F7CA57 /* TPVersionTextField.m */, + A7389ECB08808A29000D0DCB /* TPLinkTextField.h */, + A7389ECC08808A29000D0DCB /* TPLinkTextField.m */, + A7944ACC07CE62F700EE66B6 /* Layout */, + ); + name = Preferences; + sourceTree = ""; + }; + A7C9DF39059F4B5700E67CB3 /* Remote Control */ = { + isa = PBXGroup; + children = ( + A78569FC0921719100301B93 /* TPEventsController.h */, + A78569FD0921719100301B93 /* TPEventsController.m */, + A7856A000921719B00301B93 /* Remote Operations */, + A7856A01092171A500301B93 /* Event Taps */, + ); + name = "Remote Control"; + sourceTree = ""; + }; + A7C9DF3A059F4B6300E67CB3 /* Network */ = { + isa = PBXGroup; + children = ( + A700510907AD3C5C0086D029 /* TPBonjourController.h */, + A700510A07AD3C5C0086D029 /* TPBonjourController.m */, + A7853BDC09E4960F005C3446 /* Authentication */, + A76BE04A05CF019900FF0EA4 /* Connections */, + A76BE04B05CF01A700FF0EA4 /* Protocol */, + A745F91107C038A800B295FD /* Transfers */, + ); + name = Network; + sourceTree = ""; + }; + A7D173380F535F3200CC6946 /* Host animation */ = { + isa = PBXGroup; + children = ( + A7D173340F535F0E00CC6946 /* TPHostAnimationController.h */, + A7D173350F535F0E00CC6946 /* TPHostAnimationController.m */, + ); + name = "Host animation"; + sourceTree = ""; + }; + A7DD0BDD083D5F7D00DD477C /* Status Item */ = { + isa = PBXGroup; + children = ( + A7DD0BDE083D5F9E00DD477C /* TPStatusItemController.h */, + A7DD0BDF083D5F9E00DD477C /* TPStatusItemController.m */, + ); + name = "Status Item"; + sourceTree = ""; + }; + A7E6ADD805EEB2BA001D7D35 /* Hosts */ = { + isa = PBXGroup; + children = ( + A7614BEC0FE850210043C401 /* TPScreen.h */, + A7614BED0FE850210043C401 /* TPScreen.m */, + A7C9DF15059F4AE900E67CB3 /* TPHost.h */, + A7C9DF16059F4AE900E67CB3 /* TPHost.m */, + A7E6ADCC05EEB223001D7D35 /* TPLocalHost.h */, + A7E6ADCD05EEB223001D7D35 /* TPLocalHost.m */, + A7E6ADD205EEB234001D7D35 /* TPRemoteHost.h */, + A7E6ADD305EEB234001D7D35 /* TPRemoteHost.m */, + A70248491533265000B9F965 /* desktop-pictures */, + ); + name = Hosts; + sourceTree = ""; + }; + A7E6ADE205EEB4AD001D7D35 /* Layout hosts */ = { + isa = PBXGroup; + children = ( + A7B5934A05F17DFB00F7CA57 /* TPLayoutHostView.h */, + A7B5934B05F17DFB00F7CA57 /* TPLayoutHostView.m */, + A7C9DF1F059F4B0500E67CB3 /* TPLayoutRemoteHostView.h */, + A7C9DF20059F4B0500E67CB3 /* TPLayoutRemoteHostView.m */, + A7E6ADD905EEB47D001D7D35 /* TPLayoutLocalHostView.h */, + A7E6ADDA05EEB47D001D7D35 /* TPLayoutLocalHostView.m */, + ); + name = "Layout hosts"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + A7C9DEC4059F496500E67CB3 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A7C9DED1059F4A7400E67CB3 /* common.h in Headers */, + A7C9DF00059F4AA100E67CB3 /* TPBezelView.h in Headers */, + A7C9DF02059F4AA100E67CB3 /* TPBezelWindow.h in Headers */, + A7C9DF04059F4AA100E67CB3 /* TPRemoteOperationsController.h in Headers */, + A7C9DF17059F4AE900E67CB3 /* TPHost.h in Headers */, + A7C9DF1D059F4AF600E67CB3 /* TPHotBorder.h in Headers */, + A7C9DF2F059F4B1300E67CB3 /* TPMainController.h in Headers */, + A7C9DF31059F4B1300E67CB3 /* TPClientController.h in Headers */, + A7C9DF33059F4B1300E67CB3 /* TPMessage.h in Headers */, + A7C9DF35059F4B1300E67CB3 /* TPServerController.h in Headers */, + A7EF8B0805AB6CA900E9BC56 /* TPBezierPath.h in Headers */, + A75752B605ADE74000D78303 /* TPTCPSocket.h in Headers */, + A75752D305ADF9B900D78303 /* TPConnectionController.h in Headers */, + A76BE04905CF016600FF0EA4 /* Protocol.h in Headers */, + A7E6ADD005EEB223001D7D35 /* TPLocalHost.h in Headers */, + A7E6ADD605EEB234001D7D35 /* TPRemoteHost.h in Headers */, + A744D58405FBE08E0011E56C /* TPApplication.h in Headers */, + A72A1C5A07A464ED0012563F /* TPNetworkConnection.h in Headers */, + A700510707AD3BAB0086D029 /* TPHostsManager.h in Headers */, + A700510B07AD3C5C0086D029 /* TPBonjourController.h in Headers */, + A7B799E707B8304C00C717E7 /* TPHotBorderView.h in Headers */, + A745F8E207C00C5F00B295FD /* TPTransfer.h in Headers */, + A745F90F07C0389600B295FD /* TPPasteboardTransfer.h in Headers */, + A745F91F07C038E000B295FD /* TPFileTransfer.h in Headers */, + A777B8010832B65B00C4D824 /* TPHostSnapping.h in Headers */, + A7DD0BE2083D5F9E00DD477C /* TPStatusItemController.h in Headers */, + A79ED91B08959FAC00DC4A6A /* TPTransfersManager.h in Headers */, + A79EDD4F0897D69700DC4A6A /* TPBezelController.h in Headers */, + A793B81A08AACC3B0078CAA7 /* TPPreferencesManager.h in Headers */, + A78569340921675F00301B93 /* TPTestsController.h in Headers */, + A78569FE0921719100301B93 /* TPEventsController.h in Headers */, + A7856A28092171C300301B93 /* TPEventTapsController.h in Headers */, + A736CF220923A4420008AB32 /* TPUtils.h in Headers */, + A79D9D4009AEA25F00DCE620 /* TPConnectionsManager.h in Headers */, + A78539B809E48D9A005C3446 /* TPTCPSecureSocket.h in Headers */, + A7853BE609E4967C005C3446 /* TPAuthenticationManager.h in Headers */, + A7853BE709E4967C005C3446 /* TPAuthenticationRequest.h in Headers */, + A7642F4A09F6DF1F00789DC0 /* TPTransfer_Private.h in Headers */, + A71EA8C80A12688400805CC6 /* TPBackgroundImageTransfer.h in Headers */, + A7A8194E0C60042800DE5CD4 /* TPDirectEventTapsController.h in Headers */, + A7A819520C60047D00DE5CD4 /* TPEventTapsController_Internal.h in Headers */, + A7697D750DB25CCE0037F0A5 /* TPNetworkConfigurationWatcher.h in Headers */, + A7B08FAB0DCA10A3008CAFF2 /* TPFakeHostsGenerator.h in Headers */, + A78C04AA0E13287D00755C6D /* PTHotKey.h in Headers */, + A78C04AC0E13287D00755C6D /* PTHotKeyCenter.h in Headers */, + A78C04AE0E13287D00755C6D /* PTKeyBroadcaster.h in Headers */, + A78C04B00E13287D00755C6D /* PTKeyCodeTranslator.h in Headers */, + A78C04B20E13287D00755C6D /* PTKeyCombo.h in Headers */, + A78C04B40E13287D00755C6D /* PTKeyComboPanel.h in Headers */, + A7D173360F535F0E00CC6946 /* TPHostAnimationController.h in Headers */, + A7614BEE0FE850210043C401 /* TPScreen.h in Headers */, + A7609656107ED026008572E5 /* TPFileSerialization.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + A7C9DEC8059F496500E67CB3 /* teleport */ = { + isa = PBXNativeTarget; + buildConfigurationList = A729278308564D7C00518F7E /* Build configuration list for PBXNativeTarget "teleport" */; + buildPhases = ( + A7C9DEC4059F496500E67CB3 /* Headers */, + A786E22C1A19DFA500E66B43 /* Update strings */, + A7C9DEC5059F496500E67CB3 /* Resources */, + A731475715CEE635000D7ED4 /* Copy frameworks */, + A7C9DEC6059F496500E67CB3 /* Sources */, + A7C9DEC7059F496500E67CB3 /* Frameworks */, + A71EA0750A10D5B400805CC6 /* Set revision number */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = teleport; + productName = Daemon; + productReference = A70AE0D714674AD100A76119 /* teleport.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 089C1669FE841209C02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0700; + }; + buildConfigurationList = A729278F08564D7C00518F7E /* Build configuration list for PBXProject "teleport" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + it, + Spanish, + Danish, + Czech, + Italian, + Base, + ); + mainGroup = 089C166AFE841209C02AAC07 /* teleport */; + productRefGroup = A70AE0D814674AD100A76119 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A7C9DEC8059F496500E67CB3 /* teleport */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A7C9DEC5059F496500E67CB3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A743A23008B2B6CD006B9FB2 /* MainMenu.nib in Resources */, + A7EC1BD51A19E6CC00CDEC7B /* teleportPref.xib in Resources */, + A782102C05C0D10D00A555DC /* whitelogo.png in Resources */, + A746D70508B1733600CE2B89 /* Localizable.strings in Resources */, + A7E6351E083F7AD200BC143F /* menuicon.png in Resources */, + A7E635300840CAEF00BC143F /* menuicon-white.png in Resources */, + A7E635310840CAEF00BC143F /* menuicon-black.png in Resources */, + A7E61DA509F176340024712F /* teleport.icns in Resources */, + A7EC1BD61A19E82E00CDEC7B /* icon-about.png in Resources */, + A788AA7C09F5BFCA00570E96 /* Lock_White.tif in Resources */, + A7EC1BD81A19E83D00CDEC7B /* sharedscreen.tiff in Resources */, + A77812120CAF331300EEC97E /* halo.png in Resources */, + A7EC1BD91A19E84000CDEC7B /* sharedscreen-on.tiff in Resources */, + A7C4711C0FA903C300282EF2 /* switch-sound.wav in Resources */, + A7EC1BD71A19E83A00CDEC7B /* remove.tiff in Resources */, + A71221CD1159899E00DCD93B /* MultipleFiles.icns in Resources */, + A79F8E95142AAAF700ED1D19 /* KeyCodes.plist in Resources */, + A702485615332CD800B9F965 /* 10.2-Jaguar.jpg in Resources */, + A702485715332CD800B9F965 /* 10.3-Panther.jpg in Resources */, + A702485815332CD800B9F965 /* 10.4-Tiger.jpg in Resources */, + A702485915332CD800B9F965 /* 10.5-Leopard.jpg in Resources */, + A702485A15332CD800B9F965 /* 10.6-SnowLeopard.jpg in Resources */, + A702485B15332CD800B9F965 /* 10.7-Lion.jpg in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + A71EA0750A10D5B400805CC6 /* Set revision number */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Set revision number"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/usr/bin/perl -w"; + shellScript = "# Xcode auto-versioning script for Subversion\n# by Axel Andersson, modified by Daniel Jalkut to add\n# \"--revision HEAD\" to the svn info line, which allows\n# the latest revision to always be used.\n\nuse strict;\n\ndie \"$0: Must be run from Xcode\" unless $ENV{\"BUILT_PRODUCTS_DIR\"};\n\nmy $REV = `git describe`;\nmy $INFO = \"$ENV{BUILT_PRODUCTS_DIR}/$ENV{WRAPPER_NAME}/Contents/Info.plist\";\n\nmy $version = $REV;\n\nopen(FH, \"$INFO\") or die \"$0: $INFO: $!\";\nmy $info = join(\"\", );\nclose(FH);\n\n$info =~ s/([\\t ]+TPRevision<\\/key>\\n[\\t ]+).*?(<\\/string>)/$1$version$2/;\n\nopen(FH, \">$INFO\") or die \"$0: $INFO: $!\";\nprint FH $info;\nclose(FH);"; + }; + A786E22C1A19DFA500E66B43 /* Update strings */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Update strings"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"Updating string files\"\ngenstrings -u -littleEndian -o English.lproj *.m"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A7C9DEC6059F496500E67CB3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A7C9DF01059F4AA100E67CB3 /* TPBezelView.m in Sources */, + A7C9DF03059F4AA100E67CB3 /* TPBezelWindow.m in Sources */, + A7C9DF05059F4AA100E67CB3 /* TPRemoteOperationsController.m in Sources */, + A7C9DF18059F4AE900E67CB3 /* TPHost.m in Sources */, + A7C9DF1E059F4AF600E67CB3 /* TPHotBorder.m in Sources */, + A7C9DF30059F4B1300E67CB3 /* TPMainController.m in Sources */, + A7C9DF32059F4B1300E67CB3 /* TPClientController.m in Sources */, + A7C9DF34059F4B1300E67CB3 /* TPMessage.m in Sources */, + A7C9DF36059F4B1300E67CB3 /* TPServerController.m in Sources */, + A7C9DF5F059F4C2500E67CB3 /* main.m in Sources */, + A7EF8B0705AB6CA900E9BC56 /* TPBezierPath.m in Sources */, + A75752B705ADE74000D78303 /* TPTCPSocket.m in Sources */, + A75752D405ADF9B900D78303 /* TPConnectionController.m in Sources */, + A7E6ADD105EEB223001D7D35 /* TPLocalHost.m in Sources */, + A744D58305FBE08E0011E56C /* TPApplication.m in Sources */, + A72A1C5B07A464EE0012563F /* TPNetworkConnection.m in Sources */, + A700510807AD3BAB0086D029 /* TPHostsManager.m in Sources */, + A700510C07AD3C5C0086D029 /* TPBonjourController.m in Sources */, + A786E2351A19E09200E66B43 /* TPKeyComboView.m in Sources */, + A7B799E807B8304C00C717E7 /* TPHotBorderView.m in Sources */, + A745F8E307C00C5F00B295FD /* TPTransfer.m in Sources */, + A786E2371A19E09200E66B43 /* TPOptionsView.m in Sources */, + A745F91007C0389600B295FD /* TPPasteboardTransfer.m in Sources */, + A786E2361A19E09200E66B43 /* TPOptionsController.m in Sources */, + A786E2311A19E08100E66B43 /* TPClosingWindow.m in Sources */, + A745F92007C038E000B295FD /* TPFileTransfer.m in Sources */, + A777B8020832B65B00C4D824 /* TPHostSnapping.m in Sources */, + A786E22F1A19E06E00E66B43 /* TPPreferencePane.m in Sources */, + A7DD0BE3083D5F9E00DD477C /* TPStatusItemController.m in Sources */, + A79ED91C08959FAC00DC4A6A /* TPTransfersManager.m in Sources */, + A79EDD500897D69700DC4A6A /* TPBezelController.m in Sources */, + A715B6BA1AA191B200BC2EDE /* TPRemoteHost.m in Sources */, + A793B81B08AACC3B0078CAA7 /* TPPreferencesManager.m in Sources */, + A78569350921675F00301B93 /* TPTestsController.m in Sources */, + A786E2341A19E09200E66B43 /* TPLayoutView.m in Sources */, + A786E2391A19E09200E66B43 /* TPLayoutHostView.m in Sources */, + A786E23B1A19E09200E66B43 /* TPLayoutLocalHostView.m in Sources */, + A786E2301A19E07D00E66B43 /* TPTableView.m in Sources */, + A78569FF0921719100301B93 /* TPEventsController.m in Sources */, + A7856A29092171C300301B93 /* TPEventTapsController.m in Sources */, + A736CF230923A4420008AB32 /* TPUtils.m in Sources */, + A786E2381A19E09200E66B43 /* TPAnimationManager.m in Sources */, + A79D9D4109AEA25F00DCE620 /* TPConnectionsManager.m in Sources */, + A78539B909E48D9A005C3446 /* TPTCPSecureSocket.m in Sources */, + A7853BE509E4967C005C3446 /* TPAuthenticationManager.m in Sources */, + A7853BE809E4967C005C3446 /* TPAuthenticationRequest.m in Sources */, + A71EA8C90A12688400805CC6 /* TPBackgroundImageTransfer.m in Sources */, + A786E2321A19E08400E66B43 /* TPVersionTextField.m in Sources */, + A7A8194F0C60042900DE5CD4 /* TPDirectEventTapsController.m in Sources */, + A7697D760DB25CCE0037F0A5 /* TPNetworkConfigurationWatcher.m in Sources */, + A7B08FAC0DCA10A3008CAFF2 /* TPFakeHostsGenerator.m in Sources */, + A78C04AB0E13287D00755C6D /* PTHotKey.m in Sources */, + A78C04AD0E13287D00755C6D /* PTHotKeyCenter.m in Sources */, + A78C04AF0E13287D00755C6D /* PTKeyBroadcaster.m in Sources */, + A78C04B10E13287D00755C6D /* PTKeyCodeTranslator.m in Sources */, + A786E2331A19E08700E66B43 /* TPLinkTextField.m in Sources */, + A78C04B30E13287D00755C6D /* PTKeyCombo.m in Sources */, + A786E23A1A19E09200E66B43 /* TPLayoutRemoteHostView.m in Sources */, + A78C04B50E13287D00755C6D /* PTKeyComboPanel.m in Sources */, + A7D173370F535F0E00CC6946 /* TPHostAnimationController.m in Sources */, + A7614BEF0FE850210043C401 /* TPScreen.m in Sources */, + A7609657107ED026008572E5 /* TPFileSerialization.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + A70901890CD00952006576A6 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 089C167EFE841241C02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + A746D70308B1733600CE2B89 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + A79C91D406DCF86A0044D87C /* English */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + A7F1E84D0CAB155000D18B0C /* teleportPref.xib */ = { + isa = PBXVariantGroup; + children = ( + A786E22D1A19E00300E66B43 /* Base */, + ); + name = teleportPref.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + A729278408564D7C00518F7E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = abyssoft; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Versions/A/Frameworks\"", + "$(SRCROOT)", + "$(PROJECT_DIR)", + ); + GCC_PREFIX_HEADER = teleport_Prefix.pch; + INFOPLIST_FILE = Info.plist; + OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../Frameworks"; + PRODUCT_NAME = teleport; + }; + name = Release; + }; + A729278508564D7C00518F7E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = abyssoft; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Versions/A/Frameworks\"", + "$(SRCROOT)", + "$(PROJECT_DIR)", + ); + GCC_PREFIX_HEADER = teleport_Prefix.pch; + INFOPLIST_FILE = Info.plist; + OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../Frameworks"; + PRODUCT_NAME = teleport; + }; + name = Debug; + }; + A729279008564D7C00518F7E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_DYNAMIC_NO_PIC = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREPROCESSOR_DEFINITIONS = "DEBUG_BUILD=0"; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + SDKROOT = macosx; + }; + name = Release; + }; + A729279108564D7C00518F7E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREPROCESSOR_DEFINITIONS = "DEBUG_BUILD=1"; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + A751657309314763008F278D /* Tests */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = abyssoft; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "\"$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Versions/A/Frameworks\"", + "$(SRCROOT)", + "$(PROJECT_DIR)", + ); + GCC_PREFIX_HEADER = teleport_Prefix.pch; + INFOPLIST_FILE = Info.plist; + OTHER_LDFLAGS = "-Wl,-rpath,@loader_path/../Frameworks"; + PRODUCT_NAME = teleport; + }; + name = Tests; + }; + A751657609314763008F278D /* Tests */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG_BUILD=1", + "RUN_TESTS=1", + ); + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + SDKROOT = macosx; + }; + name = Tests; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A729278308564D7C00518F7E /* Build configuration list for PBXNativeTarget "teleport" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A729278508564D7C00518F7E /* Debug */, + A729278408564D7C00518F7E /* Release */, + A751657309314763008F278D /* Tests */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + A729278F08564D7C00518F7E /* Build configuration list for PBXProject "teleport" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A729279108564D7C00518F7E /* Debug */, + A729279008564D7C00518F7E /* Release */, + A751657609314763008F278D /* Tests */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; +/* End XCConfigurationList section */ + }; + rootObject = 089C1669FE841209C02AAC07 /* Project object */; +} diff --git a/teleport.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/teleport.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..ae52f28 --- /dev/null +++ b/teleport.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/teleport_Prefix.pch b/teleport_Prefix.pch new file mode 100644 index 0000000..4b562f7 --- /dev/null +++ b/teleport_Prefix.pch @@ -0,0 +1,13 @@ +// +// Prefix header for all source files of the 'teleport' target in the 'teleport' project. +// + +#ifdef __OBJC__ + #import + #import + #import + + #import "common.h" + #import "Protocol.h" + #import "TPUtils.h" +#endif diff --git a/version.plist b/version.plist new file mode 100644 index 0000000..c12b708 --- /dev/null +++ b/version.plist @@ -0,0 +1,16 @@ + + + + + BuildVersion + 54 + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1.0 + ProjectName + PreferencePaneTemplate + SourceVersion + 120000 + + diff --git a/whitelogo.png b/whitelogo.png new file mode 100644 index 0000000..42dbbfb Binary files /dev/null and b/whitelogo.png differ