From 3ebdb1bd7ae38bf0fb205dfaa2f5fde3d67ea141 Mon Sep 17 00:00:00 2001 From: nealsid Date: Wed, 1 Apr 2009 03:18:49 +0000 Subject: [PATCH] Open sourcing the Breakpad framework from Google. A=many, many people R=nealsid, jeremy moskovich(from Chromium project) git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@322 4c0a9323-5329-0410-9bdc-e9ce6186880e --- .../mac/Breakpad.xcodeproj/project.pbxproj | 1937 +++++++++++++ src/client/mac/Framework/Breakpad.h | 181 ++ src/client/mac/Framework/Breakpad.mm | 915 ++++++ src/client/mac/Framework/Breakpad_Prefix.pch | 8 + .../Framework/English.lproj/InfoPlist.strings | Bin 0 -> 92 bytes src/client/mac/Framework/Info.plist | 26 + src/client/mac/Framework/OnDemandServer.h | 146 + src/client/mac/Framework/OnDemandServer.mm | 145 + src/client/mac/UnitTests-Info.plist | 20 + src/client/mac/crash_generation/Inspector.h | 200 ++ src/client/mac/crash_generation/Inspector.mm | 431 +++ .../mac/crash_generation/InspectorMain.mm | 65 + src/client/mac/gcov/libgcov.a | Bin 0 -> 35048 bytes src/client/mac/handler/exception_handler.cc | 1 - .../mac/handler/exception_handler_test.cc | 10 +- .../mac/handler/minidump_generator_test.cc | 7 +- .../mac/sender/English.lproj/Breakpad.xib | 834 ++++++ .../sender/English.lproj/Localizable.strings | Bin 0 -> 1360 bytes src/client/mac/sender/ReporterIcon.graffle | 2489 +++++++++++++++++ src/client/mac/sender/ReporterIcon.icns | Bin 0 -> 170816 bytes .../mac/sender/crash_report_sender-Info.plist | 24 + src/client/mac/sender/crash_report_sender.h | 89 + src/client/mac/sender/crash_report_sender.m | 762 +++++ src/client/mac/testapp/Controller.h | 65 + src/client/mac/testapp/Controller.m | 257 ++ .../testapp/English.lproj/InfoPlist.strings | Bin 0 -> 192 bytes .../English.lproj/MainMenu.nib/classes.nib | 47 + .../English.lproj/MainMenu.nib/info.nib | 22 + .../MainMenu.nib/keyedobjects.nib | Bin 0 -> 34374 bytes src/client/mac/testapp/Info.plist | 46 + src/client/mac/testapp/TestClass.h | 37 + src/client/mac/testapp/TestClass.mm | 95 + src/client/mac/testapp/bomb.icns | Bin 0 -> 23659 bytes src/client/mac/testapp/crashInMain | Bin 0 -> 12588 bytes src/client/mac/testapp/crashduringload | Bin 0 -> 12588 bytes src/client/mac/testapp/main.m | 34 + .../mac/tests/SimpleStringDictionaryTest.h | 40 + .../mac/tests/SimpleStringDictionaryTest.mm | 243 ++ src/client/minidump_file_writer_unittest.cc | 28 +- src/common/mac/GTMDefines.h | 241 ++ src/common/mac/GTMGarbageCollection.h | 72 + src/common/mac/GTMLogger.h | 458 +++ src/common/mac/GTMLogger.m | 445 +++ src/common/mac/MachIPC.h | 304 ++ src/common/mac/MachIPC.mm | 297 ++ src/common/mac/SimpleStringDictionary.h | 195 ++ src/common/mac/SimpleStringDictionary.mm | 131 + src/common/mac/testing/GTMSenTestCase.h | 1004 +++++++ src/common/mac/testing/GTMSenTestCase.m | 366 +++ 49 files changed, 12695 insertions(+), 22 deletions(-) create mode 100644 src/client/mac/Breakpad.xcodeproj/project.pbxproj create mode 100644 src/client/mac/Framework/Breakpad.h create mode 100644 src/client/mac/Framework/Breakpad.mm create mode 100644 src/client/mac/Framework/Breakpad_Prefix.pch create mode 100644 src/client/mac/Framework/English.lproj/InfoPlist.strings create mode 100644 src/client/mac/Framework/Info.plist create mode 100644 src/client/mac/Framework/OnDemandServer.h create mode 100644 src/client/mac/Framework/OnDemandServer.mm create mode 100644 src/client/mac/UnitTests-Info.plist create mode 100644 src/client/mac/crash_generation/Inspector.h create mode 100644 src/client/mac/crash_generation/Inspector.mm create mode 100644 src/client/mac/crash_generation/InspectorMain.mm create mode 100644 src/client/mac/gcov/libgcov.a create mode 100644 src/client/mac/sender/English.lproj/Breakpad.xib create mode 100644 src/client/mac/sender/English.lproj/Localizable.strings create mode 100644 src/client/mac/sender/ReporterIcon.graffle create mode 100644 src/client/mac/sender/ReporterIcon.icns create mode 100644 src/client/mac/sender/crash_report_sender-Info.plist create mode 100644 src/client/mac/sender/crash_report_sender.h create mode 100644 src/client/mac/sender/crash_report_sender.m create mode 100644 src/client/mac/testapp/Controller.h create mode 100644 src/client/mac/testapp/Controller.m create mode 100644 src/client/mac/testapp/English.lproj/InfoPlist.strings create mode 100644 src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib create mode 100644 src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib create mode 100644 src/client/mac/testapp/English.lproj/MainMenu.nib/keyedobjects.nib create mode 100644 src/client/mac/testapp/Info.plist create mode 100644 src/client/mac/testapp/TestClass.h create mode 100644 src/client/mac/testapp/TestClass.mm create mode 100644 src/client/mac/testapp/bomb.icns create mode 100755 src/client/mac/testapp/crashInMain create mode 100755 src/client/mac/testapp/crashduringload create mode 100644 src/client/mac/testapp/main.m create mode 100644 src/client/mac/tests/SimpleStringDictionaryTest.h create mode 100644 src/client/mac/tests/SimpleStringDictionaryTest.mm create mode 100644 src/common/mac/GTMDefines.h create mode 100644 src/common/mac/GTMGarbageCollection.h create mode 100644 src/common/mac/GTMLogger.h create mode 100644 src/common/mac/GTMLogger.m create mode 100644 src/common/mac/MachIPC.h create mode 100644 src/common/mac/MachIPC.mm create mode 100644 src/common/mac/SimpleStringDictionary.h create mode 100644 src/common/mac/SimpleStringDictionary.mm create mode 100644 src/common/mac/testing/GTMSenTestCase.h create mode 100644 src/common/mac/testing/GTMSenTestCase.m diff --git a/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/src/client/mac/Breakpad.xcodeproj/project.pbxproj new file mode 100644 index 00000000..1794529a --- /dev/null +++ b/src/client/mac/Breakpad.xcodeproj/project.pbxproj @@ -0,0 +1,1937 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 44; + objects = { + +/* Begin PBXAggregateTarget section */ + F94585840F782326009A47BF /* All */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F94585930F78235C009A47BF /* Build configuration list for PBXAggregateTarget "All" */; + buildPhases = ( + ); + dependencies = ( + F94585880F78232B009A47BF /* PBXTargetDependency */, + F945858A0F78232E009A47BF /* PBXTargetDependency */, + F945858C0F782330009A47BF /* PBXTargetDependency */, + F945858E0F782333009A47BF /* PBXTargetDependency */, + F94585900F782336009A47BF /* PBXTargetDependency */, + F93DE3A70F830D1D00608B94 /* PBXTargetDependency */, + ); + name = All; + productName = All; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 4084699D0F5D9CF900FDCA37 /* ReporterIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4084699C0F5D9CF900FDCA37 /* ReporterIcon.icns */; }; + 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; + F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */; }; + F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53B70ECCE7B3009BE4BA /* Inspector.mm */; }; + F92C554C0ECCF534009BE4BA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; + F92C55D00ECD0064009BE4BA /* Breakpad.h in Headers */ = {isa = PBXBuildFile; fileRef = F92C55CE0ECD0064009BE4BA /* Breakpad.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F92C55D10ECD0064009BE4BA /* Breakpad.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C55CF0ECD0064009BE4BA /* Breakpad.mm */; }; + F92C56330ECD0DF1009BE4BA /* OnDemandServer.h in Headers */ = {isa = PBXBuildFile; fileRef = F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */; }; + F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */; }; + F92C563F0ECD10CA009BE4BA /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; }; + F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; }; + F92C56410ECD10CA009BE4BA /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; }; + F92C56420ECD10CA009BE4BA /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; }; + F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; }; + F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; }; + F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; }; + F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; }; + F92C56480ECD10CA009BE4BA /* SimpleStringDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */; }; + F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; + F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F92C564C0ECD10DD009BE4BA /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; }; + F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C554A0ECCF530009BE4BA /* Carbon.framework */; }; + F92C565C0ECD1158009BE4BA /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; }; + F92C565F0ECD116B009BE4BA /* protected_memory_allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */; }; + F92C56630ECD1179009BE4BA /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; }; + F92C56650ECD1185009BE4BA /* breakpadUtilities.dylib in Resources */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; }; + F92C568A0ECD15F9009BE4BA /* Inspector in Resources */ = {isa = PBXBuildFile; fileRef = F92C53540ECCE349009BE4BA /* Inspector */; }; + F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */ = {isa = PBXBuildFile; fileRef = F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */; }; + F93803CD0F8083B7004D428B /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; }; + F93803CE0F8083B7004D428B /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; }; + F93803CF0F8083B7004D428B /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; }; + F93803D00F8083B7004D428B /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F93803D10F8083B7004D428B /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; }; + F93803D20F8083B7004D428B /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F93803D30F8083B7004D428B /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; }; + F93803D40F8083B7004D428B /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; }; + F93803D50F8083B7004D428B /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; }; + F93803D60F8083B7004D428B /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; }; + F93803D70F8083B7004D428B /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; + F93803DA0F8083D8004D428B /* minidump_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93803D90F8083D8004D428B /* minidump_generator_test.cc */; }; + F93DE2D80F82A70E00608B94 /* minidump_file_writer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */; }; + F93DE2D90F82A73500608B94 /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F93DE2DA0F82A73500608B94 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; }; + F93DE2DB0F82A73500608B94 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F93DE3350F82C66B00608B94 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; }; + F93DE3360F82C66B00608B94 /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; }; + F93DE3370F82C66B00608B94 /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; }; + F93DE3380F82C66B00608B94 /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F93DE3390F82C66B00608B94 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; }; + F93DE33A0F82C66B00608B94 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F93DE33B0F82C66B00608B94 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; }; + F93DE33C0F82C66B00608B94 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; }; + F93DE33D0F82C66B00608B94 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; }; + F93DE33E0F82C66B00608B94 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; }; + F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; + F93DE3410F82C68300608B94 /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93DE3400F82C68300608B94 /* exception_handler_test.cc */; }; + F945849E0F280E3C009A47BF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F945849C0F280E3C009A47BF /* Localizable.strings */; }; + F94586220F7842CF009A47BF /* Breakpad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40F2AEEB0F4F2F55004510E8 /* Breakpad.xib */; }; + F9C444530F7981E600991B96 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F9C444510F7981E600991B96 /* InfoPlist.strings */; }; + F9C44DB20EF07288003AEBAA /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DAC0EF07288003AEBAA /* Controller.m */; }; + F9C44DB30EF07288003AEBAA /* crashduringload in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAD0EF07288003AEBAA /* crashduringload */; }; + F9C44DB40EF07288003AEBAA /* crashInMain in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAE0EF07288003AEBAA /* crashInMain */; }; + F9C44DB60EF07288003AEBAA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB00EF07288003AEBAA /* main.m */; }; + F9C44DB70EF07288003AEBAA /* TestClass.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB10EF07288003AEBAA /* TestClass.mm */; }; + F9C44DBC0EF072A0003AEBAA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */; }; + F9C44DBD0EF072A0003AEBAA /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */; }; + F9C44E000EF077CD003AEBAA /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; }; + F9C44E3C0EF08B12003AEBAA /* Breakpad.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; }; + F9C44E980EF09F56003AEBAA /* crash_report_sender.app in Resources */ = {isa = PBXBuildFile; fileRef = F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */; }; + F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */; }; + F9C44EE50EF0A006003AEBAA /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */; }; + F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */; }; + F9C77DE20F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9C77DE10F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm */; }; + F9C77DE40F7DD82F0045F7DB /* SimpleStringDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */; }; + F9C77E130F7DDF810045F7DB /* GTMSenTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + F92C564D0ECD10E5009BE4BA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C563B0ECD10B3009BE4BA; + remoteInfo = breakpadUtilities; + }; + F92C56850ECD15EF009BE4BA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C563B0ECD10B3009BE4BA; + remoteInfo = breakpadUtilities; + }; + F92C56870ECD15F1009BE4BA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C53530ECCE349009BE4BA; + remoteInfo = Inspector; + }; + F93DE2FB0F82C3C600608B94 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93803BD0F80820F004D428B /* generator_test */; + remoteInfo = generator_test; + }; + F93DE2FD0F82C3C900608B94 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */; + remoteInfo = minidump_file_writer_unittest; + }; + F93DE36F0F82CC1300608B94 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93DE32B0F82C55600608B94 /* handler_test */; + remoteInfo = handler_test; + }; + F93DE3A60F830D1D00608B94 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9C77DD90F7DD5CF0045F7DB /* UnitTests */; + remoteInfo = UnitTests; + }; + F94585870F78232B009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8DC2EF4F0486A6940098B216; + remoteInfo = Breakpad; + }; + F94585890F78232E009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C53530ECCE349009BE4BA; + remoteInfo = Inspector; + }; + F945858B0F782330009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C563B0ECD10B3009BE4BA; + remoteInfo = breakpadUtilities; + }; + F945858D0F782333009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C569F0ECE04A7009BE4BA; + remoteInfo = crash_report_sender; + }; + F945858F0F782336009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9C44DA40EF060A8003AEBAA; + remoteInfo = BreakpadTest; + }; + F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8DC2EF4F0486A6940098B216; + remoteInfo = Breakpad; + }; + F9C44E960EF09F4B003AEBAA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C569F0ECE04A7009BE4BA; + remoteInfo = crash_report_sender; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + F9C44E410EF08B17003AEBAA /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + F9C44E3C0EF08B12003AEBAA /* Breakpad.framework in Copy Frameworks */, + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad_Prefix.pch; path = Framework/Breakpad_Prefix.pch; sourceTree = ""; }; + 4084699C0F5D9CF900FDCA37 /* ReporterIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = ReporterIcon.icns; path = sender/ReporterIcon.icns; sourceTree = ""; }; + 40F2AEEC0F4F2F55004510E8 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = sender/English.lproj/Breakpad.xib; sourceTree = ""; }; + 8DC2EF5B0486A6940098B216 /* Breakpad.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Breakpad.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F9286B380F7EB25800A4DCC8 /* Inspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Inspector.h; path = crash_generation/Inspector.h; sourceTree = ""; }; + F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = InspectorMain.mm; path = crash_generation/InspectorMain.mm; sourceTree = ""; }; + F92C53540ECCE349009BE4BA /* Inspector */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Inspector; sourceTree = BUILT_PRODUCTS_DIR; }; + F92C53670ECCE3FD009BE4BA /* breakpad_exc_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = breakpad_exc_server.c; path = handler/breakpad_exc_server.c; sourceTree = SOURCE_ROOT; }; + F92C53680ECCE3FD009BE4BA /* breakpad_exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = breakpad_exc_server.h; path = handler/breakpad_exc_server.h; sourceTree = SOURCE_ROOT; }; + F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = breakpad_nlist_64.cc; path = handler/breakpad_nlist_64.cc; sourceTree = SOURCE_ROOT; }; + F92C536A0ECCE3FD009BE4BA /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = breakpad_nlist_64.h; path = handler/breakpad_nlist_64.h; sourceTree = SOURCE_ROOT; }; + F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dynamic_images.cc; path = handler/dynamic_images.cc; sourceTree = SOURCE_ROOT; }; + F92C536C0ECCE3FD009BE4BA /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dynamic_images.h; path = handler/dynamic_images.h; sourceTree = SOURCE_ROOT; }; + F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler.cc; path = handler/exception_handler.cc; sourceTree = SOURCE_ROOT; }; + F92C536E0ECCE3FD009BE4BA /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = exception_handler.h; path = handler/exception_handler.h; sourceTree = SOURCE_ROOT; }; + F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator.cc; path = handler/minidump_generator.cc; sourceTree = SOURCE_ROOT; }; + F92C53700ECCE3FD009BE4BA /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = minidump_generator.h; path = handler/minidump_generator.h; sourceTree = SOURCE_ROOT; }; + F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = protected_memory_allocator.cc; path = handler/protected_memory_allocator.cc; sourceTree = SOURCE_ROOT; }; + F92C53730ECCE3FD009BE4BA /* protected_memory_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = protected_memory_allocator.h; path = handler/protected_memory_allocator.h; sourceTree = SOURCE_ROOT; }; + F92C53740ECCE635009BE4BA /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../common/mac/file_id.cc; sourceTree = SOURCE_ROOT; }; + F92C53750ECCE635009BE4BA /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../common/mac/file_id.h; sourceTree = SOURCE_ROOT; }; + F92C53760ECCE635009BE4BA /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPMultipartUpload.h; path = ../../common/mac/HTTPMultipartUpload.h; sourceTree = SOURCE_ROOT; }; + F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HTTPMultipartUpload.m; path = ../../common/mac/HTTPMultipartUpload.m; sourceTree = SOURCE_ROOT; }; + F92C53780ECCE635009BE4BA /* MachIPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MachIPC.h; path = ../../common/mac/MachIPC.h; sourceTree = SOURCE_ROOT; }; + F92C53790ECCE635009BE4BA /* MachIPC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MachIPC.mm; path = ../../common/mac/MachIPC.mm; sourceTree = SOURCE_ROOT; }; + F92C537A0ECCE635009BE4BA /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../common/mac/macho_id.cc; sourceTree = SOURCE_ROOT; }; + F92C537B0ECCE635009BE4BA /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; }; + F92C537C0ECCE635009BE4BA /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../common/mac/macho_utilities.cc; sourceTree = SOURCE_ROOT; }; + F92C537D0ECCE635009BE4BA /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../common/mac/macho_utilities.h; sourceTree = SOURCE_ROOT; }; + F92C537E0ECCE635009BE4BA /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; }; + F92C537F0ECCE635009BE4BA /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; }; + F92C53800ECCE635009BE4BA /* SimpleStringDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SimpleStringDictionary.h; path = ../../common/mac/SimpleStringDictionary.h; sourceTree = SOURCE_ROOT; }; + F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SimpleStringDictionary.mm; path = ../../common/mac/SimpleStringDictionary.mm; sourceTree = SOURCE_ROOT; }; + F92C53820ECCE635009BE4BA /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_utilities.cc; path = ../../common/mac/string_utilities.cc; sourceTree = SOURCE_ROOT; }; + F92C53830ECCE635009BE4BA /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = string_utilities.h; path = ../../common/mac/string_utilities.h; sourceTree = SOURCE_ROOT; }; + F92C53850ECCE6AD009BE4BA /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_conversion.cc; path = ../../common/string_conversion.cc; sourceTree = SOURCE_ROOT; }; + F92C53860ECCE6AD009BE4BA /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = string_conversion.h; path = ../../common/string_conversion.h; sourceTree = SOURCE_ROOT; }; + F92C53870ECCE6C0009BE4BA /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = convert_UTF.c; path = ../../common/convert_UTF.c; sourceTree = SOURCE_ROOT; }; + F92C53880ECCE6C0009BE4BA /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = convert_UTF.h; path = ../../common/convert_UTF.h; sourceTree = SOURCE_ROOT; }; + F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "minidump_file_writer-inl.h"; path = "../minidump_file_writer-inl.h"; sourceTree = SOURCE_ROOT; }; + F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer.cc; path = ../minidump_file_writer.cc; sourceTree = SOURCE_ROOT; }; + F92C53900ECCE70A009BE4BA /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = minidump_file_writer.h; path = ../minidump_file_writer.h; sourceTree = SOURCE_ROOT; }; + F92C53B70ECCE7B3009BE4BA /* Inspector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Inspector.mm; path = crash_generation/Inspector.mm; sourceTree = SOURCE_ROOT; }; + F92C554A0ECCF530009BE4BA /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; + F92C55CE0ECD0064009BE4BA /* Breakpad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad.h; path = Framework/Breakpad.h; sourceTree = ""; }; + F92C55CF0ECD0064009BE4BA /* Breakpad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Breakpad.mm; path = Framework/Breakpad.mm; sourceTree = ""; }; + F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OnDemandServer.h; path = Framework/OnDemandServer.h; sourceTree = ""; }; + F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = OnDemandServer.mm; path = Framework/OnDemandServer.mm; sourceTree = ""; }; + F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = breakpadUtilities.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = crash_report_sender.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "crash_report_sender-Info.plist"; path = "sender/crash_report_sender-Info.plist"; sourceTree = ""; }; + F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crash_report_sender.h; path = sender/crash_report_sender.h; sourceTree = ""; }; + F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = crash_report_sender.m; path = sender/crash_report_sender.m; sourceTree = ""; }; + F93803BE0F80820F004D428B /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; }; + F93803D90F8083D8004D428B /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator_test.cc; path = handler/minidump_generator_test.cc; sourceTree = ""; }; + F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = minidump_file_writer_unittest; sourceTree = BUILT_PRODUCTS_DIR; }; + F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../minidump_file_writer_unittest.cc; sourceTree = SOURCE_ROOT; }; + F93DE32C0F82C55600608B94 /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; }; + F93DE3400F82C68300608B94 /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler_test.cc; path = handler/exception_handler_test.cc; sourceTree = ""; }; + F945849D0F280E3C009A47BF /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/Localizable.strings; sourceTree = ""; }; + F945859D0F78241E009A47BF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = ""; }; + F9C444520F7981E600991B96 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = Framework/English.lproj/InfoPlist.strings; sourceTree = ""; }; + F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BreakpadTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F9C44DAC0EF07288003AEBAA /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Controller.m; path = testapp/Controller.m; sourceTree = ""; }; + F9C44DAD0EF07288003AEBAA /* crashduringload */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashduringload; path = testapp/crashduringload; sourceTree = ""; }; + F9C44DAE0EF07288003AEBAA /* crashInMain */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashInMain; path = testapp/crashInMain; sourceTree = ""; }; + F9C44DAF0EF07288003AEBAA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = ""; }; + F9C44DB00EF07288003AEBAA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = testapp/main.m; sourceTree = ""; }; + F9C44DB10EF07288003AEBAA /* TestClass.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TestClass.mm; path = testapp/TestClass.mm; sourceTree = ""; }; + F9C44DB90EF072A0003AEBAA /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = testapp/English.lproj/InfoPlist.strings; sourceTree = ""; }; + F9C44DBB0EF072A0003AEBAA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = testapp/English.lproj/MainMenu.nib; sourceTree = ""; }; + F9C44DBF0EF0778F003AEBAA /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Controller.h; path = testapp/Controller.h; sourceTree = ""; }; + F9C44DC00EF0778F003AEBAA /* TestClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestClass.h; path = testapp/TestClass.h; sourceTree = ""; }; + F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = ""; }; + F9C44EE70EF0A3C1003AEBAA /* GTMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMLogger.h; path = ../../common/mac/GTMLogger.h; sourceTree = SOURCE_ROOT; }; + F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMLogger.m; path = ../../common/mac/GTMLogger.m; sourceTree = SOURCE_ROOT; }; + F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; + F9C77DDB0F7DD5CF0045F7DB /* UnitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "UnitTests-Info.plist"; sourceTree = ""; }; + F9C77DE00F7DD7E30045F7DB /* SimpleStringDictionaryTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SimpleStringDictionaryTest.h; path = tests/SimpleStringDictionaryTest.h; sourceTree = ""; }; + F9C77DE10F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SimpleStringDictionaryTest.mm; path = tests/SimpleStringDictionaryTest.mm; sourceTree = ""; }; + F9C77E110F7DDF810045F7DB /* GTMSenTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMSenTestCase.h; path = ../../common/mac/testing/GTMSenTestCase.h; sourceTree = SOURCE_ROOT; }; + F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMSenTestCase.m; path = ../../common/mac/testing/GTMSenTestCase.m; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DC2EF560486A6940098B216 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C565C0ECD1158009BE4BA /* breakpadUtilities.dylib in Frameworks */, + 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C53520ECCE349009BE4BA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C564C0ECD10DD009BE4BA /* breakpadUtilities.dylib in Frameworks */, + F92C554C0ECCF534009BE4BA /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C563A0ECD10B3009BE4BA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C569E0ECE04A7009BE4BA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44EE50EF0A006003AEBAA /* SystemConfiguration.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93803BC0F80820F004D428B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE2CF0F82A67300608B94 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE32A0F82C55600608B94 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C44DA30EF060A8003AEBAA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44E000EF077CD003AEBAA /* Breakpad.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C77DD70F7DD5CF0045F7DB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 034768DFFF38A50411DB9C8B /* Products */ = { + isa = PBXGroup; + children = ( + 8DC2EF5B0486A6940098B216 /* Breakpad.framework */, + F92C53540ECCE349009BE4BA /* Inspector */, + F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */, + F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */, + F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */, + F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */, + F93803BE0F80820F004D428B /* generator_test */, + F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */, + F93DE32C0F82C55600608B94 /* handler_test */, + ); + name = Products; + sourceTree = ""; + }; + 0867D691FE84028FC02AAC07 /* Breakpad */ = { + isa = PBXGroup; + children = ( + 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */, + F92C538D0ECCE6F2009BE4BA /* client */, + F92C53600ECCE3D6009BE4BA /* common */, + 0867D69AFE84028FC02AAC07 /* Frameworks */, + 034768DFFF38A50411DB9C8B /* Products */, + F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */, + F9C77DDB0F7DD5CF0045F7DB /* UnitTests-Info.plist */, + ); + name = Breakpad; + sourceTree = ""; + }; + 0867D69AFE84028FC02AAC07 /* Frameworks */ = { + isa = PBXGroup; + children = ( + F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */, + F92C554A0ECCF530009BE4BA /* Carbon.framework */, + 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */, + 0867D6A5FE840307C02AAC07 /* AppKit.framework */, + 0867D69BFE84028FC02AAC07 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + F92C53590ECCE3BB009BE4BA /* handler */ = { + isa = PBXGroup; + children = ( + F93DE3400F82C68300608B94 /* exception_handler_test.cc */, + F93803D90F8083D8004D428B /* minidump_generator_test.cc */, + F92C53670ECCE3FD009BE4BA /* breakpad_exc_server.c */, + F92C53680ECCE3FD009BE4BA /* breakpad_exc_server.h */, + F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */, + F92C536A0ECCE3FD009BE4BA /* breakpad_nlist_64.h */, + F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */, + F92C536C0ECCE3FD009BE4BA /* dynamic_images.h */, + F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */, + F92C536E0ECCE3FD009BE4BA /* exception_handler.h */, + F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */, + F92C53700ECCE3FD009BE4BA /* minidump_generator.h */, + F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */, + F92C53730ECCE3FD009BE4BA /* protected_memory_allocator.h */, + ); + name = handler; + sourceTree = ""; + }; + F92C53600ECCE3D6009BE4BA /* common */ = { + isa = PBXGroup; + children = ( + F92C53870ECCE6C0009BE4BA /* convert_UTF.c */, + F92C53880ECCE6C0009BE4BA /* convert_UTF.h */, + F92C53850ECCE6AD009BE4BA /* string_conversion.cc */, + F92C53860ECCE6AD009BE4BA /* string_conversion.h */, + F92C53840ECCE68D009BE4BA /* mac */, + ); + name = common; + sourceTree = ""; + }; + F92C53840ECCE68D009BE4BA /* mac */ = { + isa = PBXGroup; + children = ( + F9C77E0F0F7DDF650045F7DB /* testing */, + F9C44EE70EF0A3C1003AEBAA /* GTMLogger.h */, + F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */, + F92C53740ECCE635009BE4BA /* file_id.cc */, + F92C53750ECCE635009BE4BA /* file_id.h */, + F92C53760ECCE635009BE4BA /* HTTPMultipartUpload.h */, + F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */, + F92C53780ECCE635009BE4BA /* MachIPC.h */, + F92C53790ECCE635009BE4BA /* MachIPC.mm */, + F92C537A0ECCE635009BE4BA /* macho_id.cc */, + F92C537B0ECCE635009BE4BA /* macho_id.h */, + F92C537C0ECCE635009BE4BA /* macho_utilities.cc */, + F92C537D0ECCE635009BE4BA /* macho_utilities.h */, + F92C537E0ECCE635009BE4BA /* macho_walker.cc */, + F92C537F0ECCE635009BE4BA /* macho_walker.h */, + F92C53800ECCE635009BE4BA /* SimpleStringDictionary.h */, + F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */, + F92C53820ECCE635009BE4BA /* string_utilities.cc */, + F92C53830ECCE635009BE4BA /* string_utilities.h */, + ); + name = mac; + sourceTree = ""; + }; + F92C538D0ECCE6F2009BE4BA /* client */ = { + isa = PBXGroup; + children = ( + F92C53990ECCE78E009BE4BA /* mac */, + F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */, + F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */, + F92C53900ECCE70A009BE4BA /* minidump_file_writer.h */, + F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */, + ); + name = client; + sourceTree = ""; + }; + F92C53990ECCE78E009BE4BA /* mac */ = { + isa = PBXGroup; + children = ( + F9C77DDF0F7DD7CF0045F7DB /* tests */, + F9C44DAB0EF0726F003AEBAA /* testapp */, + F92C56A60ECE04B6009BE4BA /* sender */, + F92C55CD0ECD0053009BE4BA /* Framework */, + F92C53B50ECCE799009BE4BA /* crash_generation */, + F92C53590ECCE3BB009BE4BA /* handler */, + ); + name = mac; + sourceTree = ""; + }; + F92C53B50ECCE799009BE4BA /* crash_generation */ = { + isa = PBXGroup; + children = ( + F9286B380F7EB25800A4DCC8 /* Inspector.h */, + F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */, + F92C53B70ECCE7B3009BE4BA /* Inspector.mm */, + ); + name = crash_generation; + sourceTree = ""; + }; + F92C55CD0ECD0053009BE4BA /* Framework */ = { + isa = PBXGroup; + children = ( + F9C444510F7981E600991B96 /* InfoPlist.strings */, + F945859D0F78241E009A47BF /* Info.plist */, + F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */, + F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */, + F92C55CE0ECD0064009BE4BA /* Breakpad.h */, + F92C55CF0ECD0064009BE4BA /* Breakpad.mm */, + ); + name = Framework; + sourceTree = ""; + }; + F92C56A60ECE04B6009BE4BA /* sender */ = { + isa = PBXGroup; + children = ( + 4084699C0F5D9CF900FDCA37 /* ReporterIcon.icns */, + 40F2AEEB0F4F2F55004510E8 /* Breakpad.xib */, + F945849C0F280E3C009A47BF /* Localizable.strings */, + F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */, + F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */, + ); + name = sender; + sourceTree = ""; + }; + F9C44DAB0EF0726F003AEBAA /* testapp */ = { + isa = PBXGroup; + children = ( + F9C44DBF0EF0778F003AEBAA /* Controller.h */, + F9C44DC00EF0778F003AEBAA /* TestClass.h */, + F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */, + F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */, + F9C44DAC0EF07288003AEBAA /* Controller.m */, + F9C44DAD0EF07288003AEBAA /* crashduringload */, + F9C44DAE0EF07288003AEBAA /* crashInMain */, + F9C44DAF0EF07288003AEBAA /* Info.plist */, + F9C44DB00EF07288003AEBAA /* main.m */, + F9C44DB10EF07288003AEBAA /* TestClass.mm */, + ); + name = testapp; + sourceTree = ""; + }; + F9C77DDF0F7DD7CF0045F7DB /* tests */ = { + isa = PBXGroup; + children = ( + F9C77DE00F7DD7E30045F7DB /* SimpleStringDictionaryTest.h */, + F9C77DE10F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm */, + ); + name = tests; + sourceTree = ""; + }; + F9C77E0F0F7DDF650045F7DB /* testing */ = { + isa = PBXGroup; + children = ( + F9C77E110F7DDF810045F7DB /* GTMSenTestCase.h */, + F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */, + ); + name = testing; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8DC2EF500486A6940098B216 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C55D00ECD0064009BE4BA /* Breakpad.h in Headers */, + F92C56330ECD0DF1009BE4BA /* OnDemandServer.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C56380ECD10B3009BE4BA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 8DC2EF4F0486A6940098B216 /* Breakpad */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */; + buildPhases = ( + F97A0E850ED4EC15008784D3 /* Change install name of breakpadUtilities */, + 8DC2EF500486A6940098B216 /* Headers */, + 8DC2EF520486A6940098B216 /* Resources */, + 8DC2EF540486A6940098B216 /* Sources */, + 8DC2EF560486A6940098B216 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + F92C56860ECD15EF009BE4BA /* PBXTargetDependency */, + F92C56880ECD15F1009BE4BA /* PBXTargetDependency */, + F9C44E970EF09F4B003AEBAA /* PBXTargetDependency */, + ); + name = Breakpad; + productInstallPath = "$(HOME)/Library/Frameworks"; + productName = Breakpad; + productReference = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; + productType = "com.apple.product-type.framework"; + }; + F92C53530ECCE349009BE4BA /* Inspector */ = { + isa = PBXNativeTarget; + buildConfigurationList = F92C53580ECCE36D009BE4BA /* Build configuration list for PBXNativeTarget "Inspector" */; + buildPhases = ( + F94584840F27FB40009A47BF /* Change install name of breakpadUtilities */, + F92C53510ECCE349009BE4BA /* Sources */, + F92C53520ECCE349009BE4BA /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + F92C564E0ECD10E5009BE4BA /* PBXTargetDependency */, + ); + name = Inspector; + productName = Inspector; + productReference = F92C53540ECCE349009BE4BA /* Inspector */; + productType = "com.apple.product-type.tool"; + }; + F92C563B0ECD10B3009BE4BA /* breakpadUtilities */ = { + isa = PBXNativeTarget; + buildConfigurationList = F92C56670ECD11A3009BE4BA /* Build configuration list for PBXNativeTarget "breakpadUtilities" */; + buildPhases = ( + F92C56380ECD10B3009BE4BA /* Headers */, + F92C56390ECD10B3009BE4BA /* Sources */, + F92C563A0ECD10B3009BE4BA /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = breakpadUtilities; + productName = breakpadUtilities; + productReference = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + F92C569F0ECE04A7009BE4BA /* crash_report_sender */ = { + isa = PBXNativeTarget; + buildConfigurationList = F92C56A50ECE04A8009BE4BA /* Build configuration list for PBXNativeTarget "crash_report_sender" */; + buildPhases = ( + F92C569C0ECE04A7009BE4BA /* Resources */, + F92C569D0ECE04A7009BE4BA /* Sources */, + F92C569E0ECE04A7009BE4BA /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = crash_report_sender; + productName = crash_report_sender; + productReference = F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */; + productType = "com.apple.product-type.application"; + }; + F93803BD0F80820F004D428B /* generator_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93803C40F80822E004D428B /* Build configuration list for PBXNativeTarget "generator_test" */; + buildPhases = ( + F93803BB0F80820F004D428B /* Sources */, + F93803BC0F80820F004D428B /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = generator_test; + productName = generator_test; + productReference = F93803BE0F80820F004D428B /* generator_test */; + productType = "com.apple.product-type.tool"; + }; + F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93DE2D60F82A67700608B94 /* Build configuration list for PBXNativeTarget "minidump_file_writer_unittest" */; + buildPhases = ( + F93DE2CE0F82A67300608B94 /* Sources */, + F93DE2CF0F82A67300608B94 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = minidump_file_writer_unittest; + productName = minidump_file_writer_unittest; + productReference = F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */; + productType = "com.apple.product-type.tool"; + }; + F93DE32B0F82C55600608B94 /* handler_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93DE3320F82C5D800608B94 /* Build configuration list for PBXNativeTarget "handler_test" */; + buildPhases = ( + F93DE3290F82C55600608B94 /* Sources */, + F93DE32A0F82C55600608B94 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = handler_test; + productName = handler_test; + productReference = F93DE32C0F82C55600608B94 /* handler_test */; + productType = "com.apple.product-type.tool"; + }; + F9C44DA40EF060A8003AEBAA /* BreakpadTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9C44DAA0EF060A9003AEBAA /* Build configuration list for PBXNativeTarget "BreakpadTest" */; + buildPhases = ( + F9C44DA10EF060A8003AEBAA /* Resources */, + F9C44DA20EF060A8003AEBAA /* Sources */, + F9C44DA30EF060A8003AEBAA /* Frameworks */, + F9C44E410EF08B17003AEBAA /* Copy Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */, + ); + name = BreakpadTest; + productName = BreakpadTest; + productReference = F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */; + productType = "com.apple.product-type.application"; + }; + F9C77DD90F7DD5CF0045F7DB /* UnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9C77DDE0F7DD5D00045F7DB /* Build configuration list for PBXNativeTarget "UnitTests" */; + buildPhases = ( + F9C77DD50F7DD5CF0045F7DB /* Resources */, + F9C77DD60F7DD5CF0045F7DB /* Sources */, + F9C77DD70F7DD5CF0045F7DB /* Frameworks */, + F9C77DD80F7DD5CF0045F7DB /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */, + F93DE2FE0F82C3C900608B94 /* PBXTargetDependency */, + F93DE3700F82CC1300608B94 /* PBXTargetDependency */, + ); + name = UnitTests; + productName = UnitTests; + productReference = F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0867D690FE84028FC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */; + compatibilityVersion = "Xcode 3.0"; + hasScannedForEncodings = 1; + mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */; + productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DC2EF4F0486A6940098B216 /* Breakpad */, + F92C53530ECCE349009BE4BA /* Inspector */, + F92C563B0ECD10B3009BE4BA /* breakpadUtilities */, + F92C569F0ECE04A7009BE4BA /* crash_report_sender */, + F9C44DA40EF060A8003AEBAA /* BreakpadTest */, + F94585840F782326009A47BF /* All */, + F9C77DD90F7DD5CF0045F7DB /* UnitTests */, + F93803BD0F80820F004D428B /* generator_test */, + F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */, + F93DE32B0F82C55600608B94 /* handler_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8DC2EF520486A6940098B216 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44E980EF09F56003AEBAA /* crash_report_sender.app in Resources */, + F92C568A0ECD15F9009BE4BA /* Inspector in Resources */, + F92C56650ECD1185009BE4BA /* breakpadUtilities.dylib in Resources */, + F9C444530F7981E600991B96 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C569C0ECE04A7009BE4BA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F94586220F7842CF009A47BF /* Breakpad.xib in Resources */, + F945849E0F280E3C009A47BF /* Localizable.strings in Resources */, + 4084699D0F5D9CF900FDCA37 /* ReporterIcon.icns in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C44DA10EF060A8003AEBAA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44DB30EF07288003AEBAA /* crashduringload in Resources */, + F9C44DB40EF07288003AEBAA /* crashInMain in Resources */, + F9C44DBC0EF072A0003AEBAA /* InfoPlist.strings in Resources */, + F9C44DBD0EF072A0003AEBAA /* MainMenu.nib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C77DD50F7DD5CF0045F7DB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + F94584840F27FB40009A47BF /* Change install name of breakpadUtilities */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Change install name of breakpadUtilities"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "install_name_tool -id \"@executable_path/../Resources/breakpadUtilities.dylib\" ${BUILT_PRODUCTS_DIR}/breakpadUtilities.dylib\n"; + }; + F97A0E850ED4EC15008784D3 /* Change install name of breakpadUtilities */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Change install name of breakpadUtilities"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/bin/bash\ninstall_name_tool -id \"@executable_path/../Frameworks/Breakpad.framework/Resources/breakpadUtilities.dylib\" ${BUILT_PRODUCTS_DIR}/breakpadUtilities.dylib\n"; + }; + F9C77DD80F7DD5CF0045F7DB /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\necho running minidump generator tests...\n\"${BUILT_PRODUCTS_DIR}/generator_test\"\necho Running minidump file writer tests...\n\"${BUILT_PRODUCTS_DIR}/minidump_file_writer_unittest\"\necho Running exception handler tests...\n\"${BUILT_PRODUCTS_DIR}/handler_test\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DC2EF540486A6940098B216 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C565F0ECD116B009BE4BA /* protected_memory_allocator.cc in Sources */, + F92C56630ECD1179009BE4BA /* exception_handler.cc in Sources */, + F92C55D10ECD0064009BE4BA /* Breakpad.mm in Sources */, + F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C53510ECCE349009BE4BA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */, + F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C56390ECD10B3009BE4BA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C563F0ECD10CA009BE4BA /* convert_UTF.c in Sources */, + F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */, + F92C56410ECD10CA009BE4BA /* file_id.cc in Sources */, + F92C56420ECD10CA009BE4BA /* macho_id.cc in Sources */, + F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */, + F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */, + F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */, + F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */, + F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */, + F92C56480ECD10CA009BE4BA /* SimpleStringDictionary.mm in Sources */, + F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */, + F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C569D0ECE04A7009BE4BA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */, + F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */, + F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93803BB0F80820F004D428B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F93803CD0F8083B7004D428B /* dynamic_images.cc in Sources */, + F93803CE0F8083B7004D428B /* exception_handler.cc in Sources */, + F93803CF0F8083B7004D428B /* minidump_generator.cc in Sources */, + F93803D00F8083B7004D428B /* minidump_file_writer.cc in Sources */, + F93803D10F8083B7004D428B /* convert_UTF.c in Sources */, + F93803D20F8083B7004D428B /* string_conversion.cc in Sources */, + F93803D30F8083B7004D428B /* file_id.cc in Sources */, + F93803D40F8083B7004D428B /* macho_id.cc in Sources */, + F93803D50F8083B7004D428B /* macho_utilities.cc in Sources */, + F93803D60F8083B7004D428B /* macho_walker.cc in Sources */, + F93803D70F8083B7004D428B /* string_utilities.cc in Sources */, + F93803DA0F8083D8004D428B /* minidump_generator_test.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE2CE0F82A67300608B94 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F93DE2D90F82A73500608B94 /* minidump_file_writer.cc in Sources */, + F93DE2DA0F82A73500608B94 /* convert_UTF.c in Sources */, + F93DE2DB0F82A73500608B94 /* string_conversion.cc in Sources */, + F93DE2D80F82A70E00608B94 /* minidump_file_writer_unittest.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE3290F82C55600608B94 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F93DE3350F82C66B00608B94 /* dynamic_images.cc in Sources */, + F93DE3360F82C66B00608B94 /* exception_handler.cc in Sources */, + F93DE3370F82C66B00608B94 /* minidump_generator.cc in Sources */, + F93DE3380F82C66B00608B94 /* minidump_file_writer.cc in Sources */, + F93DE3390F82C66B00608B94 /* convert_UTF.c in Sources */, + F93DE33A0F82C66B00608B94 /* string_conversion.cc in Sources */, + F93DE33B0F82C66B00608B94 /* file_id.cc in Sources */, + F93DE33C0F82C66B00608B94 /* macho_id.cc in Sources */, + F93DE33D0F82C66B00608B94 /* macho_utilities.cc in Sources */, + F93DE33E0F82C66B00608B94 /* macho_walker.cc in Sources */, + F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */, + F93DE3410F82C68300608B94 /* exception_handler_test.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C44DA20EF060A8003AEBAA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44DB20EF07288003AEBAA /* Controller.m in Sources */, + F9C44DB60EF07288003AEBAA /* main.m in Sources */, + F9C44DB70EF07288003AEBAA /* TestClass.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C77DD60F7DD5CF0045F7DB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C77DE40F7DD82F0045F7DB /* SimpleStringDictionary.mm in Sources */, + F9C77DE20F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm in Sources */, + F9C77E130F7DDF810045F7DB /* GTMSenTestCase.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F92C564E0ECD10E5009BE4BA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */; + targetProxy = F92C564D0ECD10E5009BE4BA /* PBXContainerItemProxy */; + }; + F92C56860ECD15EF009BE4BA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */; + targetProxy = F92C56850ECD15EF009BE4BA /* PBXContainerItemProxy */; + }; + F92C56880ECD15F1009BE4BA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C53530ECCE349009BE4BA /* Inspector */; + targetProxy = F92C56870ECD15F1009BE4BA /* PBXContainerItemProxy */; + }; + F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93803BD0F80820F004D428B /* generator_test */; + targetProxy = F93DE2FB0F82C3C600608B94 /* PBXContainerItemProxy */; + }; + F93DE2FE0F82C3C900608B94 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */; + targetProxy = F93DE2FD0F82C3C900608B94 /* PBXContainerItemProxy */; + }; + F93DE3700F82CC1300608B94 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93DE32B0F82C55600608B94 /* handler_test */; + targetProxy = F93DE36F0F82CC1300608B94 /* PBXContainerItemProxy */; + }; + F93DE3A70F830D1D00608B94 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9C77DD90F7DD5CF0045F7DB /* UnitTests */; + targetProxy = F93DE3A60F830D1D00608B94 /* PBXContainerItemProxy */; + }; + F94585880F78232B009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8DC2EF4F0486A6940098B216 /* Breakpad */; + targetProxy = F94585870F78232B009A47BF /* PBXContainerItemProxy */; + }; + F945858A0F78232E009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C53530ECCE349009BE4BA /* Inspector */; + targetProxy = F94585890F78232E009A47BF /* PBXContainerItemProxy */; + }; + F945858C0F782330009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */; + targetProxy = F945858B0F782330009A47BF /* PBXContainerItemProxy */; + }; + F945858E0F782333009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C569F0ECE04A7009BE4BA /* crash_report_sender */; + targetProxy = F945858D0F782333009A47BF /* PBXContainerItemProxy */; + }; + F94585900F782336009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9C44DA40EF060A8003AEBAA /* BreakpadTest */; + targetProxy = F945858F0F782336009A47BF /* PBXContainerItemProxy */; + }; + F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8DC2EF4F0486A6940098B216 /* Breakpad */; + targetProxy = F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */; + }; + F9C44E970EF09F4B003AEBAA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C569F0ECE04A7009BE4BA /* crash_report_sender */; + targetProxy = F9C44E960EF09F4B003AEBAA /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 40F2AEEB0F4F2F55004510E8 /* Breakpad.xib */ = { + isa = PBXVariantGroup; + children = ( + 40F2AEEC0F4F2F55004510E8 /* English */, + ); + name = Breakpad.xib; + sourceTree = ""; + }; + F945849C0F280E3C009A47BF /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + F945849D0F280E3C009A47BF /* English */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + F9C444510F7981E600991B96 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + F9C444520F7981E600991B96 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + F9C44DB90EF072A0003AEBAA /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */ = { + isa = PBXVariantGroup; + children = ( + F9C44DBB0EF072A0003AEBAA /* English */, + ); + name = MainMenu.nib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1DEB91AE08733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = Framework/Info.plist; + INSTALL_PATH = "@executable_path/../Frameworks"; + PRODUCT_NAME = Breakpad; + WRAPPER_EXTENSION = framework; + }; + name = Debug; + }; + 1DEB91AF08733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = Framework/Info.plist; + INSTALL_PATH = "@executable_path/../Frameworks"; + PRODUCT_NAME = Breakpad; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; + WRAPPER_EXTENSION = framework; + }; + name = Release; + }; + 1DEB91B208733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ONLY_ACTIVE_ARCH_PRE_XCODE_3_1)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH_PRE_XCODE_3_1 = "$(NATIVE_ARCH)"; + PREBINDING = NO; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; + }; + name = Debug; + }; + 1DEB91B308733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "../..//**"; + PREBINDING = NO; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; + }; + name = Release; + }; + F92C53560ECCE34A009BE4BA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "$(inherited)", + ); + PREBINDING = NO; + PRODUCT_NAME = Inspector; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; + }; + name = Debug; + }; + F92C53570ECCE34A009BE4BA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/bin; + LD_GENERATE_MAP_FILE = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-lcrypto", + ); + PREBINDING = NO; + PRODUCT_NAME = Inspector; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; + ZERO_LINK = NO; + }; + name = Release; + }; + F92C563D0ECD10B3009BE4BA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/lib; + LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-headerpad_max_install_names", + "-lcrypto", + ); + PREBINDING = NO; + PRODUCT_NAME = breakpadUtilities; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; + }; + name = Debug; + }; + F92C563E0ECD10B3009BE4BA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/lib; + LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-headerpad_max_install_names", + "-lcrypto", + ); + PREBINDING = NO; + PRODUCT_NAME = breakpadUtilities; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; + ZERO_LINK = NO; + }; + name = Release; + }; + F92C56A30ECE04A8009BE4BA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = "sender/crash_report_sender-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = crash_report_sender; + }; + name = Debug; + }; + F92C56A40ECE04A8009BE4BA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = "sender/crash_report_sender-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = crash_report_sender; + ZERO_LINK = NO; + }; + name = Release; + }; + F93803C00F808210004D428B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = generator_test; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = Debug; + }; + F93803C10F808210004D428B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = generator_test; + ZERO_LINK = NO; + }; + name = Release; + }; + F93DE2D30F82A67400608B94 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_CHAR_IS_UNSIGNED_CHAR = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = minidump_file_writer_unittest; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = Debug; + }; + F93DE2D40F82A67400608B94 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = minidump_file_writer_unittest; + ZERO_LINK = NO; + }; + name = Release; + }; + F93DE32E0F82C55700608B94 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = handler_test; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = Debug; + }; + F93DE32F0F82C55700608B94 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = handler_test; + ZERO_LINK = NO; + }; + name = Release; + }; + F93DE3B90F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ONLY_ACTIVE_ARCH_PRE_XCODE_3_1)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH_PRE_XCODE_3_1 = "$(NATIVE_ARCH)"; + PREBINDING = NO; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BA0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = Framework/Info.plist; + INSTALL_PATH = "@executable_path/../Frameworks"; + PRODUCT_NAME = Breakpad; + WRAPPER_EXTENSION = framework; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BB0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "$(inherited)", + ); + PREBINDING = NO; + PRODUCT_NAME = Inspector; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BC0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/lib; + LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-headerpad_max_install_names", + "-lcrypto", + ); + PREBINDING = NO; + PRODUCT_NAME = breakpadUtilities; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BD0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = "sender/crash_report_sender-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = crash_report_sender; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BE0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; + INFOPLIST_FILE = testapp/Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = BreakpadTest; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BF0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = All; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C00F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Cocoa.framework/Headers/Cocoa.h"; + INFOPLIST_FILE = "UnitTests-Info.plist"; + INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles"; + LIBRARY_SEARCH_PATHS = ./gcov; + OTHER_LDFLAGS = ( + "-lgcov", + "-framework", + Cocoa, + "-framework", + SenTestingKit, + ); + PREBINDING = NO; + PRODUCT_NAME = UnitTests; + USER_HEADER_SEARCH_PATHS = "../../ ../../common/mac/**"; + WRAPPER_EXTENSION = octest; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C10F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + LIBRARY_SEARCH_PATHS = ./gcov; + OTHER_LDFLAGS = ( + "-lgcov", + "-lcrypto", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = generator_test; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C20F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_CHAR_IS_UNSIGNED_CHAR = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + LIBRARY_SEARCH_PATHS = ./gcov; + OTHER_LDFLAGS = ( + "-lgcov", + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = minidump_file_writer_unittest; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C30F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + LIBRARY_SEARCH_PATHS = ./gcov; + OTHER_LDFLAGS = ( + "-lcrypto", + "-lgcov", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = handler_test; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = "Debug With Code Coverage"; + }; + F94585850F782326009A47BF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = All; + }; + name = Debug; + }; + F94585860F782326009A47BF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = All; + ZERO_LINK = NO; + }; + name = Release; + }; + F9C44DA80EF060A8003AEBAA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; + INFOPLIST_FILE = testapp/Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = BreakpadTest; + }; + name = Debug; + }; + F9C44DA90EF060A8003AEBAA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + INFOPLIST_FILE = testapp/Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = BreakpadTest; + ZERO_LINK = NO; + }; + name = Release; + }; + F9C77DDC0F7DD5D00045F7DB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Cocoa.framework/Headers/Cocoa.h"; + INFOPLIST_FILE = "UnitTests-Info.plist"; + INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles"; + OTHER_LDFLAGS = ( + "-framework", + Cocoa, + "-framework", + SenTestingKit, + ); + PREBINDING = NO; + PRODUCT_NAME = UnitTests; + USER_HEADER_SEARCH_PATHS = "../../ ../../common/mac/**"; + WRAPPER_EXTENSION = octest; + }; + name = Debug; + }; + F9C77DDD0F7DD5D00045F7DB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_MODEL_TUNING = G5; + INFOPLIST_FILE = "UnitTests-Info.plist"; + INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles"; + OTHER_LDFLAGS = ( + "-framework", + Cocoa, + "-framework", + SenTestingKit, + ); + PREBINDING = NO; + PRODUCT_NAME = UnitTests; + WRAPPER_EXTENSION = octest; + ZERO_LINK = NO; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91AE08733DA50010E9CD /* Debug */, + F93DE3BA0F830E7000608B94 /* Debug With Code Coverage */, + 1DEB91AF08733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91B208733DA50010E9CD /* Debug */, + F93DE3B90F830E7000608B94 /* Debug With Code Coverage */, + 1DEB91B308733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F92C53580ECCE36D009BE4BA /* Build configuration list for PBXNativeTarget "Inspector" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F92C53560ECCE34A009BE4BA /* Debug */, + F93DE3BB0F830E7000608B94 /* Debug With Code Coverage */, + F92C53570ECCE34A009BE4BA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F92C56670ECD11A3009BE4BA /* Build configuration list for PBXNativeTarget "breakpadUtilities" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F92C563D0ECD10B3009BE4BA /* Debug */, + F93DE3BC0F830E7000608B94 /* Debug With Code Coverage */, + F92C563E0ECD10B3009BE4BA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F92C56A50ECE04A8009BE4BA /* Build configuration list for PBXNativeTarget "crash_report_sender" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F92C56A30ECE04A8009BE4BA /* Debug */, + F93DE3BD0F830E7000608B94 /* Debug With Code Coverage */, + F92C56A40ECE04A8009BE4BA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93803C40F80822E004D428B /* Build configuration list for PBXNativeTarget "generator_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93803C00F808210004D428B /* Debug */, + F93DE3C10F830E7000608B94 /* Debug With Code Coverage */, + F93803C10F808210004D428B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93DE2D60F82A67700608B94 /* Build configuration list for PBXNativeTarget "minidump_file_writer_unittest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93DE2D30F82A67400608B94 /* Debug */, + F93DE3C20F830E7000608B94 /* Debug With Code Coverage */, + F93DE2D40F82A67400608B94 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93DE3320F82C5D800608B94 /* Build configuration list for PBXNativeTarget "handler_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93DE32E0F82C55700608B94 /* Debug */, + F93DE3C30F830E7000608B94 /* Debug With Code Coverage */, + F93DE32F0F82C55700608B94 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F94585930F78235C009A47BF /* Build configuration list for PBXAggregateTarget "All" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F94585850F782326009A47BF /* Debug */, + F93DE3BF0F830E7000608B94 /* Debug With Code Coverage */, + F94585860F782326009A47BF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9C44DAA0EF060A9003AEBAA /* Build configuration list for PBXNativeTarget "BreakpadTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9C44DA80EF060A8003AEBAA /* Debug */, + F93DE3BE0F830E7000608B94 /* Debug With Code Coverage */, + F9C44DA90EF060A8003AEBAA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9C77DDE0F7DD5D00045F7DB /* Build configuration list for PBXNativeTarget "UnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9C77DDC0F7DD5D00045F7DB /* Debug */, + F93DE3C00F830E7000608B94 /* Debug With Code Coverage */, + F9C77DDD0F7DD5D00045F7DB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} diff --git a/src/client/mac/Framework/Breakpad.h b/src/client/mac/Framework/Breakpad.h new file mode 100644 index 00000000..05005a99 --- /dev/null +++ b/src/client/mac/Framework/Breakpad.h @@ -0,0 +1,181 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Framework to provide a simple C API to crash reporting for +// applications. By default, if any machine-level exception (e.g., +// EXC_BAD_ACCESS) occurs, it will be handled by the BreakpadRef +// object as follows: +// +// 1. Create a minidump file (see Breakpad for details) +// 2. Prompt the user (using CFUserNotification) +// 3. Invoke a command line reporting tool to send the minidump to a +// server +// +// By specifying parameters to the BreakpadCreate function, you can +// modify the default behavior to suit your needs and wants and +// desires. + +typedef void *BreakpadRef; + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + + // Keys for configuration file +#define kReporterMinidumpDirectoryKey "MinidumpDir" +#define kReporterMinidumpIDKey "MinidumpID" + +// Specify some special keys to be used in the configuration file that is +// generated by Breakpad and consumed by the crash_sender. +#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay" +#define BREAKPAD_PRODUCT "BreakpadProduct" +#define BREAKPAD_VENDOR "BreakpadVendor" +#define BREAKPAD_VERSION "BreakpadVersion" +#define BREAKPAD_URL "BreakpadURL" +#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval" +#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm" +#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit" +#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation" + +#define BREAKPAD_REPORTER_EXE_LOCATION \ + "BreakpadReporterExeLocation" +#define BREAKPAD_LOGFILES "BreakpadLogFiles" +#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize" +#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile" +#define BREAKPAD_EMAIL "BreakpadEmail" +#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments" +#define BREAKPAD_COMMENTS "BreakpadComments" + +// Optional user-defined function to dec to decide if we should handle +// this crash or forward it along. +// Return true if you want Breakpad to handle it. +// Return false if you want Breakpad to skip it +// The exception handler always returns false, as if SEND_AND_EXIT were false +// (which means the next exception handler will take the exception) +typedef bool (*BreakpadFilterCallback)(int exception_type, + int exception_code, + mach_port_t crashing_thread); + +// Create a new BreakpadRef object and install it as an +// exception handler. The |parameters| will typically be the contents +// of your bundle's Info.plist. +// +// You can also specify these additional keys for customizable behavior: +// Key: Value: +// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct") +// This one is used as the key to identify +// the product when uploading +// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty +// name for the product when the crash_sender +// pops up UI for the user. Falls back to +// BREAKPAD_PRODUCT if not specified. +// BREAKPAD_VERSION Product version (e.g., 1.2.3), used +// as metadata for crash report +// BREAKPAD_VENDOR Vendor named, used in UI (e.g. the Xxxx +// foo bar company product widget has crashed) +// BREAKPAD_URL URL destination for reporting +// BREAKPAD_REPORT_INTERVAL # of seconds between sending +// reports. If an additional report is +// generated within this time, it will +// be ignored. Default: 3600sec. +// Specify 0 to send all reports. +// BREAKPAD_SKIP_CONFIRM If true, the reporter will send the report +// without any user intervention. +// BREAKPAD_SEND_AND_EXIT If true, the handler will exit after sending. +// This will prevent any other handler (e.g., +// CrashReporter) from getting the crash. +// BREAKPAD_INSPECTOR_LOCATION The full path to the Inspector executable. +// BREAKPAD_REPORTER_EXE_LOCATION The full path to the Reporter/sender +// executable. +// BREAKPAD_LOGFILES Indicates an array of log file paths that +// should be uploaded at crash time +// BREAKPAD_REQUEST_COMMENTS If true, the message dialog will have a +// text box for the user to enter comments as +// well as a name and email address. +// BREAKPAD_COMMENTS The text the user provided as comments. +// +// The BREAKPAD_PRODUCT and BREAKPAD_VERSION are required to have non- +// NULL values. By default, the BREAKPAD_PRODUCT will be the +// CFBundleName and the BREAKPAD_VERSION will be the CFBundleVersion +// when these keys are present in the bundle's Info.plist. If the +// BREAKPAD_PRODUCT or BREAKPAD_VERSION are ultimately undefined, +// BreakpadCreate() will fail. You have been warned. +// +// If you are running in a debugger, breakpad will not install, unless the +// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero. +// +// The BREAKPAD_SKIP_CONFIRM and BREAKPAD_SEND_AND_EXIT default +// values are NO and YES. However, they can be controlled by setting their +// values in a user or global plist. +// +// It's easiest to use Breakpad via the Framework, but if you're compiling the +// code in directly, BREAKPAD_INSPECTOR_LOCATION and +// BREAKPAD_REPORTER_EXE_LOCATION allow you to specify custom paths +// to the helper executables. +// + +// Returns a new BreakpadRef object on success, NULL otherwise. +BreakpadRef BreakpadCreate(NSDictionary *parameters); + +// Uninstall and release the data associated with |ref|. +void BreakpadRelease(BreakpadRef ref); + +// Clients may set an optional callback which gets called when a crash occurs. +// The callback function should return |true| if we should handle the crash, +// generate a crash report, etc. or |false| if we should ignore it and forward +// the crash (normally to CrashReporter) +void BreakpadSetFilterCallback(BreakpadRef ref, + BreakpadFilterCallback callback); + +// User defined key and value string storage +// All set keys will be uploaded with the minidump if a crash occurs +// Keys and Values are limited to 255 bytes (256 - 1 for terminator). +// NB this is BYTES not GLYPHS. +// Anything longer than 255 bytes will be truncated. Note that the string is +// converted to UTF8 before truncation, so any multibyte character that +// straddles the 255 byte limit will be mangled. +// +// A maximum number of 64 key/value pairs are supported. An assert() will fire +// if more than this number are set. +void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value); +NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key); +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key); + +// Add a log file for Breakpad to read and send upon crash dump +void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname); + +// Generate a minidump and send +void BreakpadGenerateAndSendReport(BreakpadRef ref); + +#ifdef __cplusplus +} +#endif diff --git a/src/client/mac/Framework/Breakpad.mm b/src/client/mac/Framework/Breakpad.mm new file mode 100644 index 00000000..52301c72 --- /dev/null +++ b/src/client/mac/Framework/Breakpad.mm @@ -0,0 +1,915 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#define VERBOSE 0 + +#if VERBOSE + static bool gDebugLog = true; +#else + static bool gDebugLog = false; +#endif + +#define DEBUGLOG if (gDebugLog) fprintf +#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER" + +#import "client/mac/Framework/Breakpad.h" +#import "client/mac/crash_generation/Inspector.h" +#import "client/mac/Framework/OnDemandServer.h" +#import "client/mac/handler/protected_memory_allocator.h" +#import "common/mac/MachIPC.h" +#import "common/mac/SimpleStringDictionary.h" + +#import +#import + +#import + +#import "exception_handler.h" +#import "string_utilities.h" + +using google_breakpad::KeyValueEntry; +using google_breakpad::SimpleStringDictionary; +using google_breakpad::SimpleStringDictionaryIterator; + +//============================================================================= +// We want any memory allocations which are used by breakpad during the +// exception handling process (after a crash has happened) to be read-only +// to prevent them from being smashed before a crash occurs. Unfortunately +// we cannot protect against smashes to our exception handling thread's +// stack. +// +// NOTE: Any memory allocations which are not used during the exception +// handling process may be allocated in the normal ways. +// +// The ProtectedMemoryAllocator class provides an Allocate() method which +// we'll using in conjunction with placement operator new() to control +// allocation of C++ objects. Note that we don't use operator delete() +// but instead call the objects destructor directly: object->~ClassName(); +// +ProtectedMemoryAllocator *gMasterAllocator = NULL; +ProtectedMemoryAllocator *gKeyValueAllocator = NULL; +ProtectedMemoryAllocator *gBreakpadAllocator = NULL; + +// Mutex for thread-safe access to the key/value dictionary used by breakpad. +// It's a global instead of an instance variable of Breakpad +// since it can't live in a protected memory area. +pthread_mutex_t gDictionaryMutex; + +//============================================================================= +// Stack-based object for thread-safe access to a memory-protected region. +// It's assumed that normally the memory block (allocated by the allocator) +// is protected (read-only). Creating a stack-based instance of +// ProtectedMemoryLocker will unprotect this block after taking the lock. +// Its destructor will first re-protect the memory then release the lock. +class ProtectedMemoryLocker { +public: + // allocator may be NULL, in which case no Protect() or Unprotect() calls + // will be made, but a lock will still be taken + ProtectedMemoryLocker(pthread_mutex_t *mutex, + ProtectedMemoryAllocator *allocator) + : mutex_(mutex), allocator_(allocator) { + // Lock the mutex + assert(pthread_mutex_lock(mutex_) == 0); + + // Unprotect the memory + if (allocator_ ) { + allocator_->Unprotect(); + } + } + + ~ProtectedMemoryLocker() { + // First protect the memory + if (allocator_) { + allocator_->Protect(); + } + + // Then unlock the mutex + assert(pthread_mutex_unlock(mutex_) == 0); + }; + +private: + // Keep anybody from ever creating one of these things not on the stack. + ProtectedMemoryLocker() { } + ProtectedMemoryLocker(const ProtectedMemoryLocker&); + ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&); + + pthread_mutex_t *mutex_; + ProtectedMemoryAllocator *allocator_; +}; + +//============================================================================= +class Breakpad { + public: + // factory method + static Breakpad *Create(NSDictionary *parameters) { + // Allocate from our special allocation pool + Breakpad *breakpad = + new (gBreakpadAllocator->Allocate(sizeof(Breakpad))) + Breakpad(); + + if (!breakpad) + return NULL; + + if (!breakpad->Initialize(parameters)) { + // Don't use operator delete() here since we allocated from special pool + breakpad->~Breakpad(); + return NULL; + } + + return breakpad; + } + + ~Breakpad(); + + void SetKeyValue(NSString *key, NSString *value); + NSString *KeyValue(NSString *key); + void RemoveKeyValue(NSString *key); + + void GenerateAndSendReport(); + + void SetFilterCallback(BreakpadFilterCallback callback) { + filter_callback_ = callback; + } + + private: + Breakpad() + : handler_(NULL), + config_params_(NULL), + send_and_exit_(true), + filter_callback_(NULL) { + inspector_path_[0] = 0; + } + + bool Initialize(NSDictionary *parameters); + + bool ExtractParameters(NSDictionary *parameters); + + // Dispatches to HandleException() + static bool ExceptionHandlerDirectCallback(void *context, + int exception_type, + int exception_code, + mach_port_t crashing_thread); + + bool HandleException(int exception_type, + int exception_code, + mach_port_t crashing_thread); + + // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's + // MachineExceptions.h, we have to explicitly name the handler. + google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG) + + char inspector_path_[PATH_MAX]; // Path to inspector tool + + SimpleStringDictionary *config_params_; // Create parameters (STRONG) + + OnDemandServer inspector_; + + bool send_and_exit_; // Exit after sending, if true + + BreakpadFilterCallback filter_callback_; + + unsigned int logFileCounter; +}; + +#pragma mark - +#pragma mark Helper functions + +//============================================================================= +// Helper functions + +//============================================================================= +static BOOL IsDebuggerActive() { + BOOL result = NO; + NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults]; + + // We check both defaults and the environment variable here + + BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER]; + + if (!ignoreDebugger) { + char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER); + ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0; + } + + if (!ignoreDebugger) { + pid_t pid = getpid(); + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + int mibSize = sizeof(mib) / sizeof(int); + size_t actualSize; + + if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) { + struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize); + + if (info) { + // This comes from looking at the Darwin xnu Kernel + if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0) + result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO; + + free(info); + } + } + } + + return result; +} + +//============================================================================= +bool Breakpad::ExceptionHandlerDirectCallback(void *context, + int exception_type, + int exception_code, + mach_port_t crashing_thread) { + Breakpad *breakpad = (Breakpad *)context; + + // If our context is damaged or something, just return false to indicate that + // the handler should continue without us. + if (!breakpad) + return false; + + return breakpad->HandleException( exception_type, + exception_code, + crashing_thread); +} + +//============================================================================= +#pragma mark - + +#include + +//============================================================================= +// Returns the pathname to the Resources directory for this version of +// Breakpad which we are now running. +// +// Don't make the function static, since _dyld_lookup_and_bind_fully needs a +// simple non-static C name +// +extern "C" { +NSString * GetResourcePath(); +NSString * GetResourcePath() { + NSString *resourcePath = nil; + + // If there are multiple breakpads installed then calling bundleWithIdentifier + // will not work properly, so only use that as a backup plan. + // We want to find the bundle containing the code where this function lives + // and work from there + // + + // Get the pathname to the code which contains this function + void *address = nil; + NSModule module = nil; + _dyld_lookup_and_bind_fully("_GetResourcePath", + &address, + &module); + + if (module && address) { + const char* moduleName = NSNameOfModule(module); + if (moduleName) { + // The "Resources" directory should be in the same directory as the + // executable code, since that's how the Breakpad framework is built. + resourcePath = [NSString stringWithUTF8String:moduleName]; + resourcePath = [resourcePath stringByDeletingLastPathComponent]; + resourcePath = [resourcePath stringByAppendingPathComponent:@"Resources/"]; + } else { + DEBUGLOG(stderr, "Missing moduleName\n"); + } + } else { + DEBUGLOG(stderr, "Could not find GetResourcePath\n"); + // fallback plan + NSBundle *bundle = + [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"]; + resourcePath = [bundle resourcePath]; + } + + return resourcePath; +} +} // extern "C" + +//============================================================================= +bool Breakpad::Initialize(NSDictionary *parameters) { + // Initialize + config_params_ = NULL; + handler_ = NULL; + + // Check for debugger + if (IsDebuggerActive()) { + DEBUGLOG(stderr, "Debugger is active: Not installing handler\n"); + return true; + } + + // Gather any user specified parameters + if (!ExtractParameters(parameters)) { + return false; + } + + // Get path to Inspector executable. + NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION); + + // Standardize path (resolve symlinkes, etc.) and escape spaces + inspectorPathString = [inspectorPathString stringByStandardizingPath]; + inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "] + componentsJoinedByString:@"\\ "]; + + // Create an on-demand server object representing the Inspector. + // In case of a crash, we simply need to call the LaunchOnDemand() + // method on it, then send a mach message to its service port. + // It will then launch and perform a process inspection of our crashed state. + // See the HandleException() method for the details. +#define RECEIVE_PORT_NAME "com.Breakpad.Inspector" + + name_t portName; + snprintf(portName, sizeof(name_t), "%s%d", RECEIVE_PORT_NAME, getpid()); + + // Save the location of the Inspector + strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation], + sizeof(inspector_path_)); + + // Append a single command-line argument to the Inspector path + // representing the bootstrap name of the launch-on-demand receive port. + // When the Inspector is launched, it can use this to lookup the port + // by calling bootstrap_check_in(). + strlcat(inspector_path_, " ", sizeof(inspector_path_)); + strlcat(inspector_path_, portName, sizeof(inspector_path_)); + + kern_return_t kr = inspector_.Initialize(inspector_path_, + portName, + true); // shutdown on exit + + if (kr != KERN_SUCCESS) { + return false; + } + + // Create the handler (allocating it in our special protected pool) + handler_ = + new (gBreakpadAllocator->Allocate(sizeof(google_breakpad::ExceptionHandler))) + google_breakpad::ExceptionHandler( + Breakpad::ExceptionHandlerDirectCallback, this, true); + + return true; +} + +//============================================================================= +Breakpad::~Breakpad() { + // Note that we don't use operator delete() on these pointers, + // since they were allocated by ProtectedMemoryAllocator objects. + // + if (config_params_) { + config_params_->~SimpleStringDictionary(); + } + + if (handler_) + handler_->~ExceptionHandler(); +} + +//============================================================================= +bool Breakpad::ExtractParameters(NSDictionary *parameters) { + NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults]; + NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT]; + NSString *version = [parameters objectForKey:@BREAKPAD_VERSION]; + NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL]; + NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL]; + NSString *inspectorPathString = + [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION]; + NSString *reporterPathString = + [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION]; + NSString *skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM]; + NSString *sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT]; + NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES]; + NSString *logFileTailSize = [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]; + NSString *reportEmail = [parameters objectForKey:@BREAKPAD_EMAIL]; + NSString *requestUserText = + [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS]; + NSString *vendor = + [parameters objectForKey:@BREAKPAD_VENDOR]; + + // If these two are not already set(skipConfirm and sendAndExit can + // come from user defaults and take priority) + if (!skipConfirm) { + skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM]; + } + if (!sendAndExit) { + sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT]; + } + + if (!product) + product = [parameters objectForKey:@"CFBundleName"]; + + if (!display) + display = product; + + if (!version) + version = [parameters objectForKey:@"CFBundleVersion"]; + + if (!interval) + interval = @"3600"; + + if (!logFileTailSize) + logFileTailSize = @"200000"; + + if (!vendor) { + vendor = @"Vendor not specified"; + } + + // Normalize the values + if (skipConfirm) { + skipConfirm = [skipConfirm uppercaseString]; + + if ([skipConfirm isEqualToString:@"YES"] || + [skipConfirm isEqualToString:@"TRUE"] || + [skipConfirm isEqualToString:@"1"]) + skipConfirm = @"YES"; + else + skipConfirm = @"NO"; + } else { + skipConfirm = @"NO"; + } + + send_and_exit_ = true; + if (sendAndExit) { + sendAndExit = [sendAndExit uppercaseString]; + + if ([sendAndExit isEqualToString:@"NO"] || + [sendAndExit isEqualToString:@"FALSE"] || + [sendAndExit isEqualToString:@"0"]) + send_and_exit_ = false; + } + + if (requestUserText) { + requestUserText = [requestUserText uppercaseString]; + + if ([requestUserText isEqualToString:@"YES"] || + [requestUserText isEqualToString:@"TRUE"] || + [requestUserText isEqualToString:@"1"]) + requestUserText = @"YES"; + else + requestUserText = @"NO"; + } else { + requestUserText = @"NO"; + } + + // Find the helper applications if not specified in user config. + NSString *resourcePath = nil; + if (!inspectorPathString || !reporterPathString) { + resourcePath = GetResourcePath(); + if (!resourcePath) { + DEBUGLOG(stderr, "Could not get resource path\n"); + return false; + } + } + + // Find Inspector. + if (!inspectorPathString) { + inspectorPathString = + [resourcePath stringByAppendingPathComponent:@"Inspector"]; + } + + // Verify that there is an Inspector tool + if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) { + DEBUGLOG(stderr, "Cannot find Inspector tool\n"); + return false; + } + + // Find Reporter. + if (!reporterPathString) { + reporterPathString = + [resourcePath stringByAppendingPathComponent:@"crash_report_sender.app"]; + reporterPathString = [[NSBundle bundleWithPath:reporterPathString] executablePath]; + } + + // Verify that there is a Reporter application + if (![[NSFileManager defaultManager] + fileExistsAtPath:reporterPathString]) { + DEBUGLOG(stderr, "Cannot find Reporter tool\n"); + return false; + } + + // The product and version are required values + if (![product length] || ![version length]) { + DEBUGLOG(stderr, "Missing required product and/or version keys\n"); + return false; + } + + config_params_ = + new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) ) + SimpleStringDictionary(); + + SimpleStringDictionary &dictionary = *config_params_; + + dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]); + dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]); + dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]); + dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]); + dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]); + dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]); + dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION, + [inspectorPathString fileSystemRepresentation]); + dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION, + [reporterPathString fileSystemRepresentation]); + dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE, + [logFileTailSize UTF8String]); + dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS, + [requestUserText UTF8String]); + dictionary.SetKeyValue(BREAKPAD_VENDOR, + [vendor UTF8String]); + + if (logFilePaths) { + char logFileKey[255]; + for(unsigned int i = 0; i < [logFilePaths count]; i++) { + sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i); + dictionary.SetKeyValue(logFileKey, [[logFilePaths objectAtIndex:i] fileSystemRepresentation]); + } + } + + if (reportEmail) { + dictionary.SetKeyValue(BREAKPAD_EMAIL, + [reportEmail UTF8String]); + } +#if 0 // for testing + BreakpadSetKeyValue(this, @"UserKey1", @"User Value 1"); + BreakpadSetKeyValue(this, @"UserKey2", @"User Value 2"); + BreakpadSetKeyValue(this, @"UserKey3", @"User Value 3"); + BreakpadSetKeyValue(this, @"UserKey4", @"User Value 4"); +#endif + + return true; +} + +//============================================================================= +void Breakpad::SetKeyValue(NSString *key, NSString *value) { + // We allow nil values. This is the same as removing the keyvalue. + if (!config_params_ || !key) + return; + + config_params_->SetKeyValue([key UTF8String], [value UTF8String]); +} + +//============================================================================= +NSString * Breakpad::KeyValue(NSString *key) { + if (!config_params_ || !key) + return nil; + + const char *value = config_params_->GetValueForKey([key UTF8String]); + return value ? [NSString stringWithUTF8String:value] : nil; +} + +//============================================================================= +void Breakpad::RemoveKeyValue(NSString *key) { + if (!config_params_ || !key) + return; + + config_params_->RemoveKey([key UTF8String]); +} + +//============================================================================= +void Breakpad::GenerateAndSendReport() { + HandleException(0, 0, 0); +} + +//============================================================================= +bool Breakpad::HandleException(int exception_type, + int exception_code, + mach_port_t crashing_thread) { + DEBUGLOG(stderr, "Breakpad: an exception occurred\n"); + + if (filter_callback_) { + bool should_handle = filter_callback_(exception_type, + exception_code, + crashing_thread); + if (!should_handle) return false; + } + + // We need to reset the memory protections to be read/write, + // since LaunchOnDemand() requires changing state. + gBreakpadAllocator->Unprotect(); + // Configure the server to launch when we message the service port. + // The reason we do this here, rather than at startup, is that we + // can leak a bootstrap service entry if this method is called and + // there never ends up being a crash. + inspector_.LaunchOnDemand(); + gBreakpadAllocator->Protect(); + + // The Inspector should send a message to this port to verify it + // received our information and has finished the inspection. + ReceivePort acknowledge_port; + + // Send initial information to the Inspector. + MachSendMessage message(kMsgType_InspectorInitialInfo); + message.AddDescriptor(mach_task_self()); // our task + message.AddDescriptor(crashing_thread); // crashing thread + message.AddDescriptor(mach_thread_self()); // exception-handling thread + message.AddDescriptor(acknowledge_port.GetPort());// message receive port + + InspectorInfo info; + info.exception_type = exception_type; + info.exception_code = exception_code; + info.parameter_count = config_params_->GetCount(); + message.SetData(&info, sizeof(info)); + + MachPortSender sender(inspector_.GetServicePort()); + + kern_return_t result = sender.SendMessage(message, 2000); + + if (result == KERN_SUCCESS) { + // Now, send a series of key-value pairs to the Inspector. + const KeyValueEntry *entry = NULL; + SimpleStringDictionaryIterator iter(*config_params_); + + while ( (entry = iter.Next()) ) { + KeyValueMessageData keyvalue_data(*entry); + + MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair); + keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data)); + + result = sender.SendMessage(keyvalue_message, 2000); + + if (result != KERN_SUCCESS) { + break; + } + } + + if (result == KERN_SUCCESS) { + // Wait for acknowledgement that the inspection has finished. + MachReceiveMessage acknowledge_messsage; + result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 2000); + } + } + +#if VERBOSE + PRINT_MACH_RESULT(result, "Breakpad: SendMessage "); + printf("Breakpad: Inspector service port = %#x\n", + inspector_.GetServicePort()); +#endif + + // If we don't want any forwarding, return true here to indicate that we've + // processed things as much as we want. + if (send_and_exit_) + return true; + + return false; +} + +//============================================================================= +//============================================================================= + +#pragma mark - +#pragma mark Public API + +//============================================================================= +BreakpadRef BreakpadCreate(NSDictionary *parameters) { + try { + // This is confusing. Our two main allocators for breakpad memory are: + // - gKeyValueAllocator for the key/value memory + // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other + // breakpad allocations which are accessed at exception handling time. + // + // But in order to avoid these two allocators themselves from being smashed, + // we'll protect them as well by allocating them with gMasterAllocator. + // + // gMasterAllocator itself will NOT be protected, but this doesn't matter, + // since once it does its allocations and locks the memory, smashes to itself + // don't affect anything we care about. + gMasterAllocator = + new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2); + + gKeyValueAllocator = + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) + ProtectedMemoryAllocator(sizeof(SimpleStringDictionary)); + + // Create a mutex for use in accessing the SimpleStringDictionary + int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL); + if (mutexResult != 0) { + throw mutexResult; // caught down below + } + + // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. + // Let's round up to the nearest page size. + // + int breakpad_pool_size = 4096; + + /* + sizeof(Breakpad) + + sizeof(google_breakpad::ExceptionHandler) + + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) + */ + + gBreakpadAllocator = + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) + ProtectedMemoryAllocator(breakpad_pool_size); + + // Stack-based autorelease pool for Breakpad::Create() obj-c code. + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + Breakpad *breakpad = Breakpad::Create(parameters); + + if (breakpad) { + // Make read-only to protect against memory smashers + gMasterAllocator->Protect(); + gKeyValueAllocator->Protect(); + gBreakpadAllocator->Protect(); + } else { + [pool release]; +#ifdef __EXCEPTIONS + throw(-1); +#else + return NULL; +#endif + } + + // Can uncomment this line to figure out how much space was actually + // allocated using this allocator + // printf("gBreakpadAllocator allocated size = %d\n", + // gBreakpadAllocator->GetAllocatedSize() ); + + [pool release]; + return (BreakpadRef)breakpad; + } catch(...) { // don't let exception leave this C API + if (gKeyValueAllocator) { + gKeyValueAllocator->~ProtectedMemoryAllocator(); + gKeyValueAllocator = NULL; + } + + if (gBreakpadAllocator) { + gBreakpadAllocator->~ProtectedMemoryAllocator(); + gBreakpadAllocator = NULL; + } + + delete gMasterAllocator; + gMasterAllocator = NULL; + } + + return NULL; +} + +//============================================================================= +void BreakpadRelease(BreakpadRef ref) { + try { + Breakpad *breakpad = (Breakpad *)ref; + + if (gMasterAllocator) { + gMasterAllocator->Unprotect(); + gKeyValueAllocator->Unprotect(); + gBreakpadAllocator->Unprotect(); + + breakpad->~Breakpad(); + + // Unfortunately, it's not possible to deallocate this stuff + // because the exception handling thread is still finishing up + // asynchronously at this point... OK, it could be done with + // locks, etc. But since BreakpadRelease() should usually only + // be called right before the process exits, it's not worth + // deallocating this stuff. +#if 0 + gKeyValueAllocator->~ProtectedMemoryAllocator(); + gBreakpadAllocator->~ProtectedMemoryAllocator(); + delete gMasterAllocator; + + gMasterAllocator = NULL; + gKeyValueAllocator = NULL; + gBreakpadAllocator = NULL; +#endif + + pthread_mutex_destroy(&gDictionaryMutex); + } + } catch(...) { // don't let exception leave this C API + fprintf(stderr, "BreakpadRelease() : error\n"); + } +} + +//============================================================================= +void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) { + try { + // Not called at exception time + Breakpad *breakpad = (Breakpad *)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + breakpad->SetKeyValue(key, value); + } + } catch(...) { // don't let exception leave this C API + fprintf(stderr, "BreakpadSetKeyValue() : error\n"); + } +} + +//============================================================================= +NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) { + NSString *value = nil; + + try { + // Not called at exception time + Breakpad *breakpad = (Breakpad *)ref; + + if (!breakpad || !key || !gKeyValueAllocator) + return nil; + + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + value = breakpad->KeyValue(key); + } catch(...) { // don't let exception leave this C API + fprintf(stderr, "BreakpadKeyValue() : error\n"); + } + + return value; +} + +//============================================================================= +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) { + try { + // Not called at exception time + Breakpad *breakpad = (Breakpad *)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + breakpad->RemoveKeyValue(key); + } + } catch(...) { // don't let exception leave this C API + fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); + } +} + +//============================================================================= +void BreakpadGenerateAndSendReport(BreakpadRef ref) { + try { + Breakpad *breakpad = (Breakpad *)ref; + + if (breakpad && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + gBreakpadAllocator->Unprotect(); + breakpad->GenerateAndSendReport(); + gBreakpadAllocator->Protect(); + } + } catch(...) { // don't let exception leave this C API + fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n"); + } +} + +//============================================================================= +void BreakpadSetFilterCallback(BreakpadRef ref, + BreakpadFilterCallback callback) { + + try { + Breakpad *breakpad = (Breakpad *)ref; + + if (breakpad && gBreakpadAllocator) { + // share the dictionary mutex here (we really don't need a mutex) + ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator); + + breakpad->SetFilterCallback(callback); + } + } catch(...) { // don't let exception leave this C API + fprintf(stderr, "BreakpadSetFilterCallback() : error\n"); + } +} + +//============================================================================ +void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) { + int logFileCounter = 0; + + NSString *logFileKey = [NSString stringWithFormat:@"%@%d", + @BREAKPAD_LOGFILE_KEY_PREFIX, + logFileCounter]; + + NSString *existingLogFilename = nil; + existingLogFilename = BreakpadKeyValue(ref, logFileKey); + // Find the first log file key that we can use by testing for existence + while (existingLogFilename) { + if ([existingLogFilename isEqualToString:logPathname]) { + return; + } + logFileCounter++; + logFileKey = [NSString stringWithFormat:@"%@%d", + @BREAKPAD_LOGFILE_KEY_PREFIX, + logFileCounter]; + existingLogFilename = BreakpadKeyValue(ref, logFileKey); + } + + BreakpadSetKeyValue(ref, logFileKey, logPathname); + +} diff --git a/src/client/mac/Framework/Breakpad_Prefix.pch b/src/client/mac/Framework/Breakpad_Prefix.pch new file mode 100644 index 00000000..72986663 --- /dev/null +++ b/src/client/mac/Framework/Breakpad_Prefix.pch @@ -0,0 +1,8 @@ +// +// Prefix header for all source files of the 'Breakpad' target in the +// 'Breakpad' project. +// + +#ifdef __OBJC__ + #import +#endif diff --git a/src/client/mac/Framework/English.lproj/InfoPlist.strings b/src/client/mac/Framework/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..5e45963c382ba690b781b953a00585212b898ac5 GIT binary patch literal 92 zcmW-XQ3`+{5C!MkQ~2$No+IcIkqMDxWCV8j>LCj|yTg2Mz+o9F%uHlf9u}h9EuK`F a!Y*1dX%G66ZqL#C$|bw0ZoP5@jOGW1ArT7z literal 0 HcmV?d00001 diff --git a/src/client/mac/Framework/Info.plist b/src/client/mac/Framework/Info.plist new file mode 100644 index 00000000..0ec80c38 --- /dev/null +++ b/src/client/mac/Framework/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleName + ${PRODUCT_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSPrincipalClass + + + diff --git a/src/client/mac/Framework/OnDemandServer.h b/src/client/mac/Framework/OnDemandServer.h new file mode 100644 index 00000000..9d8bc961 --- /dev/null +++ b/src/client/mac/Framework/OnDemandServer.h @@ -0,0 +1,146 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import +#import +#import +#import +#import +#import +#import + +//============================================================================== +// class OnDemandServer : +// A basic on-demand server launcher supporting a single named service port +// +// Example Usage : +// +// kern_return_t result; +// OnDemandServer *server = OnDemandServer::Create("/tmp/myserver", +// "com.MyCompany.MyServiceName", +// true, +// &result); +// +// if (server) { +// server->LaunchOnDemand(); +// mach_port_t service_port = GetServicePort(); +// +// // Send a mach message to service_port and "myserver" will be launched +// } +// +// +// ---- Now in the server code ---- +// +// // "myserver" should get the service port and read the message which +// // launched it: +// mach_port_t service_rcv_port_; +// kern_return_t kr = bootstrap_check_in(bootstrap_port, +// "com.MyCompany.MyServiceName", +// &service_rcv_port_); +// // mach_msg() read service_rcv_port_ .... +// +// .... +// +// // Later "myserver" may want to unregister the service if it doesn't +// // want its bootstrap service to stick around after it exits. +// +// // DO NOT use mach_port_deallocate() here -- it will fail and the +// // following bootstrap_register() will also fail leaving our service +// // name hanging around forever (until reboot) +// kern_return_t kr = mach_port_destroy(mach_task_self(), service_rcv_port_); +// +// kr = bootstrap_register(bootstrap_port, +// "com.MyCompany.MyServiceName", +// MACH_PORT_NULL); + +class OnDemandServer { + public: + // must call Initialize() to be useful + OnDemandServer() + : server_port_(MACH_PORT_NULL), + service_port_(MACH_PORT_NULL), + unregister_on_cleanup_(true) { + } + + // Creates the bootstrap server and service + kern_return_t Initialize(const char *server_command, + const char *service_name, + bool unregister_on_cleanup); + + // Returns an OnDemandServer object if successful, or NULL if there's + // an error. The error result will be returned in out_result. + // + // server_command : the full path name including optional command-line + // arguments to the executable representing the server + // + // service_name : represents service name + // something like "com.company.ServiceName" + // + // unregister_on_cleanup : if true, unregisters the service name + // when the OnDemandServer is deleted -- unregistering will + // ONLY be possible if LaunchOnDemand() has NOT been called. + // If false, then the service will continue to be registered + // even after the current process quits. + // + // out_result : if non-NULL, returns the result + // this value will be KERN_SUCCESS if Create() returns non-NULL + // + static OnDemandServer *Create(const char *server_command, + const char *service_name, + bool unregister_on_cleanup, + kern_return_t *out_result); + + // Cleans up and if LaunchOnDemand() has not yet been called then + // the bootstrap service will be unregistered. + ~OnDemandServer(); + + // This must be called if we intend to commit to launching the server + // by sending a mach message to our service port. Do not call it otherwise + // or it will be difficult (impossible?) to unregister the service name. + void LaunchOnDemand(); + + // This is the port we need to send a mach message to after calling + // LaunchOnDemand(). Sending a message causing an immediate launch + // of the server + mach_port_t GetServicePort() { return service_port_; }; + + private: + // Disallow copy constructor + OnDemandServer(const OnDemandServer&); + + // Cleans up and if LaunchOnDemand() has not yet been called then + // the bootstrap service will be unregistered. + void Unregister(); + + name_t service_name_; + + mach_port_t server_port_; + mach_port_t service_port_; + bool unregister_on_cleanup_; +}; diff --git a/src/client/mac/Framework/OnDemandServer.mm b/src/client/mac/Framework/OnDemandServer.mm new file mode 100644 index 00000000..32ac1faa --- /dev/null +++ b/src/client/mac/Framework/OnDemandServer.mm @@ -0,0 +1,145 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import "OnDemandServer.h" + +#if DEBUG + #define PRINT_MACH_RESULT(result_, message_) \ + printf(message_"%s (%d)\n", mach_error_string(result_), result_ ); +#else + #define PRINT_MACH_RESULT(result_, message_) +#endif + +//============================================================================== +OnDemandServer *OnDemandServer::Create(const char *server_command, + const char *service_name, + bool unregister_on_cleanup, + kern_return_t *out_result) { + OnDemandServer *server = new OnDemandServer(); + + if (!server) return NULL; + + kern_return_t result = server->Initialize(server_command, + service_name, + unregister_on_cleanup); + + if (out_result) { + *out_result = result; + } + + if (result == KERN_SUCCESS) { + return server; + } + + delete server; + return NULL; +}; + +//============================================================================== +kern_return_t OnDemandServer::Initialize(const char *server_command, + const char *service_name, + bool unregister_on_cleanup) { + unregister_on_cleanup_ = unregister_on_cleanup; + + kern_return_t kr = + bootstrap_create_server(bootstrap_port, + const_cast(server_command), + geteuid(), // server uid + true, + &server_port_); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "bootstrap_create_server() : "); + return kr; + } + + strlcpy(service_name_, service_name, sizeof(service_name_)); + + // Create a service called service_name, and return send rights to + // that port in service_port_. + kr = bootstrap_create_service(server_port_, + const_cast(service_name), + &service_port_); + + if (kr != KERN_SUCCESS) { + //PRINT_MACH_RESULT(kr, "bootstrap_create_service() : "); + + // perhaps the service has already been created - try to look it up + kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "bootstrap_look_up() : "); + Unregister(); // clean up server port + return kr; + } + } + + return KERN_SUCCESS; +} + +//============================================================================== +OnDemandServer::~OnDemandServer() { + if (unregister_on_cleanup_) { + Unregister(); + } +} + +//============================================================================== +void OnDemandServer::LaunchOnDemand() { + // We need to do this, since the launched server is another process + // and holding on to this port delays launching until the current process + // exits! + mach_port_deallocate(mach_task_self(), server_port_); + server_port_ = NULL; + + // Now, the service is still registered and all we need to do is send + // a mach message to the service port in order to launch the server. +} + +//============================================================================== +void OnDemandServer::Unregister() { + if (service_port_ != MACH_PORT_NULL) { + mach_port_deallocate(mach_task_self(), service_port_); + service_port_ = MACH_PORT_NULL; + } + + if (server_port_ != MACH_PORT_NULL) { + // unregister the service + kern_return_t kr = bootstrap_register(server_port_, + service_name_, + MACH_PORT_NULL); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : "); + } + + mach_port_deallocate(mach_task_self(), server_port_); + server_port_ = MACH_PORT_NULL; + } +} diff --git a/src/client/mac/UnitTests-Info.plist b/src/client/mac/UnitTests-Info.plist new file mode 100644 index 00000000..65013556 --- /dev/null +++ b/src/client/mac/UnitTests-Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/src/client/mac/crash_generation/Inspector.h b/src/client/mac/crash_generation/Inspector.h new file mode 100644 index 00000000..38d441fb --- /dev/null +++ b/src/client/mac/crash_generation/Inspector.h @@ -0,0 +1,200 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Interface file between the Breakpad.framework and +// the Inspector process. + +#import "common/mac/SimpleStringDictionary.h" + +#import +#import "client/mac/handler/minidump_generator.h" + +#define VERBOSE 0 + +extern bool gDebugLog; + +#define DEBUGLOG if (gDebugLog) fprintf + +// Types of mach messsages (message IDs) +enum { + kMsgType_InspectorInitialInfo = 0, // data is InspectorInfo + kMsgType_InspectorKeyValuePair = 1, // data is KeyValueMessageData + kMsgType_InspectorAcknowledgement = 2 // no data sent +}; + +// Initial information sent from the crashed process by +// Breakpad.framework to the Inspector process +// The mach message with this struct as data will also include +// several descriptors for sending mach port rights to the crashed +// task, etc. +struct InspectorInfo { + int exception_type; + int exception_code; + unsigned int parameter_count; // key-value pairs +}; + +// Key/value message data to be sent to the Inspector +struct KeyValueMessageData { + public: + KeyValueMessageData() {} + KeyValueMessageData(const google_breakpad::KeyValueEntry &inEntry) { + strlcpy(key, inEntry.GetKey(), sizeof(key) ); + strlcpy(value, inEntry.GetValue(), sizeof(value) ); + } + + char key[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE]; + char value[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE]; +}; + +using std::string; +using google_breakpad::MinidumpGenerator; + +namespace google_breakpad { + +static BOOL EnsureDirectoryPathExists(NSString *dirPath); + +//============================================================================= +class ConfigFile { + public: + ConfigFile() { + config_file_ = -1; + config_file_path_[0] = 0; + has_created_file_ = false; + }; + + ~ConfigFile() { + }; + + void WriteFile(const SimpleStringDictionary *configurationParameters, + const char *dump_dir, + const char *minidump_id); + + const char *GetFilePath() { return config_file_path_; } + + void Unlink() { + if (config_file_ != -1) + unlink(config_file_path_); + + config_file_ = -1; + } + + private: + BOOL WriteData(const void *data, size_t length); + + BOOL AppendConfigData(const char *key, + const void *data, + size_t length); + + BOOL AppendConfigString(const char *key, + const char *value); + + int config_file_; // descriptor for config file + char config_file_path_[PATH_MAX]; // Path to configuration file + bool has_created_file_; +}; + +//============================================================================= +class MinidumpLocation { + public: + MinidumpLocation() { + NSString *minidumpDirBase = NSHomeDirectory(); + NSString *minidumpDir; + + // Put root processes at root + if (geteuid() == 0) + minidumpDirBase = @"/"; + + minidumpDir = + [minidumpDirBase stringByAppendingPathComponent:@"Library/Logs/Google"]; + + // Ensure that the path exists. Fallback to /tmp if unable to locate path. + if (!EnsureDirectoryPathExists(minidumpDir)) { + DEBUGLOG(stderr, "Unable to create: %s\n", [minidumpDir UTF8String]); + minidumpDir = @"/tmp"; + } + + strlcpy(minidump_dir_path_, [minidumpDir fileSystemRepresentation], + sizeof(minidump_dir_path_)); + + // now generate a unique ID + string dump_path(minidump_dir_path_); + string next_minidump_id; + + string next_minidump_path_ = + (MinidumpGenerator::UniqueNameInDirectory(dump_path, &next_minidump_id)); + + strlcpy(minidump_id_, next_minidump_id.c_str(), sizeof(minidump_id_)); + }; + + const char *GetPath() { return minidump_dir_path_; } + const char *GetID() { return minidump_id_; } + + private: + char minidump_dir_path_[PATH_MAX]; // Path to minidump directory + char minidump_id_[128]; +}; + +//============================================================================= +class Inspector { + public: + Inspector() {}; + + // given a bootstrap service name, receives mach messages + // from a crashed process, then inspects it, creates a minidump file + // and asks the user if he wants to upload it to a server. + void Inspect(const char *receive_port_name); + + private: + kern_return_t ServiceCheckIn(const char *receive_port_name); + kern_return_t ServiceCheckOut(const char *receive_port_name); + + kern_return_t ReadMessages(); + + bool InspectTask(); + kern_return_t SendAcknowledgement(); + void LaunchReporter(const char *inConfigFilePath); + + mach_port_t service_rcv_port_; + + int exception_type_; + int exception_code_; + mach_port_t remote_task_; + mach_port_t crashing_thread_; + mach_port_t handler_thread_; + mach_port_t ack_port_; + + SimpleStringDictionary config_params_; + + ConfigFile config_file_; +}; + + +} // namespace google_breakpad + + diff --git a/src/client/mac/crash_generation/Inspector.mm b/src/client/mac/crash_generation/Inspector.mm new file mode 100644 index 00000000..f659b669 --- /dev/null +++ b/src/client/mac/crash_generation/Inspector.mm @@ -0,0 +1,431 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Utility that can inspect another process and write a crash dump + +#import +#import +#import +#import +#import + +#import "client/mac/crash_generation/Inspector.h" + +#import "client/mac/Framework/Breakpad.h" +#import "client/mac/handler/minidump_generator.h" + +#import "common/mac/SimpleStringDictionary.h" +#import "common/mac/MachIPC.h" + +#import + +#if VERBOSE + bool gDebugLog = true; +#else + bool gDebugLog = false; +#endif + +namespace google_breakpad { + +//============================================================================= +static BOOL EnsureDirectoryPathExists(NSString *dirPath) { + NSFileManager *mgr = [NSFileManager defaultManager]; + + // If we got a relative path, prepend the current directory + if (![dirPath isAbsolutePath]) + dirPath = [[mgr currentDirectoryPath] stringByAppendingPathComponent:dirPath]; + + NSString *path = dirPath; + + // Ensure that no file exists within the path which would block creation + while (1) { + BOOL isDir; + if ([mgr fileExistsAtPath:path isDirectory:&isDir]) { + if (isDir) + break; + + return NO; + } + + path = [path stringByDeletingLastPathComponent]; + } + + // Path now contains the first valid directory (or is empty) + if (![path length]) + return NO; + + NSString *common = + [dirPath commonPrefixWithString:path options:NSLiteralSearch]; + + // If everything is good + if ([common isEqualToString:dirPath]) + return YES; + + // Break up the difference into components + NSString *diff = [dirPath substringFromIndex:[common length] + 1]; + NSArray *components = [diff pathComponents]; + unsigned count = [components count]; + + // Rebuild the path one component at a time + NSDictionary *attrs = + [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750] + forKey:NSFilePosixPermissions]; + path = common; + for (unsigned i = 0; i < count; ++i) { + path = [path stringByAppendingPathComponent:[components objectAtIndex:i]]; + + if (![mgr createDirectoryAtPath:path attributes:attrs]) + return NO; + } + + return YES; +} + +//============================================================================= +BOOL ConfigFile::WriteData(const void *data, size_t length) { + size_t result = write(config_file_, data, length); + + return result == length; +} + +//============================================================================= +BOOL ConfigFile::AppendConfigData(const char *key, + const void *data, size_t length) { + assert(config_file_ != -1); + + if (!key) { + DEBUGLOG(stderr, "Breakpad: Missing Key\n"); + return NO; + } + + if (!data) { + DEBUGLOG(stderr, "Breakpad: Missing data for key: %s\n", key ? key : + ""); + return NO; + } + + // Write the key, \n, length of data (ascii integer), \n, data + char buffer[16]; + char nl = '\n'; + BOOL result = WriteData(key, strlen(key)); + + snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length); + result &= WriteData(buffer, strlen(buffer)); + result &= WriteData(data, length); + result &= WriteData(&nl, 1); + return result; +} + +//============================================================================= +BOOL ConfigFile::AppendConfigString(const char *key, + const char *value) { + return AppendConfigData(key, value, strlen(value)); +} + +//============================================================================= +void ConfigFile::WriteFile(const SimpleStringDictionary *configurationParameters, + const char *dump_dir, + const char *minidump_id) { + + assert(config_file_ == -1); + + // Open and write out configuration file preamble + strlcpy(config_file_path_, "/tmp/Config-XXXXXX", + sizeof(config_file_path_)); + config_file_ = mkstemp(config_file_path_); + + if (config_file_ == -1) + return; + + has_created_file_ = true; + + // Add the minidump dir + AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir); + AppendConfigString(kReporterMinidumpIDKey, minidump_id); + + // Write out the configuration parameters + BOOL result = YES; + const SimpleStringDictionary &dictionary = *configurationParameters; + + const KeyValueEntry *entry = NULL; + SimpleStringDictionaryIterator iter(dictionary); + + while ((entry = iter.Next())) { + result = AppendConfigString(entry->GetKey(), entry->GetValue()); + + if (!result) + break; + } + + close(config_file_); + config_file_ = -1; +} + +//============================================================================= +void Inspector::Inspect(const char *receive_port_name) { + kern_return_t result = ServiceCheckIn(receive_port_name); + + if (result == KERN_SUCCESS) { + result = ReadMessages(); + + if (result == KERN_SUCCESS) { + // Inspect the task and write a minidump file. + InspectTask(); + + // Send acknowledgement to the crashed process that the inspection + // has finished. It will then be able to cleanly exit. + if (SendAcknowledgement() == KERN_SUCCESS) { + // Ask the user if he wants to upload the crash report to a server, + // and do so if he agrees. + LaunchReporter(config_file_.GetFilePath()); + } + + // Now that we're done reading messages, cleanup the service, but only + // if there was an actual exception + // Otherwise, it means the dump was generated on demand and the process + // lives on, and we might be needed again in the future. + if (exception_code_) { + ServiceCheckOut(receive_port_name); + } + } else { + PRINT_MACH_RESULT(result, "Inspector: WaitForMessage()"); + } + } +} + +//============================================================================= +kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) { + // We need to get the mach port representing this service, so we can + // get information from the crashed process. + kern_return_t kr = bootstrap_check_in(bootstrap_port, + (char*)receive_port_name, + &service_rcv_port_); + + if (kr != KERN_SUCCESS) { +#if VERBOSE + PRINT_MACH_RESULT(kr, "Inspector: bootstrap_check_in()"); +#endif + } + + return kr; +} + +//============================================================================= +kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) { + // We're done receiving mach messages from the crashed process, + // so clean up a bit. + kern_return_t kr; + + // DO NOT use mach_port_deallocate() here -- it will fail and the + // following bootstrap_register() will also fail leaving our service + // name hanging around forever (until reboot) + kr = mach_port_destroy(mach_task_self(), service_rcv_port_); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, + "Inspector: UNREGISTERING: service_rcv_port mach_port_deallocate()"); + return kr; + } + + // Unregister the service associated with the receive port. + kr = bootstrap_register(bootstrap_port, + (char*)receive_port_name, + MACH_PORT_NULL); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()"); + } + + return kr; +} + +//============================================================================= +kern_return_t Inspector::ReadMessages() { + // Wait for an initial message from the crashed process containing basic + // information about the crash. + ReceivePort receive_port(service_rcv_port_); + + MachReceiveMessage message; + kern_return_t result = receive_port.WaitForMessage(&message, 1000); + + if (result == KERN_SUCCESS) { + InspectorInfo &info = (InspectorInfo &)*message.GetData(); + exception_type_ = info.exception_type; + exception_code_ = info.exception_code; + +#if VERBOSE + printf("message ID = %d\n", message.GetMessageID()); +#endif + + remote_task_ = message.GetTranslatedPort(0); + crashing_thread_ = message.GetTranslatedPort(1); + handler_thread_ = message.GetTranslatedPort(2); + ack_port_ = message.GetTranslatedPort(3); + +#if VERBOSE + printf("exception_type = %d\n", exception_type_); + printf("exception_code = %d\n", exception_code_); + printf("remote_task = %d\n", remote_task_); + printf("crashing_thread = %d\n", crashing_thread_); + printf("handler_thread = %d\n", handler_thread_); + printf("ack_port_ = %d\n", ack_port_); + printf("parameter count = %d\n", info.parameter_count); +#endif + + // The initial message contains the number of key value pairs that + // we are expected to read. + // Read each key/value pair, one mach message per key/value pair. + for (unsigned int i = 0; i < info.parameter_count; ++i) { + MachReceiveMessage message; + result = receive_port.WaitForMessage(&message, 1000); + + if(result == KERN_SUCCESS) { + KeyValueMessageData &key_value_data = + (KeyValueMessageData&)*message.GetData(); + + config_params_.SetKeyValue(key_value_data.key, key_value_data.value); + } else { + PRINT_MACH_RESULT(result, "Inspector: key/value message"); + break; + } + } + } + + return result; +} + +//============================================================================= +bool Inspector::InspectTask() { + // keep the task quiet while we're looking at it + task_suspend(remote_task_); + + MinidumpLocation minidumpLocation; + + config_file_.WriteFile( &config_params_, + minidumpLocation.GetPath(), + minidumpLocation.GetID()); + + + MinidumpGenerator generator(remote_task_, handler_thread_); + + if (exception_type_ && exception_code_) { + generator.SetExceptionInformation(exception_type_, + exception_code_, + crashing_thread_); + } + + NSString *minidumpPath = [NSString stringWithFormat:@"%s/%s.dmp", + minidumpLocation.GetPath(), minidumpLocation.GetID()]; + + bool result = generator.Write([minidumpPath fileSystemRepresentation]); + + DEBUGLOG(stderr, "Inspector: finished writing minidump file: %s\n", + [minidumpPath fileSystemRepresentation]); + + // let the task continue + task_resume(remote_task_); + + return result; +} + +//============================================================================= +// The crashed task needs to be told that the inspection has finished. +// It will wait on a mach port (with timeout) until we send acknowledgement. +kern_return_t Inspector::SendAcknowledgement() { + if (ack_port_ != MACH_PORT_DEAD) { + MachPortSender sender(ack_port_); + MachSendMessage ack_message(kMsgType_InspectorAcknowledgement); + + DEBUGLOG(stderr, "Inspector: trying to send acknowledgement to port %d\n", + ack_port_); + + kern_return_t result = sender.SendMessage(ack_message, 2000); + +#if VERBOSE + PRINT_MACH_RESULT(result, "Inspector: sent acknowledgement"); +#endif + + return result; + } + + DEBUGLOG(stderr, "Inspector: port translation failure!\n"); + return KERN_INVALID_NAME; +} + +//============================================================================= +void Inspector::LaunchReporter(const char *inConfigFilePath) { + // Extract the path to the reporter executable. + const char *reporterExecutablePath = + config_params_.GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION); + DEBUGLOG(stderr, "reporter path = %s\n", reporterExecutablePath); + + // Setup and launch the crash dump sender. + const char *argv[3]; + argv[0] = reporterExecutablePath; + argv[1] = inConfigFilePath; + argv[2] = NULL; + + // Launch the reporter + pid_t pid = fork(); + + // If we're in the child, load in our new executable and run. + // The parent will not wait for the child to complete. + if (pid == 0) { + execv(argv[0], (char * const *)argv); + config_file_.Unlink(); // launch failed - get rid of config file + DEBUGLOG(stderr, "Inspector: unable to launch reporter app\n"); + _exit(1); + } + + // Wait until the Reporter child process exits. + // + + // We'll use a timeout of one minute. + int timeoutCount = 60; // 60 seconds + + while (timeoutCount-- > 0) { + int status; + pid_t result = waitpid(pid, &status, WNOHANG); + + if (result == 0) { + // The child has not yet finished. + sleep(1); + } else if (result == -1) { + DEBUGLOG(stderr, "Inspector: waitpid error (%d) waiting for reporter app\n", + errno); + break; + } else { + // child has finished + break; + } + } +} + +} // namespace google_breakpad + diff --git a/src/client/mac/crash_generation/InspectorMain.mm b/src/client/mac/crash_generation/InspectorMain.mm new file mode 100644 index 00000000..137c6a1e --- /dev/null +++ b/src/client/mac/crash_generation/InspectorMain.mm @@ -0,0 +1,65 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Main driver for Inspector + +#import "client/mac/crash_generation/Inspector.h" +#import + +namespace google_breakpad { + +//============================================================================= +extern "C" { + +int main(int argc, char *const argv[]) { +#if DEBUG + // Since we're launched on-demand, this is necessary to see debugging + // output in the console window. + freopen("/dev/console", "w", stdout); + freopen("/dev/console", "w", stderr); +#endif + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + if (argc != 2) { + exit(0); + } + // Our first command-line argument contains the name of the service + // that we're providing. + google_breakpad::Inspector inspector; + inspector.Inspect(argv[1]); + + [pool release]; + + return 0; +} + +} // extern "C" + +} // namespace google_breakpad diff --git a/src/client/mac/gcov/libgcov.a b/src/client/mac/gcov/libgcov.a new file mode 100644 index 0000000000000000000000000000000000000000..f45a58d71d01b554fbf4db03f4805f2123797e18 GIT binary patch literal 35048 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 zdG<2use_minidump_write_mutex_) pthread_mutex_unlock(&self->minidump_write_mutex_); } else { - // When forking a child process with the exception handler installed, // if the child crashes, it will send the exception back to the parent // process. The check for task == self_task() ensures that only diff --git a/src/client/mac/handler/exception_handler_test.cc b/src/client/mac/handler/exception_handler_test.cc index 59944377..d84f1997 100644 --- a/src/client/mac/handler/exception_handler_test.cc +++ b/src/client/mac/handler/exception_handler_test.cc @@ -55,6 +55,7 @@ static void *SleepyFunction(void *) { while (1) { sleep(10000); } + return NULL; } static void Crasher() { @@ -77,15 +78,14 @@ bool MDCallback(const char *dump_dir, const char *file_name, fprintf(stdout, "Minidump: %s\n", path.c_str()); // Indicate that we've handled the callback - return true; + exit(0); } int main(int argc, char * const argv[]) { char buffer[PATH_MAX]; - struct passwd *user = getpwuid(getuid()); // Home dir - snprintf(buffer, sizeof(buffer), "/Users/%s/Desktop/", user->pw_name); + snprintf(buffer, sizeof(buffer), "/tmp/"); string path(buffer); ExceptionHandler eh(path, NULL, MDCallback, NULL, true); @@ -97,8 +97,8 @@ int main(int argc, char * const argv[]) { perror("pthread_create"); } - // Dump a test - eh.WriteMinidump(); +// // Dump a test +// eh.WriteMinidump(); // Test the handler SoonToCrash(); diff --git a/src/client/mac/handler/minidump_generator_test.cc b/src/client/mac/handler/minidump_generator_test.cc index 62530832..3a77beb4 100644 --- a/src/client/mac/handler/minidump_generator_test.cc +++ b/src/client/mac/handler/minidump_generator_test.cc @@ -45,14 +45,13 @@ static bool doneWritingReport = false; static void *Reporter(void *) { char buffer[PATH_MAX]; MinidumpGenerator md; - struct passwd *user = getpwuid(getuid()); // Write it to the desktop snprintf(buffer, sizeof(buffer), - "/Users/%s/Desktop/test.dmp", - user->pw_name); - + "/tmp/test.dmp"); + + fprintf(stdout, "Writing %s\n", buffer); unlink(buffer); md.Write(buffer); diff --git a/src/client/mac/sender/English.lproj/Breakpad.xib b/src/client/mac/sender/English.lproj/Breakpad.xib new file mode 100644 index 00000000..7b5053a5 --- /dev/null +++ b/src/client/mac/sender/English.lproj/Breakpad.xib @@ -0,0 +1,834 @@ + + + + 1050 + 9F33 + 670 + 949.34 + 352.00 + + YES + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + Reporter + + + FirstResponder + + + NSApplication + + + 1 + 2 + {{196, 157}, {484, 353}} + 536871936 + + NSWindow + + {3.40282e+38, 3.40282e+38} + + + 264 + + YES + + + 266 + {{89, 279}, {378, 54}} + + YES + + 67239424 + 272629760 + RE8gTk9UIExPQ0FMSVpFLiBUaGUgPFJlYWxseSBMb25nIENvbXBhbnkgTmFtZT4gcHJvZ3JhbSA8UmVh +bGx5IExvbmcgQXBwIE5hbWUgSGVyZT4gaGFzIHVuZXhwZWN0ZWRseSBxdWl0LiA + + LucidaGrande-Bold + 1.400000e+01 + 16 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2OQA + + + + 6 + System + controlTextColor + + 3 + MAA + + + + + + + 268 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{20, 269}, {64, 64}} + + YES + + 130560 + 33554432 + + NSImage + NSApplicationIcon + + 0 + 0 + 0 + NO + + YES + + + + 266 + {{17, 191}, {450, 70}} + + YES + + 67239424 + 272760832 + RE8gTk9UIExPQ0FMSVpFLiBUaGUgc3lzdGVtIGFuZCBvdGhlciBhcHBsaWNhdGlvbnMgaGF2ZSBub3Qg +YmVlbiBhZmZlY3RlZC4gQSByZXBvcnQgaGFzIGJlZW4gY3JlYXRlZCB0aGF0IHlvdSBjYW4gc2VuZCB0 +byA8UmVhbGx5IExvbmcgQ29tcGFueSBOYW1lPiB0byBoZWxwIGlkZW50aWZ5IHRoZSBwcm9ibGVtLgoK +UGxlYXNlIGhlbHAgdXMgZml4IHRoZSBwcm9ibGVtIGJ5IGRlc2NyaWJpbmcgd2hhdCBoYXBwZW5lZCBi +ZWZvcmUgdGhlIGNyYXNoLg + + LucidaGrande + 1.100000e+01 + 3100 + + + + + + + + + 290 + {{17, 85}, {450, 28}} + + YES + + 67239424 + 272760832 + DO NOT LOCALIZE. Providing your email address is optional and will allow us contact you in case we need more details. + + + + + + + + + 289 + {{353, 12}, {117, 32}} + + YES + + 67239424 + 134217728 + Send Report + + LucidaGrande + 1.300000e+01 + 1044 + + + -2038284033 + 129 + + + DQ + 200 + 25 + + + + + 289 + {{243, 12}, {110, 32}} + + YES + + 67239424 + 134217728 + Cancel + + + -2038284033 + 129 + + + Gw + 200 + 25 + + + + + 290 + {{59, 58}, {195, 19}} + + YES + + -1804468671 + 272761856 + preston.jackson@gmail.com + + optional + + YES + + 6 + System + textBackgroundColor + + 3 + MQA + + + + 6 + System + textColor + + + + + + + 292 + {{17, 60}, {37, 14}} + + YES + + 68288064 + 71435264 + Email: + + + + + + + + + 289 + {{352, 59}, {16, 17}} + + YES + + -2080244224 + 262144 + Privacy Policy + + LucidaGrande + 9.000000e+00 + 3614 + + + -2040250113 + 36 + + NSImage + NSFollowLinkFreestandingTemplate + + + + 400 + 75 + + + + + 289 + {{259, 60}, {89, 14}} + + YES + + 68288064 + 71435264 + Privacy Policy + + + + + + + + + 274 + {{20, 124}, {444, 61}} + + YES + + 341966337 + 272760832 + DO NOT LOCALIZE: Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 + + + YES + + + + + + {484, 353} + + + {{0, 0}, {1680, 1028}} + {3.40282e+38, 3.40282e+38} + + + + + YES + + + alertWindow + + + + 42 + + + + sendReport: + + + + 45 + + + + cancel: + + + + 46 + + + + showPrivacyPolicy: + + + + 53 + + + + value: headerMessage + + + + + + value: headerMessage + value + headerMessage + 2 + + + 85 + + + + value: reportMessage + + + + + + value: reportMessage + value + reportMessage + 2 + + + 86 + + + + value: emailMessage + + + + + + value: emailMessage + value + emailMessage + 2 + + + 87 + + + + value: emailValue + + + + + + value: emailValue + value + emailValue + + NSNullPlaceholder + optional + + 2 + + + 90 + + + + initialFirstResponder + + + + 91 + + + + value: commentsValue + + + + + + value: commentsValue + value + commentsValue + + NSNullPlaceholder + optional comments + + 2 + + + 124 + + + + nextKeyView + + + + 125 + + + + nextKeyView + + + + 126 + + + + nextKeyView + + + + 127 + + + + delegate + + + + 128 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + -3 + + + Application + + + 1 + + + YES + + + + + + 2 + + + YES + + + + + + + + + + + + + + + + 3 + + + YES + + + + + + 4 + + + + + 6 + + + YES + + + + + + 7 + + + + + 8 + + + YES + + + + + + 9 + + + + + 12 + + + YES + + + + + + 13 + + + + + 14 + + + YES + + + + + + 15 + + + + + 18 + + + YES + + + + + + 19 + + + + + 20 + + + YES + + + + + + 21 + + + + + 48 + + + YES + + + + + + 49 + + + + + 58 + + + YES + + + + + + 59 + + + + + 66 + + + YES + + + + + + 67 + + + + + 116 + + + YES + + + + + + 117 + + + + + + + YES + + YES + -1.IBPluginDependency + -2.IBPluginDependency + -3.IBPluginDependency + 1.IBEditorWindowLastContentRect + 1.IBPluginDependency + 1.IBViewEditorWindowController.showingBoundsRectangles + 1.IBViewEditorWindowController.showingLayoutRectangles + 1.IBWindowTemplateEditedContentRect + 1.NSWindowTemplate.visibleAtLaunch + 1.WindowOrigin + 1.editorWindowContentRectSynchronizationRect + 116.IBPluginDependency + 117.IBPluginDependency + 12.IBPluginDependency + 13.IBPluginDependency + 14.IBPluginDependency + 15.IBPluginDependency + 18.IBPluginDependency + 19.IBPluginDependency + 2.IBPluginDependency + 2.IBUserGuides + 20.IBPluginDependency + 21.IBPluginDependency + 3.IBPluginDependency + 4.IBPluginDependency + 48.IBPluginDependency + 49.IBPluginDependency + 58.IBPluginDependency + 59.IBPluginDependency + 6.IBPluginDependency + 66.IBPluginDependency + 67.IBPluginDependency + 7.IBPluginDependency + 8.IBPluginDependency + 9.IBPluginDependency + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{641, 409}, {484, 353}} + com.apple.InterfaceBuilder.CocoaPlugin + + + {{641, 409}, {484, 353}} + + {196, 240} + {{357, 418}, {480, 270}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + YES + + + 1.120000e+02 + 1 + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 128 + + + + YES + + Reporter + NSObject + + YES + + YES + cancel: + sendReport: + showPrivacyPolicy: + + + YES + id + id + id + + + + alertWindow + NSWindow + + + IBProjectSource + sender/crash_report_sender.h + + + + + 0 + ../../Breakpad.xcodeproj + 3 + + diff --git a/src/client/mac/sender/English.lproj/Localizable.strings b/src/client/mac/sender/English.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..25ddc73174327512a9031af58315243e7f2cfd86 GIT binary patch literal 1360 zcmc(f%TD7!5JhXvSF|FrVUg@XNH8O@VIByE4`^a1v1H>A8$y1b;oNF99W{TA+$~yceoSySc?2n%YJGKkfYrK2%D@MmU?XEmxuxE1B_KHj?5(lhxP7jq_=zvEld1`hL#qJJo~p#CPzLWpLOXJjxk+?$vF;XqVKge#p4k=CjDD^+HBkeZm zGF4X)URouvF=Tko?vpl(SE!XT}sey4k(teM%O`NFC=j>K#aAeY8pIIBK# zrd~^Z#ZOAC7}Ce!Rc$?9i&Z3UOO!41RB_UQSX1w#>#%4@wIwAbzP7}ailtuF)#0Z+ zi4*w?`LL%F`AL1Ze6yavk$Yq)XSvZ?OW~7k>8sQ{CrWFF{lAkvTV$_&jUM*;ZPy=n zUw@GXhQ-3pZorpr^NxSnt^L@;x^nz$JLOL`gH4So|Barp#H(hNlsKd6{ixy~N1dXS J5e{2N@D7_K>TUo4 literal 0 HcmV?d00001 diff --git a/src/client/mac/sender/ReporterIcon.graffle b/src/client/mac/sender/ReporterIcon.graffle new file mode 100644 index 00000000..62cee85a --- /dev/null +++ b/src/client/mac/sender/ReporterIcon.graffle @@ -0,0 +1,2489 @@ + + + + + ActiveLayerIndex + 0 + ApplicationVersion + + com.omnigroup.OmniGrafflePro + 137.6.0.106738 + + AutoAdjust + + BackgroundGraphic + + Bounds + {{0, 0}, {512, 512}} + Class + SolidGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 2 + Style + + fill + + Color + + a + 0 + b + 0 + g + 0.852018 + r + 0.998962 + + + shadow + + Draws + NO + + stroke + + Draws + NO + + + + CanvasOrigin + {0, 0} + CanvasSize + {512, 512} + ColumnAlign + 1 + ColumnSpacing + 36 + CreationDate + 2008-11-14 16:58:15 -0700 + Creator + Preston Jackson + DisplayScale + 1 pt = 1 px + FileType + flat + GraphDocumentVersion + 6 + GraphicsList + + + Bounds + {{33.9443, 35.3885}, {444.111, 437.112}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 31 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.439247} + {-0.5, -0.485429} + {-0.446294, -0.512626} + {-0.409932, -0.494153} + {-0.373569, -0.47568} + {0.436363, -0.0733799} + {0.472729, -0.0549059} + {0.50909, -0.0364333} + {0.509091, 0.0364345} + {0.472729, 0.0549059} + {0.436368, 0.0733802} + {-0.373569, 0.475681} + {-0.409932, 0.494153} + {-0.446294, 0.512626} + {-0.500001, 0.485429} + {-0.5, 0.439247} + {-0.49998, 0.393072} + {-0.500002, -0.393066} + + + Style + + fill + + Color + + b + 0 + g + 0.770962 + r + 0.997971 + + Draws + NO + FillType + 3 + GradientCenter + {-0.609524, 0} + GradientColor + + b + 0 + g + 0.911574 + r + 0.998779 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.43 + b + 0 + g + 0 + r + 0 + + Draws + NO + Fuzziness + 7.2213706970214844 + ShadowVector + {0, 6} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Width + 7 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + Bounds + {{3.89085, 67.8908}, {404.218, 332}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 30 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.5} + {-0.459695, -0.475464} + {0.429465, 0.0537758} + {0.469773, 0.0783133} + {0.510074, 0.102849} + {0.510077, 0.198357} + {0.469773, 0.222892} + {0.429473, 0.247428} + {-0.00521517, 0.499998} + {-0.00521785, 0.5} + {-0.00521713, -0.113381} + {-0.44962, -0.458615} + + + Style + + fill + + Color + + a + 0 + b + 1 + g + 1 + r + 1 + + FillType + 2 + GradientAngle + 180 + GradientCenter + {-0.609524, 0} + GradientColor + + a + 0.5 + w + 1 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.51 + b + 0 + g + 0 + r + 0 + + Draws + NO + Fuzziness + 3.3371961116790771 + ShadowVector + {0, 2} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Draws + NO + Width + 2 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + Bounds + {{33.9443, 35.3886}, {444.112, 437.111}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 29 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.439247} + {-0.500001, -0.485429} + {-0.446295, -0.512626} + {-0.409932, -0.494153} + {-0.373568, -0.475681} + {0.436363, -0.0733802} + {0.472729, -0.0549062} + {0.509089, -0.0364334} + {0.509092, 0.0364341} + {0.472729, 0.0549056} + {0.436369, 0.0733803} + {-0.373568, 0.475681} + {-0.409932, 0.494153} + {-0.446294, 0.512626} + {-0.500001, 0.485428} + {-0.5, 0.439248} + {-0.499978, 0.39307} + {-0.500003, -0.393066} + + + Style + + fill + + Color + + a + 0.2 + b + 1 + g + 1 + r + 1 + + FillType + 2 + GradientAngle + 90 + GradientCenter + {-0.609524, 0} + GradientColor + + a + 0 + w + 1 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.51 + b + 0 + g + 0 + r + 0 + + Draws + NO + Fuzziness + 3.3371961116790771 + ShadowVector + {0, 2} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Draws + NO + Width + 2 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + Bounds + {{176, 102.384}, {158.841, 537.616}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 425 + + ID + 26 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Pad + 0 + Text + {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf350 +{\fonttbl\f0\fnil\fcharset0 CalistoMT;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\b\fs850 \cf1 !} + VerticalPad + 0 + + Wrap + NO + + + Bounds + {{176, 104}, {158.841, 537.616}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0 + g + 0.749523 + r + 0.997726 + + Font + CalisMTBol + Size + 425 + + ID + 27 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Pad + 0 + RTFD + + BAtzdHJlYW10eXBlZIHoA4QBQISEhBJOU0F0dHJpYnV0 + ZWRTdHJpbmcAhIQITlNPYmplY3QAhZKEhIQITlNTdHJp + bmcBlIQBKwEhhoQCaUkBAZKEhIQMTlNEaWN0aW9uYXJ5 + AJSEAWkEkoSWlhBOU1BhcmFncmFwaFN0eWxlhpKEhIQQ + TlNQYXJhZ3JhcGhTdHlsZQCUhARDQ0BTAgCEhIQHTlNB + cnJheQCUmQyShISECU5TVGV4dFRhYgCUhAJDZgAchpKE + n54AOIaShJ+eAFSGkoSfngBwhpKEn54AgYwAhpKEn54A + gagAhpKEn54AgcQAhpKEn54AgeAAhpKEn54AgfwAhpKE + n54AgRgBhpKEn54AgTQBhpKEn54AgVABhoYAhpKElpYG + TlNGb250hpKEhIQGTlNGb250HpSZIIQFWzMyY10GAAAA + FgAAAP/+QwBhAGwAaQBzAE0AVABCAG8AbAAAAIQBZoGp + AYQBYwCiAaIAogCGkoSWlg1OU1N0cm9rZVdpZHRohpKE + hIQITlNOdW1iZXIAhIQHTlNWYWx1ZQCUhAEqhIQBZKYD + hpKElpYHTlNDb2xvcoaShISEB05TQ29sb3IAlKIChARm + ZmZmAYN4dz8/AAGGhoY= + + Text + {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf350 +{\fonttbl\f0\fnil\fcharset0 CalistoMT;} +{\colortbl;\red255\green255\blue255;\red254\green191\blue0;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\b\fs850 \cf2 \outl\strokewidth60 \strokec2 !} + VerticalPad + 0 + + Wrap + NO + + + Bounds + {{33.9441, 35.3884}, {444.112, 437.111}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 16 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.439247} + {-0.5, -0.485429} + {-0.446295, -0.512626} + {-0.409933, -0.494153} + {-0.373569, -0.47568} + {0.436363, -0.073379} + {0.472729, -0.0549049} + {0.50909, -0.0364324} + {0.509091, 0.0364344} + {0.472729, 0.0549058} + {0.436368, 0.0733801} + {-0.373569, 0.47568} + {-0.409933, 0.494153} + {-0.446295, 0.512626} + {-0.500001, 0.485429} + {-0.5, 0.439247} + {-0.49998, 0.393072} + {-0.500002, -0.393066} + + + Style + + fill + + Color + + b + 0 + g + 0.770962 + r + 0.997971 + + FillType + 3 + GradientCenter + {-0.609524, 0} + GradientColor + + b + 0 + g + 0.911574 + r + 0.998779 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.9 + b + 0 + g + 0 + r + 0 + + Fuzziness + 8.0632610321044922 + ShadowVector + {0, 9} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Draws + NO + Width + 2 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + GridInfo + + GridSpacing + 4 + ShowsGrid + YES + SnapsToGrid + YES + + GuidesLocked + NO + GuidesVisible + YES + HPages + 1 + ImageCounter + 2 + KeepToScale + + Layers + + + Lock + NO + Name + Layer 1 + Print + YES + View + YES + + + LayoutInfo + + Animate + NO + circoMinDist + 18 + circoSeparation + 0.0 + layoutEngine + dot + neatoSeparation + 0.0 + twopiSeparation + 0.0 + + LinksVisible + NO + MagnetsVisible + NO + MasterSheets + + ModificationDate + 2008-11-17 11:41:28 -0700 + Modifier + Preston Jackson + NotesVisible + NO + Orientation + 2 + OriginVisible + NO + PageBreaks + YES + PrintInfo + + NSBottomMargin + + float + 41 + + NSLeftMargin + + float + 18 + + NSPaperSize + + size + {612, 792} + + NSRightMargin + + float + 18 + + NSTopMargin + + float + 18 + + + PrintOnePage + + QuickLookPreview + + JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPDwgL0xlbmd0aCA1IDAgUiAvRmls + dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGVlktvJEUQhO/9K5IbHFyuR9brioEV + nFh5JM5o5MWsPIA9IP4+X2b12gPrBbHWSt3lzkdFREb6Ud7Ko0R+asr+/+lOfpBfJIYW + /Z/8LNc35yTHsyROh/ZYxyTgfJTrN/zip7NcxVD3r1+e9oQbCd/J9fd3T8e7337/48cH + eSLjm3O2wEfR2ry8tiJ5hDJSVEk9pF7jkONJrr89JfnqV/p8uz1KK1YpJynNHlKVk2gJ + JZWqdqZz9iklBc21ZmkxlJoin0/vsEonTo6b9lC79iQzh6azSc7FYjINZWIkk4MCKtrG + ejhyRpZeRYcSNIbk7oXmtk5m4mRaD/NvYcOK1bKnpnkuu4qt6jqEVujLmtuOvFgdrXv7 + mcjWQxut71ds5LcbAIADIcTkDgpt4TKmyigWoXNzVAYtfYzdUe5fOT25ACxMtZAQiPeX + xEVTSCkNeaBLDbXWDhwfnxH1QHJX0sfiulDDhhpMgMnUJZAGDLkCea3T6b+9T3K+N/pf + F6qL8+ZW0hYDjM4ESlFubyTlAFd/kvfwRKilj3IFRdTQHJsk6EwzW5UvDwBY1xf2cNVL + SDWiyTa2AyL8JgXr8fBOPv/sCzm8l68PNERtwm0wGIb4yTrK2LiYt3+rI5+uY1df7JW8 + CD9tS/XNGUdxFSUs1e+yiQPuXPUMyVI9lL2qeh2bq16Rjet+qRdVLcWrceySz8+S30+A + zyTPTNiYWMQSe10Z64vY+/OoudZNus9dudwRqE+rVVty97v63bZd7iZHL7PkjnfYe5xw + vvTOAJtW5+gMv3vFB8RetF6yzYQ5x3/L08wKeQZ3t1pin5Fp1GpD0ORKy7AnlLN/kbPS + 2ofZwIlqwA1G35aT5d3JyGncLARwMKZb0Tt2gIAHLOBGpTJExgtaxZ/MjxbK+B8mYdQ0 + 5QuYoSumBgvBBXEsP0n9khlidnI8xrK6LZqBzVm2bFzEhIjMiIwcPyGGeQqjdjrwT7h+ + LYHiADxbwGHg6+Uux+3+4u1/I6yj5DSiaKw0CBBXpRSDluldCFM4zgHvPa9zujJygMR3 + RXlB+JWt1t0PvmNg35PwHxsOE4mw1Weu0cykNci2JJjJhX+sVUm1pt4BgIOOr6HBGsLd + eYUt0uRFYFIEgAl4n6yrBqw6QuzKxtA0wdf4g/OZ2QWMAd4DfUgXOqHaYjtc4/Gjshmh + y/PP/YQ62VDzj4dlZttYGh2ZHAwCzaCeVcoaJty3VGm2b4bnZwuhC2LommlOA9lxF2ub + WDS6QrjdWjcjNZJ3Uzh/OyA6IjK7cIVwj0t8fPwuD05ya6b+F7C1v1cKZW5kc3RyZWFt + CmVuZG9iago1IDAgb2JqCjk4MwplbmRvYmoKMiAwIG9iago8PCAvVHlwZSAvUGFnZSAv + UGFyZW50IDMgMCBSIC9SZXNvdXJjZXMgNiAwIFIgL0NvbnRlbnRzIDQgMCBSIC9NZWRp + YUJveCBbMCAwIDUxMiA1MTJdCj4+CmVuZG9iago2IDAgb2JqCjw8IC9Qcm9jU2V0IFsg + L1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSSBdIC9Db2xvclNwYWNlIDw8 + IC9DczIgMTIgMCBSCi9DczEgNyAwIFIgPj4gL0V4dEdTdGF0ZSA8PCAvR3MxIDE3IDAg + UiAvR3MyIDE4IDAgUiA+PiAvRm9udCA8PCAvRjEuMCAxMSAwIFIKPj4gL1hPYmplY3Qg + PDwgL0ltMiAxMyAwIFIgL0ltMSA4IDAgUiAvSW0zIDE1IDAgUiA+PiAvU2hhZGluZyA8 + PCAvU2gxIDEwIDAgUgo+PiA+PgplbmRvYmoKMTAgMCBvYmoKPDwgL0NvbG9yU3BhY2Ug + NyAwIFIgL1NoYWRpbmdUeXBlIDMgL0Nvb3JkcyBbIC0yNzEuMzA2MyAwIDAgLTI3MS4z + MDYzIDAgNTQwLjI2NApdIC9Eb21haW4gWyAwIDEgXSAvRXh0ZW5kIFsgZmFsc2UgZmFs + c2UgXSAvRnVuY3Rpb24gMTkgMCBSID4+CmVuZG9iagoxMyAwIG9iago8PCAvTGVuZ3Ro + IDE0IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDI1NiAv + SGVpZ2h0IDI1NiAvQ29sb3JTcGFjZQo3IDAgUiAvU01hc2sgMjAgMCBSIC9CaXRzUGVy + Q29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7dABAQAA + CAKg/p+2Bx4QJpBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDDQBg4DBgwYMGDA + wNjAA65NNU0KZW5kc3RyZWFtCmVuZG9iagoxNCAwIG9iago4ODMKZW5kb2JqCjggMCBv + YmoKPDwgL0xlbmd0aCA5IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2Ug + L1dpZHRoIDkxMiAvSGVpZ2h0IDkyNiAvQ29sb3JTcGFjZQoyMiAwIFIgL1NNYXNrIDIz + IDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0 + cmVhbQp4Ae3QgQAAAADDoPlTH+SFUGHAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + wMvAAKraAAEKZW5kc3RyZWFtCmVuZG9iago5IDAgb2JqCjExMDcwCmVuZG9iagoxNSAw + IG9iago8PCAvTGVuZ3RoIDE2IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1h + Z2UgL1dpZHRoIDI1NiAvSGVpZ2h0IDI1NiAvQ29sb3JTcGFjZQo3IDAgUiAvU01hc2sg + MjUgMCBSIC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4K + c3RyZWFtCngB7dKBDQAgDMMw/n+6SHBGvA+aeXMKFAucd8XlNiuw8U9BuQD/5e/bzj8D + 5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+ + vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889A + uQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/ + bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQ + LsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv + 284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyU + C/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7 + tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPl + AvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+ + 7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5 + AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79v + O/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1Au + wH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/b + zj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL + 8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2 + 889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC + /Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t + /DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA + /+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287 + /wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7A + f/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vO + PwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvw + X/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu3f/wUgwjJ6CmVu + ZHN0cmVhbQplbmRvYmoKMTYgMCBvYmoKMTIxNAplbmRvYmoKMjMgMCBvYmoKPDwgL0xl + bmd0aCAyNCAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCA5 + MTIgL0hlaWdodCA5MjYgL0NvbG9yU3BhY2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21w + b25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHsnYlfTV37/288 + j4xJUVJRGtCEoggliZCxyFQUlUiGNCgJIXOFyFTKlCljmUMlUaZK4XY/N31fv//jd621 + 9z57n6nxDPvU1ev1PHfts89a61zn+njvz7XW3uuvv/AHI4ARwAhgBDACGAGMQOeIQDfy + 0132hx7tHB8QPwVGQCciwOiwR48e//nPf/4r9QMH/gPHQaRwjk58FhwkRkCHI0ClSIT4 + 3//27Kmn10vuR0+vZ8///peoEjWpw98zDl0HIkAuT0GLIEWqxN59+vTt269ff8lPv379 + +vbt07s3iJSoEkSJmtSBrxWHqIsRoGKkWuzVqzcRYn/9AQMMDAwGDhxoyPwMHAh/GgzQ + 1+/fD1QJmtQDTTKc1MUPjGPGCIg1AqwYe8IFKmiRSNFgoKHRoEGDjY1NTIbAjyn5PxMT + Y+PBg4wMQZcD9PszmmQkiWZSrN8sjkv3IgBqJI6xJxEjaNHAAKRobDLE1HSombmFxbBh + w+nPsGHDLMzNzYaampqAKkGUA/SBk+TSFShJLlx174PjiDECoosAMY0gRj0qRuCi0WDj + IaZDzS2GWVpZjbC2sbG1tWN+bG1trEdYWVkOH2ZuBqIETRoYEEyCJOl1KypSdN8tDkjX + IsCqsVevPn37DwAwDjYBLYIUR9jYjRxlb+/g6OjkDD9j4H9OTo4O9vajR9nZgiyHW5iZ + DjEmmNTvB2aSgSQqUte+fhyvqCLAqJGiUZ+QcYipmYWllbXtyNH2js5jxrm4uI6f4Obu + 7j5xIvyfm9uE8a4u48Y6OznYj7KzGWE1zHyoKUjSYED/vqhIUX2vOBhdjAD1jWAbwTXq + GxgOMiFiHGEzcrSD0xgX1wnuEz0mT5nq6TVtmrf39One3t7TvLw8p0yePMndbbzLWGdH + +1G21lbDzU2HDDYayCmS+EhdjASOGSOg9QiQKg4p4vTpBxeqgEbz4VY2I+0dQYtuEydP + 8Zw23cd3pt/sOXPn+jM/c+fM9pvlO8PH22vqZA/3CS5jnQCT1pbDzACSAw0oI0llBy9a + tf7N4gB0LwIUjlDFoWwcPGSohaW13WgQ44SJk6dOmz5j1uy5/vMXLgoIDFyydGkQ+Vm6 + ZElgwOJFC+b5z/Hz9fH2nDzJzXUcSNKGQBIUSRlJa62ISN1LBxyxdiNA4QjGsS9cqYIa + hxE0Oo8bP3Gyp/eMWXP8FywOXLpsxargkNWhYWFr165bt27t2rDQNauDg1cuDwoMWDjf + f7avz7QpHm4uYxxH244gijQyGNCvDxR2EJHa/Waxdx2MAJEjgWP/AQMHmQy1sLIZ5TDG + xc3Dc7qvH2hxybKVwWvCwiPWb4jeFBOzecuWrVu3btmyOSYmemNUZPja0JBVy5cGLpo3 + Z6aP1+SJ4wGSdiOGw1WroYE+FHYAkegidTAlcMhaiwC5ViV1HICjkbGpuaU1qNF14uRp + Pn7+CwODVoaEhkdu2LR5a2x8QmJS8s6UXbt2pe7alZKyM2lHQnzcti0xG9eHr10dvHzJ + YpDk9KkeE1yc7G2thg0dAhetBJHURWrtw2HHGAHdigDI8T//Jc4R4DjEbPiIkfagxine + vnPmBwStXL02YkPM1u3xO3bu2r133/709IOHDh0mP4cOpqcf2Je2JzUlKSFu2+bo9eGh + wcsDF/r7+Xh6TBjnNNrGyoJctFJEkmtW3YoJjhYjoKUIsNaxT38DQ2NTCytbe2cXUOPM + uQsDV4SsjdgYExuflLI77UD64aPHMzNPnDyVnX0afrKzT508kZWZcezIwfR9e1KTE+O2 + booKX7MqaPG82T5eHhPGOoy0Hm4GiNTvR65ZUZBa+naxWx2LACtHcq1qYjbcepTjOLfJ + RI1LVq4J3wBiTE7de+DQ0Yysk9k5OecuXMzNu3QpH34uXcrLvXjh/NmcM6dOZB47nL5v + d8qOuC3RkWHBywLmASMnuY6Bi1Zz08GG5JoVBaljWYHD1VIEmEoOWEe4VjUHOI4Z7+E1 + g6gxNCJ6S3xSalr64eNZp86cvZB7Kf/y1WvXC2/cuHETfuA/NwqvX71SkJ938VzO6ROZ + Rw/u370zITZm/TqiyFnTp7i5OI4cMYyUdfqjILX07WK3OhaBbt2gsNqrN1jHwXCtOtJh + nNsUH7/5gSvXRERvjd+5+wCIMTvnQl7+lWuFN27dKbp3/wH8PCQ/8N/79+4W3b554/rV + gksXz505mXk0PS11x/aYqLXBQYvm+np5uDqPthk+1MSICFIPL1l1LDVwuFqIAJRyeuox + chw2YpSTyyQvX//Fy0LCN25NSNmbfjQTxHjp8jWQ4r0HDx6VPH7y9NmzZ8+ZH/jt6ZOS + 4ocP7hfdvnH9Sn7uuTMnjh/al5q0PWZ92KrA+X7TJ7uNtbe1NBtCBYmXrFr4erFL3YoA + raz27jtgoLHpMOvRY8ZP9vabv2TV2qjN8Tv3ph87ceZ8XsG1G7fvghSfPHv+ovTV6zdl + ZeXsT1nZm9evSl++ePb0cTFo8lbhlfyLOacyD+9P3RG7KXz1ssVzZ3i6uzjYWZmzgsQq + q24lB45W0xEg8456vcA7GpsOt7EfA9eqcxYtWx2xaXvSblBjzoX8qzdu33tQ/OTZi9LX + b8rL31a+q6p6//4D8/P+fdW7yrcV5WVvXr18/rTk4f2im9cLcs9mZx7atythS9TaVYHz + ZnpNcnEUChJnPTT9FWN/uhMBiRwHEzmOdZs6wz9gRVjUloTU/UeyzlzIv3ajiIjx5euy + 8rfvqj5UV9d8/Pjp8+fPX+AH/vPp08ea6uoP799VVpS/KX3+rOThvduFl/POnjp+cE/y + 9k3hwUsX+Hl7uDqOpISEaQ9YqYOC1J30wJFqOAK0ltNH32AwXKzaj3X39PUPDF4XHZu8 + 52BG9vn8azfvPighYqyoBC2CEL98ra2rl/qpq6v9+uUzqPL9u7dEkk8e3QdF5uacOLp/ + V/zmyDXLFs6ePpkI0swEpj16Y01Hw18wdqdLESDmEZasGgzi5DhvSXB4THzK/iMncvKu + 3Lj74PGz0jflle+raz59+QpK/NbQ0Pj9+w/Jz/fvjY0N3+rrQZSfP1Z/qHpb/vrF0+L7 + t6/nXzidkb57x9ao0OWL5hBB2lkONTYc0LeX3n96ICB1KUVwrBqMADGPvfsZGA2xGDF6 + jJun77ylIRExCanpx7MvFBTeATW+KntbRcRYW18PSvzx8+fff/+S/vn7758/vhNR1hFJ + vq8kinx07+bVvJysw2nJsRvDViyeM93DxcF2+NDBA/VhGhKvWDX4BWNXuhQBMtXRq+8A + QxMzq5HOE6b6zlsSErk5cffBzJzcqzfvFT8rLXv7vvojiPFbA2iRKPGf//3vf/8KfuDP + f/6B43//BE0SSdZ8eAeKfPLgTmH+uZNH9+3cvnHtikWzvSe52NsMMx1k0B+vWHUpQXCs + mowAKebA1erAwUMt7RzHT57hD3LckrjnUFbOpet3Hjx58QbY+AkuU0GMoMV//gEl/v79 + h/w00R/66+/fv//993+gSSLJb/W1oMjK8lfPiu/duHwh+9j+lO0bw5Yv8ps2cezoERZD + jAb0Q0Bq8ivGvnQoAlDMIVerYB5tHFwm+0ApJ2Jz4p7DJ87l37j76Nmr8ndEjd8af/wk + XAQtskL8P+EPEeafP6BJKskf3xuIIt+/ffPi8YNbV3NPHz9ABLlswSxPN+dRVmbG9IoV + V5brUJLgUDUVAcnVqvmI0eMmes8JWBUeQ+VYcONeyfM3bz/UfCFq/JsTIyhPqET+d4Ek + AZIN9V8/Vb8rL3368Pa13DPH9++M3bAmaN6MKeMdbYebDmauWLGko6kvGfvRmQh0I8Wc + /gaDh4J5dPOctXDFuk3xuw+dOFdw4/7jl2UAx9r6BkaNQEalYmRlSSlJIAmK/Fb3peZ9 + xetnj+5cA0LuT962PiTQ38fDxd6aXLH2xStWnUkRHKjmIsDg0cCIXK26Tp4xLyh0Y1zq + wayzIMcnpeVVBI7ffxI2UjXyNCS//T/mR+ogoSS9bP37RyMg8sNbRpDZR9N2bA5fFTB7 + mvsYesXavw9MQiIgNfdFY086EQHAY68++gNNyNXqpOlzA0PWx6akZ+bks3L8SuGoSI2s + GOUlKVEkILL2c3XlGyDk1YunjuxJiFm3fIEvc8U6yABLOjqRHzhIjUaA4pEUcyztnCZ4 + zlq0KnxL0r7jZy4V3nv8svz9x6/1jSwcpV2jlBjpH0JIMletcNH6vaGOCvLh7SvnTxxM + jdu4ZilcsY6zH2FuYgiTkAhIjX7X2Jn4I8Dg0XCIhbU91FbnBYVtit9zJDv3+t2SF+VV + rBzlLlXl1SgrSeai9dffP4gg4ZL1wc2Cs5kHdm6NDF4828vNeaQllHQQkOJPDxyhZiPA + 4pEUc8a4T5sTGBIVu+vgifNX7jx6XlZV87WeWEeQoxQclaiRHOYhSRBJ6jpEkB8qXj25 + f+PSmWNpiTFrl8+fMZmWdBCQmv2qsTcdiACHR1LMmeK7cEX45qT9GTn5Nx88e1NZ/YVc + rBLrKJRjM2qUU+RvIshvdZ/el78sKbp28eSh1LgNqwPnek+Eks5QBKQO5AcOUaMR4PE4 + auzE6f5L1myM330kO6/w3pNXb6s/K5BjC2qURSQI8m8o6nysKnv+8Ba5Yk3eEr5y4cwp + rg42w4YgIDX6XWNn4o+ABI+2juOnQjEnYuvO9Cy4Wi1+Uf7+U13DD9mL1ZblKFQkMZH/ + g6JO/ZfqytdP4Yr19NE98dGhS/2nTxqLgBR/duAINRwBDo9mVjDX4eO/NGxTwt5jZ/Jv + wNXqu5qv32Tl2Co1ygvyZ2P95w/kivXqhRPpKdsiVi2aNXW8IwJSw182dif6CFA8DjAa + MszWEeY6FgdHQjHn5MVrdx+XVhDz+Pev/wm9Y2vlKCjrACH/JRaSXLFCjTU/53hawqaw + IH+fSeNGo4MUfX7gADUaASEePXz8g8JiEvdl5BTcevgcpjrqGkgtR1DKab0cBYgka3XA + QsIV69tXj+9ev3jy4K5YmPPw85zgaIsOUqPfNnYm9ggw7pHgEZYC+C0OXh+beuhU7nUo + 5lRWw1TH3zDTIamstkmN0oL836+fDaTGSko6ORn7EmMIID0AkGZYYhV7iuD4NBcBaTzO + C1obs2NfxtnLtx/RYg6Z6oCrVXZGsa1ylFyzMles3799ra589eReYe6pQ6mx6xGQmvuW + sSddiYA0HgOC128HPOYV3n/6upIt5kiuVtsuR16QpMb6s5EA8sWj25fPZuzbEbM2aB4C + UlfSBMepmQjI4HEZwWMmg8cPMPX49y+yEKDdeOQvWdmSztcaMuehAJB4m4dmvm/sRdwR + EMw9gnukeDwsxGOHrlYpUBkxk5LOr79hzgMBKe6EwNFpMwLK8AhLATg8csWc9lytkvew + cKWrAn58EwByu5SDREBqMw+wb3FEAPCo10ffkJl7ZPAo5R55PLZXjpwgm5rInEdj/acP + 5S+KJQ4S5yDFkQc4ClFEQBEeSXGVw6NgrqP9emQJKQ9IpsQKj9Khc5AISFGkBA5CixGQ + xiPMPcoUV1WBRyEgqYMEQHIlVlyko8UvH7sWWwQ4PA6lK1f5uUd5PHaAjhILqRiQszy5 + VawISLHlB45HsxEQ4HE8vzSHn3tUDR45QRIHSQBJHCQAUrJIZ9I4vM1Ds9879ibKCEjj + kaxcZZbmcHiEuUe2uNoxPEquWMmiAGGJla5i5W/zQECKMk1wUBqKgASPNuS+R27lqhrw + yAqSAhLuu2IAya9ixfsgNfSFYzdijgCPx1Fj4b5H9sYOunL1wydmaY6K8MgD8l8OkE/u + Xc89eYgHpAV5UAACUsz5gmNTbwRk8Qj3PcKNHWrBIwdIskiHAJIs0oHbPI6nJW4Kg2c/ + 8oDs0V29nxlbxwiINQJkf7l+ZIcAIR4LmBs7yENzVOYeifvkVs3RG5PhNo/XFJDkPkj6 + oAAHGwSkWPMEx6WRCHB4tGDdI8HjSXLfI3tjB7kNmVFRR6s59P20KTrlIQ9IwZN0EJAa + +fKxE9FFoHsPHo/TyVNzEtOOw2MB6H2PDB5ZPapEjgoBSR4UsE0CSPKwcr3/dsfdPESX + Kjgg9UegW3dm5aqFjQN9qFzkNnhqDoPHauYhVirFI+8gufsgnzMOMmGT5FFzdDcPBKT6 + v3vsQXwRYPA4iLpHwGOoEI91wscCqAaP8g4SHhRAn6RDHjU3cyo8i9UCANlb7z8ISPEl + C45I3RGAyQ5yY4eJFB4vsk/Noc94VDEeFQCSPEmHPGqOAHIieRbrIAPYDxIBqe7vHtsX + XwTk8JjAuMfn5BHI6sCjUkDSZ7GSh5VbIyDFlyc4Io1EgOCxN4NH16kz4ZHkxD2qF48K + AQnPYt2bQB9WDrt5WJoiIDXy7WMnYosAwWNfA+oeYccOcI8MHuGZq2rDoxQguUfNXb/I + PqycBeTA/uggxZYrOB61R0CCR2vY0IriMUX9eFQCyGMEkEv8Ybsr2A8SAan27x47EF8E + WDyaWo4cQ/EYnbD3eE4+eSS5GvEoB8i3r57Aw8oBkFsjmO2uwEEiIMWXLTgiNUeA4rH/ + QBMLHo/pJy5ev0v2l/v67TvsZ6Xy4iozayJcpMM+rDw/59je+Og1S8h+kBJA4poANWcA + Ni+mCAjx6O2/JBTweEwDeJQGZP1XupvHNbLdFQtIewSkmNIEx6KZCEjjceHKiK0pgEfY + 0IrgsV6NeJRykLCbB9mgFba7kgPkf3sgIDWTCtiLCCLQjSmuUvfoPXfJmuh4gseb6naP + 5JJVcJsHt90VAeTOrbBhsu8UV3trc3SQIsgQHIIGI8Dj0R6KqwtXhhM8XmDw+EW9eJTo + kezm0VBH9oMUAtKdc5AISA3mA3al3QjAjVZk7lEej2VVsDu5cEMrVS1dFbQjC8jSx3d5 + QE52QUBqNzewd41HgMOjuTXg0ZfgcafG8MhfsEoB8szRPfEbSYmVAeSAvr0QkBrPC+xQ + OxGgeBxA8ehO3ONGcI9nwD0+K2O3Q5ZsaCXAmup+VQjIrPSdW8JXLPRFQGonJ7BXrUVA + gEeXySwes4h7LH1brW73SFTN6ZEH5IOb+RSQqwPneLs7kzlIBKTW0gM71nAE5PG45yjg + 8YGG8MgLEnbXoSXW0sdFVy8wgFxAADkCS6waTgnsTnsRkMbjghXhW3amaxKPvB4FgLwh + AeQ0d2c7S1MjBKT2MgR71mQEeDw6u0+bE7h6Y7yG8cgLUgjI81npyeAgWUAawyrWnvig + AE3mBfallQgAHnv27j/Q2HyEPbhHDo9Xi1j3+LfaVq4KCkJSDrKWzEE+kAbkcASkVpID + O9V4BBg8Gpla2gnxeENz7lFY0mEBWVFaUnSVA+QM6iCNDRCQGs8N7FDjEZDHY3J61nmC + xwpSXNUIHvkLVuogOUCePro7fgOUWKe5OdshIDWeGdihNiLA4XG4nbMbcY8bqHtk8Fir + 7qU5/CUrd8UqBcjMA8mbwUEiILWRGNinNiLA4NGAcY8ziHtk8FiiUTwqBuSl00ekAdkH + F+loI0ewT81FQB6Pu4+eztc4HnlBygNy+XwKSDN0kJpLC+xJOxGQxuP8FeGbkw9kgnvU + NB55PXIO8g2UWAkg4zaEBM72cnOiDhIBqZ00wV41FAGCxz4DjEwl7jFu95HTlwCPb6o+ + atA9SkqsTU1/KCA/V1e8LCm6cp44yHU8IPv1wjlIDWUGdqOFCFA89jMwNiNzjzPmL5fg + 8WVF9We2uMrsaMVXXtT1G1fRAUD+aKj9+A4BqYWEwC61GgEej05uXrMDQzZI8PhO03hk + r1ilAHnnyrnMA0kcIK3AQSIgtZov2LlaI0DwCPuvSvC4bnPSgcxzV+6UaAGPUg6SB2T2 + kdS4KMZB2pI5SHSQas0IbFybEZDFY5QUHn/8+p9673uUvfKlV6wSQH6oeFnMADIGHKQP + LNJBQGozWbBvdUdAgkcrcI8+85drF4+ygKyhDjIv+zABZMBsrwlOtsMQkOrOCWxfexGQ + 4NGWcY9RcalHsmlx9V1NbYPG8ShxkL+ZEisHyP1JMeuWzfPxGDfaymwwOkjt5Qv2rNYI + yOExJmk/cY/FLys+aLq4yly6Ckus32p5QG6PCmYBOcRIHx2kWrMCG9dWBDg8DrN1muA1 + OyAkanvq4ew8MveoJTxKAbKx/vOHcsZBAiDXIiC1lSbYr2YiwOJxsJnV6HEePvOWrZPg + sRzw2Ag3dvxu0tTcI1fZkQfk/RvEQRJA+nkSBznEEAGpmfzAXjQbAYpHfaMhBI+efgHB + UniE7ck1XFwVXLE2NREHyQHy8rnM/TsQkJpNDuxN0xGQxePamB2se9QeHqVLrMRBvn7K + AnI9BaQjA0g9fBarptMF+1NzBBg8GgIeHSke1zPu8f7T1+AetYVHeQf5ovj25bMISDUn + Azav7QgoxOPZy1Bc1SYeZQD5lQKyMO8UOMj1wYvBQSIgtZ042L9aIgB41OujL43HU3k3 + tI1HJYDM2AcOMoiZgxyKc5BqyQhsVIsRkMFjELjHfZlnL98ufqFdPMoBshIcZGHuqUOp + sRSQ4x1taIkVHaQWkwe7VnkEpPG4OBjc46FTeYWAx8qar9pzj6TIyq5ilZRYqYMkgAwL + 8veZBIt0EJAqTwdsULsR4PA4FOYeJ/n4UzxmiAKPzQNy1lQEpHYzB3tXRwQkeLRxHO85 + C/AYC3jMFQUeBYD8xcxBvngEJdaMfYkMIMeOQkCqIyWwTe1FQBaPYeAeCR4fse7xlxaW + 5nBLdJQCcldsZPBiBKT2sgZ7VlcEhHicCniMFBMehYD82Vj/6X05ALIgJyNNFpA9uqsr + PtguRkCDEeDxOGoscY9hMYn7MnIKKB4/wcpV7eJRASCf3CvMPXmQB6QFWcWKJVYNpgx2 + pcYIcHi0APfI4HGXeNwjX2L98/vfXxwgbxXkHE9L3BS21H/6JImDRECqMUmwaY1FoHsP + 8tScwUOtWDxuSkxj8fge8PhT23jkAfnvP79+fPtaXfn6yb3rBJDbIlctAgfpYMMCsns3 + jcUMO8IIqCkCMnhcFRm76+DJ3Ov3nrBzj5rYYI6v3ij8jZmDZABZRx0kC8hQDpCDyIMC + EJBqShFsVoMREOJxuv/SMMDj8ZyCW1Bc5fD4R9P3PcqKkrsPkgPkKwDkRSlAmhjq99bD + /Vk1mDbYlXoi0K07s3LVwsYB3OOiVZHbAI8XKR6rydIcEeCRK7H+IZsHNBJAPn9EAZmw + iQByInGQgwz6IiDVkyHYqiYjwOBxEHWPgMfQTQkMHp8DHuvAPUruQ5aFlib/VgLIlG0R + qxbNnOoKDhIBqcmkwb7UFQGY7NDrrW9oAnh0JXiM4PD4qlI8eFQAyIe38qHEygJyzCgr + UwSkulIE29VgBAge+xpQPE4ULR7lSqxvWQfJAHKKq4O1hcnA/uggNZg42JU6IkDx2J/B + 45SZBI8pjHsUFx6VAPLY3oTo0CX+3hPHjLREQKojP7BNzUaAxaOp5agxFI/RCXuP5+Tf + eigq90icqsBBfoc5SADk3esXT6SnbI1YuXAmAlKzWYO9qSkCDB4HmlhYO7iyeEw/QYqr + r96KyT1KBNkkKLESB0kAuWbJXCEgcU2AmnIFm1V/BCR4HDlmorf/klDA4zFR4lEakPVf + AJCP7167wAPSHh2k+tMFe1BvBKTxuHBlxNYUwOO1uxSP9d/FMffIzatwi3SYOciqsucP + bxJAxksDEh/9qN6UwdbVGIFuTHHV1JLgce6SNdHxBI83xeceJRes/0c2MP/1XQDInVvD + Vy70neJqb22OJVY15go2rfYIcHg0t7YH97hwZTjB44Vrdx+De/wiNjxKKjpkkU5D3ceq + smcsIDcSB+nOlVgRkGrPG+xAPRGAleRk7pHg0V0Kj2VVn+oaRLI0h7tclXeQpYyDZAAJ + +7MiINWTJtiqZiIgxONkX4LHnSLGI69HHpAPbuafObonXgDIAX17ISA1kz7Yi4ojQPE4 + gMfjRtY9Piur+iiFx//jGaXV34RzkMRBUkBmpe/cEr5igS8CUsXpgc1pNgICPLpweMwi + 7rFUjO5RWNGRB+TqwDnT3J3JIh0EpGazCHtTVQR4PDqDewxcszF+z9Ez+TcfiBaP/BUr + 3cCcArLo6gUBIEeYG8Mq1p54H6SqcgTb0VgEJHgcYQ94XLAifMvOdMBjEYtH2H+VvQ1Z + LFerQEjuglUAyBuMg2QAaWdpaoSA1FgKYUcqjIAEj3bO7tPmBK7WATzygmQBWVH6uOjq + +az0ZM5BIiBVmCHYlAYjAHjs2bv/QGNzKTxeFTUeeT1SQNaSOcgHDCA3EEC6OdsNR0Bq + MImwK5VFgMGjkelwKTzeoO6xVjj3KKLLVcEVqwSQJTwgZ0CJFQBpgA5SZVmCDWkoAgwe + DRg8ziDuMTk96zzBYwVZmiNK9yhXYuUAefro7ngEpIYyB7tRRwQEeHQj7nFD/G4orooe + j/wVqxQgMw8kbw5fPh8BqY5UwTbVHwE5PG5OPpAJeCwROR55PXIO8g04yEunj+yO2xDC + O8g+uEhH/TmEPaguAgrweOT0JR3AIyvIJnKbx9/kNo+KlyVFV84LAWlmbNAP5yBVlyvY + ktojwOHRjBRXZ8xfHq4zeJQD5DshIGd7uTnREisCUu05hB2oLgIEj30G0OKqm9fswJAN + cbsZPL6p+iji4iop6MgA8jMB5J0r5zIPJG1exzpIAsheuEhHdemCLak3AgSPsIOOsTQe + rxSVvKyo/swWV7W9RQCjPUX/L1ik86Oh9iMPyKiQQAJIWzIHiYBUbwph6yqMAI9HJxk8 + vgM8/uAfSS6uuUdOnMyDO1gHKQNIH5iDtEJAqjBZsCl1R0CCRyvGPa7bnHQg89yVO7qB + RykHCYCsYQCZfSQ1jgJyAgJS3QmE7as0AhI82jJ4jIpLPZJNiqtvdAKPsg7yQ8XLYuIg + 9yfFgIP08SCAHIwOUqUpg42pLwJSePSZv3xdDIPHYp1wj+SiVdpBMoDMyz6cuj0qJGC2 + FwBymKmRPjpI9aUQtqzCCAjwOMFrdkAI4PFwdh7FY40OuEeJIJuafsMcZGP95w/lPCCX + zfPxGDcaAanCfMGm1BoBFo+DzaxGj/PwmbcM8LifuEfA4wemuPq7SbzFVaamIwTkN9ZB + MoAMDvDzJIAcYoiAVGsaYeMqigDFo77RkGG2TgSPwVHbBXiE/Vcl+z2Ks7gqEKQ8IHfE + rEVAqihPsBmNREAWj2sleCwHPDbCjR3ix6O0gySAfP30/g0KyPUUkI4MIPXwUXMaySns + pP0RYPBoSPHo6RcQvJ7B4/1nr9/V1OoKHiUlVqGDvHw2cz8Csv2Zge/UQgTk8bgD3ONl + cI+6hEcZQH6lgCzMOwUlVgSkFrIKu2xvBACPen30CR4dJwjx+FS38CgPyBfFty+fzdgH + gAxiSqxDcQ6yvUmC79NUBBTgcV/m2cu3i1/oFh7lAFkJDrIw99Sh1Nj1wYv9PMc72tAS + KzpITWUW9tOeCEjjcTFxj4dO5RXeJ3j8qjvuUdEcpASQYUH+PpNgDhIB2Z4MwfdoMAIc + Hocyc49Ba2N27MvQSTw2D8hZUxGQGkwr7KqdEZDg0cZxvKcf4DEW8JhL8Fipa3gUOMhf + zCKdF4+og0yMoYAcOwoB2c4swbdpKAJCPE7y8Q8K02E8KgXkrtjI4MUISA2lFHbTgQgI + 8Th11uLgSF3GowwgP70vB0AW5GSkyQKyR/cOhAzfihFQVwR4PI4ay+AxcV9GzuXbj6C4 + +gmW5vzSiaU5zII5SUUHNjCHR839+Pa1pvL1k3uFuScP8oC0IKtYscSqrnzCdjsWAQ6P + FuAeGTzu0l33KBEk6PHfXz8b6zlAHk9L3BS21H/6JImDREB2LG/w3eqJQPce5Kk5g4da + SfCYlpFTQPD4HvD4U9fwyDvIf/8hgKwmgLxOALktctUicJAONiwgu3dTT0CxVYxA+yOg + AI8HT+Zev/dEF4urzFUr8yQdBpB1AMjnj24V5BBAhnKAHEQeFICAbH/W4DvVFQEhHqf7 + Lw3blJh2PKfglgCP7IaPvEMT+2/cfZAcIF8BIC9KAdLEUL+3Hu7Pqq6cwnbbHYFu3enK + VRMLGwdwj4tWRW7bdfDkRYrHarI0R4z7r7b47wEHyP+BgxQAMoECciJxkIMM+iIg2500 + +Ea1RYDB4yDqHgGPoZsSGDw+B/dYB+5Rch9yiyIQ0QlKAJmyLWLVoplTXcFBmhj2R0Cq + Lamw4fZGACY79HrrGxI8uhI8RnB4fFWpu3jk5iDJlAcLyIe38sFBsoAcM8rKFAHZ3pTB + 96kxAgSPfQ0oHid2GjzKlVjfsg6SAeQUVwdrC5OBCEg15hU23a4IUDz2H2hiYe3gOmUm + wWMK4x51G49KAHlsb0J06BJ/74ljRloiINuVMPgmtUaAxaOp5cgxFI/RCXuP5+TfeqjT + 7pEYWYGD/A5zkADIu9cvnkhP2RqxcuFMBKRakwobb28EFOAx/QQprr56q8vuUSJIumhO + 4CCP7Y2PXrNkrhCQuCagvcmD71N5BIR49PZfEgp4PNYp8CgNSNif9e2rx3evXeABaW9t + jg5S5fmEDXYoAjwe7cE9LlwZsTUF8HjtLsVj/XfdnHvkpl0Ec5ANdZ+qyp4/vJmfIwdI + fPRjhzII36zKCMBSOVJcpe7Re+6SNdHxBI83dd89Si5Y4TYPWKRDNjDnALlza/jKhb5T + XBGQqswkbEsFEeDwaG4NePRduDKc4PHCtbuPwT1+0XU8Sio6ZA6yoe5jVdkzFpAbiYN0 + pyXWAX17ISBVkEnYhCoiIMCjuxQey6o+1TXo6NIc7nJV3kGWEgeZlc4AEvZnRQepiiTC + NlQVASEeJ1M87uxEeOT1yAPywc38M0f3xDOAdCZzkAhIVWUTttPRCFA8DqDukeJxY/ye + Y2fAPT4rq/oohUcx76DD41D+N+EcJHGQHCC3hK9Y4IuA7Gj+4PtVGgEBHl0AjyvCt+xM + zyLusbQzuEdhRUcekKsD50xzR0CqNJ+wsY5FgMejs7v3nMA1gMejgMcHnQaP/BUr7M9K + S6ylj4uuEgfJAXKEuTGsYu2J90F2LJPw3SqIAOCxZ+/+A43NR9gDHhdweCxi8QgbzLG3 + Ievq1SoQkrtgpYCsJSXWBzcYB8kA0m64qRE6SBUkEzbR4QgweDQytbRzdp82J3B1J8Qj + L0gWkBUEkOez0pMJIGeAg0RAdjiPsAGVREAxHq92KjzyepQD5AYCSDdnBKRKkgkb6XAE + ODwOt3N24/F4g7rHWuHcow5frgquWCWALAFAZkoB0gAdZIezCRvoYAQYPBow7nEGcY/J + 6VnnrxaVlFaQpTmdwj3KlVg5B3n6yO54BGQHMwjfrsoIyOJxQ/zuo6fzOx0e+StWaUAe + SN4cvnw+dZBmxghIVWYWttWOCMjhcXPygcxOiEdej5yDfAMl1ksAyLgNIYFzvFgH2QdX + sbYjifAtKosAwWOfAUamnHvcELf7yOlLnRCPrCCbyG0edA6y4mVJ0ZXzmdKA7IdzkCpL + LWyo7RGgeOxnYGxG5h5nzF8ezuHxZedyjwoc5DshIGd7uTnREisCsu1JhO9QWQR4PDq5 + ec0ODJHg8c27j52ouErkKAPIz9UAyDtXzmUeSNq8jnGQVuAgYfMAXKSjsvTChtoWAYJH + 2EFHgsd1BI/nrhSVAB4/s8XVJrq2hclo3f5/wSKdHw21HzlApsZFhQQSQNqSRToIyLal + EJ6twgjI4jGKc48Ujz/4R5Lr9twj988IFSTnIAkgixlAxgAgfWCRDgJShbmFTbU5AhI8 + WoF79Jm/fN3mJILHO50Tj1IlVgBkDQPI7MMEkAGzvSYgINucQfgGVUZAgkdbpwnEPUbF + pR7JJsXVN+9qahs6HR4lDvI3LbF+/sACcn9SzLpl83w8xo22MhuMDlKVGYZttSECLB4H + mwEePQgeY5L2EzwWv6z40PncI7loFTrIbywg8wCQ26OCGUAOG2Kkjw6yDTmEp6ouAhSP + +kamwygeA0Kitqcezs7rxHiUAmRj/ecP5YyDBECuRUCqLrGwpfZEgMfj6HEePvOWSeOx + EVau/m7qPMVVpqYjD8j7Nygg1wcH+HmCgxw2xBAB2Z5swvd0NAIMHg2HEDx6+gUES+ER + 9l+V7PfYOYqrAkE2NREHyQHy8rnM/TsQkB1NJ3x/xyIgi8e1MTtY91gO7rFz4lHeQb5+ + KgVIRwaQevgs1o4lF767zREAPOr10Sd4dKR4XM+4x/tPX0NxtbPiUd5Bvii+ffls5j4E + ZJsTCN+gyggoxOPZy1Bc7cx4lAHk15p3AMjCvFOHUrevD14MDhIBqcokw7ZaHQFFeDyV + V9jZ8agEkBkEkEH+dA5yKM5BtjqL8EQVRYDD41ArWlwNAve4L/Ps5dvFLzo3HuUAWUkA + mQuAjKWAHO9oQ0us6CBVlGjYTKsiIMDjeE+/xcHrY1MPncoleKys+dp53aNkUYCgxPri + EThIAsgwAOQkWKSDgGxVCuFJqouAEI+TfPyDwgCPGV0Cj80AMjJ48aypCEjVZRm21NoI + SPBo4zh+6qwuhUeBg/wFc5CfPpQDIAtyMvYlMoAcOwoB2do0wvNUEwEej6PGMnhMpHh8 + xLrHX51waQ6zHkBywQr7s8KTdH58+1rDOchdsRwgLcgiHXSQqsk1bKXlCMjiMTJ2V1dx + jxJBEgf56ycA8j0LyLTETdRBSgDZo3vLkcQzMAIdj0D3HuSxAIOHWknwmJaRU3Ab8Pj+ + EyzN6dx4FDjIf1hAPrl3PffkQQRkxzMLW2hHBDg8WrDuEfB48GRu4b0uUFxlrlqZBwX8 + oYCso4C8VZBznAByqf/0SQSQg8h9kAjIdiQXvqXNERDicbr/0rBNiWnHeTz+7Ox45AH5 + LwVkdeVrAORFAOS2yFWLoMTqYGNhQh1k925tji2+ASPQxgh0686sXLWwcYDi6qJVDB6v + 33vyurKazD12hg3m+OqNwt84QP4PHCQB5PNHFJAJm0IRkG3MJjy9oxFg8DiIukfAYyiD + x1vUPdY1EjyyGz4qTOVOcZC7D5ID5CsOkBGrFs2c6soAsrcePvqxo7mG728xAjDZoddb + 39BEgsdt4B4vAh5fdRk8cnOQZMpDASAnMg6yLzrIFpMJT+hwBAge+xpQPE6keEwg7vHW + o+dQXCV4lNyH3ClIqORDKAFkyjYCyCmuDtYWJrCBOQKyw9mGDbQQAQEeXafOXLQqYltK + 18OjAkA+vJWfc3xvQjRxkBPHjLQ0HWSAgGwhl/DljkeAxaOp1agxPB7zbz3sUniUK7G+ + pQ7yRDoAciUCsuNZhi20MgIUj/0HmlhYO7hOkcLj265SXGUuYeVKrASQxwggl8z1RkC2 + Mp3wtA5GgMOj5cgxE739l4RGJ+w9ltP18CgFyO/1X6vfvnp899pFAOTWiJULwUHao4Ps + YKbh21sRAWk8LlwZsTUl/cTF63efvCJ4/N4V5h65Co88IG8SQMZHrxECEp9s1YqswlPa + G4FuTHHVlOJx7pI10fFdFI8ygPzCAPLCifSdW8NXLvQlgDTHEmt70wzf17oI8Hi0B/fI + 4vHCtbuPAY9f6rsUHqVKrA11n6rKnj8UANKdK7EiIFuXWnhWOyIAK8nJ3KMMHm8+fF5W + 1XXmHqUuWOE+SFik871eFpCTEZDtyC98S5siwOHR3Brw6LtwZfjWneknuioehbvr/Gyo + +1hV9gwAeebYnviNxEEygBzQtxcCsk05hie3PgIUjwMoHt29wT1upO7x5sNnZVUf6xq6 + yNIcDo/yDrIUSqwXstJ3bglfsdAX9mdFB9n61MIz2x4BAR5dJrN4zCJ4LO2C7hFkyS2a + I6tYGUA+AEAeBUCuDpzj7e5MFukgINueZ/iO1kVAHo97jp7Jv/lADo+daQcdHofyv3GC + pPuzgoMsfVzEAXIBAeQIc2NYxdoTb/NoXX7hWW2KgDQeF6wI37IzvSvjsQVATnN3trM0 + NUJAtinJ8ORWR4DHo7P7tDmBqzfGd3E88oIUAPLqecZBIiBbnVh4YnsiAHjs2bv/QGPz + EfbgHjk8Xi1i3SPsv8rehtxVrlZlHWQtKbE+uCFxkNPcnO2GIyDbk2v4npYjwODRyNTS + TohH2J5ctrjahfQoKemwgKwoLSkigEyGEuuCGYyDNEAH2XJy4RltjYA8HpPTs84TPFaQ + pTldEo/8BSstsXKAPH10d/wGKLEiINuaZHh+qyPA4XG4nbMbcY8bqHtk8FgrnHvsSnjk + BSkFyMwDyZvDV8xHQLY6u/DENkaAwaMB4x5nEPeYnJ4JeCzp0njk9SgE5KXTR3bHCQHZ + BxfptDHd8PQWIiCPx91HTud3eTzyguQA+ZI4SArI5QwgzYwN+uEcZAvphS+3LQIcHs1I + cXXG/OXhm5MPIB7JCgFuTQALyHdvoMTKADIkcLaXmxMtsSIg25ZueHYLESB47DPAyJS4 + R685gSEb4gCPlwCPb6o+dmH3KBFkE7nN4+/v9Z+rK16W3LlyjgBynQCQvXCRTgsZhi+3 + IQIUj/0MjKXxeKWo5CVfXG2ipCAZ2rV+BID80VD7kQdkFAKyDSmGp7YhAjwendy8Zgvx + +K6r45G9YpUHZBIBpA/MQVoRB4mAbEO64anNR4DgETaYMzazYtzjus1JBzLPXblD8PiZ + nXvsqniUcpAAyBoGkNlHUuMoICc42ZJFOuggm08xfLUNEZDg0ZbBY1RcKuseCR5/8I8k + 71pzj9x1Ob1ilQDyQ8XLYuIg9yfFEEB6EEAORkC2Id3w1OYjIIVHn/nL18UweCxGPFJJ + SjtIBpB52YdTt0eFBMz2AkAOMzXSR0A2n2T4aqsjIMDjBK/ZASGAx8PZtLj6rgbxCJJk + AfkbSqyN9Z8/lPOAXDbPx2PcaARkq3MNT2wxAiweB5tZjR7n4TNvGeBxP3GPgMcPjHv8 + 3dR13SMhpBCQ31gHyQAyOMDPkwByCOzPiot0Wkw1PKEVEaB41DcaMszWieAxOGo74DGP + zD0CHmH/VcmGVl3TPUoE2dQkC8gdMWsRkK3IMDylDRGQxeNaCR7LAY+NcGNHV8ejPCBf + P71/gwJyPQWkIwNIPXzUXBvyDk9VHAEGj4YUj55+AcHrGTzef/Ya8UjYSH8UOMjLZzP3 + IyAV5xQebXcE5PG4A9zjZXCPiEdOjTKA/FrzDgBZmHcKSqwIyHZnHr5RUQQAj3p99Ake + HScI8fgU8cjLUb7E+qL49uWzGfsAkEFMiXUozkEqSi881rYIcHgcyhZX18bs2Jd59vLt + 4heIRzk9wuYBcJvHj29fayoJIHNPHUqNXR+82M9zvKMNLbGig2xb9uHZshGQxuNi4h4P + ncorvE/w+BWLq7wkZR2kBJBhQf4+k2AOEgEpm1v4d5sjII1H/yCCxwzEIy9DyW9Sc5Ay + gJw1FQHZ5tTDNyiIgASPNo7jPf0Aj7GAx1yCx0rEo0SL9BcJIH8xi3RePKIOMjGGAnLs + KASkgvzCQ22KAI/HUeMm+fgHhbF4fITuUVqM8JcSQO6KjQxejIBsU9rhyUoiIMTj1FmL + gyMRj3I6lByQAuSn9+UAyIKcjDRZQPboriTYeBgj0EIEmPseBw+1GjWWwWPivoycgtsE + j59gac4vXJojUaMCQD65V5h78iAPSAuyihVLrC3kHL6sNAIcHi3APTJ43IXuUaBAmV8Z + QP75/e+vn431HCCPpyVuClvqP32SxEEiIJUmHL7QbAS69yCPBWDwOB3c46bENBaP7wGP + PxGP0oLkHCRsYA5zkNWVr5/cu04AuS1y1SJwkA42LCC7d2s26vgiRkBhBLp1Z5bmCPB4 + 8GTu9XtP2OLqP7+73g460gqU+UsIyDoA5PNHtwpyCCBDOUAOIg8KQEAqTDc82EIEGDwO + ou5xuv9SgsfjOQW3wD2+/1RH8cjqUSYtu+yfsoB8BYC8SAAZQQDpCoA0MdTvrYf7s7aQ + ePiyogjAZAdZuWpiYeMA7nHRqshtuw6evEjxWE2W5nTFDeZa+KeGAyRsYN4oAGQCBeRE + 4iAHGfRFQCrKNjzWUgRk8Bi6KYHB43MWj5L7kFtI0i70shJAphBAzpzCALI/ArKlzMPX + FUSA4LE3g0fXqTMXrYrg8PiqEvGo7N8YOUA+vJWfc3wvC8gxoyxNEZAKcg0PtRwBgse+ + BsQ9jpkI7pHD40PEozIxwnEZQL5lHGS6BJDWFiYDEZAtJx+eIRsBisf+A00srB1cp1A8 + pjDuEfHYjB7ZR82R265YB0kAeWxvQnToEn/viWNGIiBlEw3/blUEWDyaWo4cM9Eb8Bid + sPd4Tv4txGNzapQC5HeYgwRA3r1+8UR6ytaIlQvBQdojIFuVfXiSTARk8LgyYmtK+glS + XH31Ft1jc5JU6CCP7Y2PXrNkrhCQuCZAJuPwz+Yi0I1xjwwe5y4heDyGeGxOiOxrAgf5 + vf4LAPLx3WsXCCDDWUCao4NsLvHwNUUR4PFoD+5xIYvHa3cfEzzWf8e5R+XKFACyoe5T + VdnzhzeJg2QA6c45SHz0o6K0w2NKIgAryUlxlcPjmuh4gsebD5+XMUtzcO5RqSCVAHIn + AaTvZHCQCEglSYeHlUWAw6O5NeDRd+HK8K07009cYPD4BfGoVIvkBU6PdAPzuo9VZc9Y + QG4kDpIB5IC+uHmAstzD4/IREODR3XvuEgken5VVfapr+MlvEdBsZnbRFzlBwm0e1EGW + EgeZlc4C0gUBKZ9veKTZCAjw6DJZiMfSt9WIx5b+meH0yAPywc38M0f3xG9cEzjH292Z + zEEiIJtNQHxRKgIUjwOoe6R43Bi/5+gZcI+Ax49SeOy6O+g0J0pOkLD9HAvIIgrILeEr + FvjCBuYjqIPsibd5SCUd/qEsAjJ4XBG+ZWd6FnGPiMfmZCh5jdOjPCBXB86Z5u5sZwkb + mKODVJZ+eFwmAjwend295wSuZvH4APEokVzzv3CCFADyKnGQPCCNYRUrAlIm7/BPhREA + PPbs3X+gsfkIe3CPC1g8Xi1i8QgbzOFjAVqnRwrIWlJifXCDcZAEkG7OdsMRkApTDw8q + iACDRyNTSztn92kSPML+q+gem5ch/6oMICtKHxddPZ+VnkwAOYM6SGMDBKSC3MNDchGQ + x2NyetZ5gscKUlxFPPKyU/obp0cpQJ4+ujt+AwJSLuHwQLMR4PA43M7ZTRaPtcK5Ryyu + KtWjZFEA6yArSksAkJkHkjcjIJtNPnxRNgIMHg0Y9ziDuEcGjyWIR+Xyk3tFESAvnT6y + O04IyD64SEc2+/Bv2QjI4nFD/O6jp/MZ94h4lBOesgOcIOUBuXw+dZBmxgb9sMQqm334 + t0wEpPE4f3n45uQDmeAeEY/KlKf4OKdHzkG+gRIrA8iQwNlebk60xIqAlMk+/FM2AgSP + fQYYmbLuMWRD3O4jpy8BHt9UfUQ8KtaewqNUkE1NfyggP1dXvCwpukId5DoBIHvhHKRs + AuLfwghQPPYzMDYjc48zODxeKSp5icVVhbJTelAAyB8NtR/fISCFeYa/ty4CPB6d3Lxm + Bwrw+A7xqFR7Cl+QB+SdK+cyDyRtZgFpRRwkArJ1edlFzyJ4hB10JHhcR9zjuSt3CB4/ + s3OPTTTRFKYgHhREQCEgs4+kxkUxDtKWLNJBB9lFlda6jy3Boy2DxyiJeyR4/MHf94hz + jwLlKflVDpDFBJD7k2IAkD4eLvZWZoMRkK3Lyy56lgSPVuAefeYvXxeThHhUoraWD0sD + soY6yLzsw6nbo0ICZntNcLIdZmqkj4DsolprzccW4HECcY9RcalHsmlx9V0N4rFlBcqc + wQLyN5RYG+s/fyh/KQHksnk+HuNGIyBbk5Rd9xwWj4PNrEa7ePjMWwZ43E/cY/HLig/o + HmXE1oo/hYD8VisEZHCAnycBJNnAHBfpdF3FNf/JKR71jUyH2ToBHgOCo7anHs7OI3OP + gEfYYE7yUDl0j61QI5yiBJA7YtYiIJtPRXz1r78EeBwnjcdywGMj3NjxuwmLq61TInOW + LCBfP71/gzrI9RSQjgwg9fBZrCg/BRFg8Gg4hODR0w/x2BblKTlXASAvn83cj4BUkH54 + SDoCsnhcG7ODdY+IRyVya/GwFCC/1rwDQBbmnYISKwJSOvnwL7kIAB7J9uSAR0eKx/WM + e7z/9DW6xxaFp+wEWUC+KL4NgNwHgAxiSqxDcQ5SLhPxAERAIR7PXr5T/ALxqExtLR+X + AWQlAWTuqUOpseuDF/t5jne0oSVWdJAoQdkISONxcTDB46m8QorHr1hcbVl6is9QCMgM + AGRYkL/PJJiDREDKZiL+DRHg8DjUajQprgaBe9yXcfbybcSjYp219mhzgJw1FQGJ4lMc + AQkebRzHe/oBHmNTD53KJXisrEE8tlZ9Cs6TAPIXs0jnxSNwkBn7EhlAjh2FgFSckF37 + qBCPk3z8g8IQjwq01Z5DSgC5KzYyeDECsmurTvmnF+Jx6qzFwZGIx/aIT9F7pAD56X05 + ALIgJyNNFpA9uiv/dvCVLhYBHo+jxjJ4TKTu8RFbXP2FS3MUSa1Vx+QBea8w9+RBHpAW + ZBUrlli7mOSa/bgcHi3APTJ43IXusVVqa8VJPCB/NtZzgDyelrgpbKn/9EkSB4mAbDZF + u9SL3XuQxwIMHmolwWNaRk7BbcDj+0/1jT8Rj62QndJTOEDC/qw/vn2trnz95N51Asht + kasWgYN0sGEB2b1bl8o5/LBKI6AAjwdP5hbee4LFVaUqa8MLDCD//P7318/GOgrIWwU5 + BJChHCAHkQcFICCVJmgXe0GIx+n+S8M2JaYdzym4JcAju6NVG5IQT5VEQBaQrwCQFwkg + IzhAmhjq99bD/Vm7mOyUfdxu3enKVRMLGwdwj4tWRW7bdfDkxesEj9Vk7hE3mJNIq32/ + cID8HwvI548oIBMoICcSBznIoC8CUll+drXjDB4HUfcIeAzdlMDg8Tm4xzrqHhGP7RMi + +y4lgEwhgJw51RUcpIlhfwRkV9Odks8Lkx16vfUNCR5dp85ctCqCw+MrxGOHZMi/WQ6Q + D2/l5xzfywJyzChLUwSkkuzseocJHvsaUDxOVIRHyWM6+PzC39oWARlAvmUcZDoDyCmu + DtYWJrCBOTrIric++U9M8dh/oImFtYPrFIrHFMY9Ih7bJrrmzlYIyGN7E6JDl/h7Txwz + EgEpn5hd9AiLR1PLkWMoHqMT9h7Pyb/1kHOPiMfmhNbK1wSA/A5zkADIu9cvnkhP2Rqx + cuFMBGQXlZ6ijy2Dx5UR21LST5Di6qu3WFxtpdpacZoSQMZHr1kyVwhIXBOgKEm70DEh + Hr3nLgkFPB5DPLZCYW07RQjI+i8AyMd3r10ggAyngLS3NkcH2YVUp/Sj8ni0B/e4cGXE + VoLHa3cfEzzWf8e5x7bJTvnZ0oCsKnv+8GZ+zrG9DCDdOQeJj35Umqld4wVYKkeKq9Q9 + Ah7XRMcTPN5E96hcWe17RQkgdxJA+k52RUB2Db218Ck5PJpbAx59F64M37oz/cQFBo9f + EI/tk57idwkA2VD3sarsGQvIjcRBMoAc0Bc3D2ghYTv5ywI8ukvhsazqU13DT36LAMU5 + hkdbHwFZQJYSB5mVzgLSBQHZyaXWmo8nxONkxGPrxdWOMzk9/oZVrAwgH9zMP3N0T/zG + NYEASGcyB4mAbE3Wdt5zKB4HUPdI8bgxfs+xM+Aen5VVfZTCI+6g0w4FyryFEyRsP/ed + lFhLHxdRQG4JX7HAd7KL/QgssXZepbXqkwnw6AJ4XBG+ZWd6FnGPpW+r0T3KyKnDf3J6 + lAfk6sA509yd7RCQrcraznsSj0dnd+85gasBj0cBjw8Qjx0Wn6IGOEEKAHmVOEgekMaw + irUn3gfZeRXX7CcDPPbs3X+gsfkI2J7cdwGHxyIWj7DBHHufFV6tKpJXm49xeqSArCUl + 1gc3GAfJAnK4qRE6yGZTtlO/yODRyNTSztl9GuKxzfpq8xs4QbKArAAHefV8VnoyAeQM + 6iCNDRCQnVpzzXw4eTwmp2edv4p4bLPOWvsGTo8ygNwdv4EA0s3ZDgHZTL529pc4PA63 + c3bj8Qjbk0NxtVY494iXq60VXEvncYKUALIEAJmZnrwZAdnZ1dbi52PwaMC4xxnEPTJ4 + LCmtIMVVdI8tiasdr3N6FALy0ukj0oDsg4t0WkzeTniCLB43xO8+ejof8dgOmbX+LZwg + pQF5AAC5fD51kGbGBv2wxNoJ5dbSR5LG4/wV4ZuTD2SCe0Q8tl5dbT+T0yMHyDdQYiWA + jNsQEjjbi3WQCMiWkrcTvk7w2GeAkSnnHjfE7T5y+hLg8Q26x7brrNXvoIJsavpDAfm5 + uuJlSdGV85kAyHUCQPbCOchOqLhmPxLFYz8DYzMy9zhj/nIJHl+ie2y1uNpxogCQPxpq + P76TAaQTLbEiIJvN3c74Io9HJzev2YEhPB7fYXG1HTpr9VvkAXnnyrnMA0kcIK2Ig0RA + dkbNNfOZCB5hBx0JHtcR93juyp0SwONntrjaRFOn1YmGJ7YqAgoBmX0kNS6KOkgnWzIH + iYBsJnc740uyeIySuEeCxx/8fY8499gqlbXhJDlAFhNA7k+KAQfpA4t0rMwGIyA7o+aa + +UwSPFqBe/SZv3zd5iTEYxs01ZFTpQFZQx1kXvbh1O1RIQGzvSY42Q4zNdJHQDaTvZ3v + JQkebZ0mEPcYFZd6JJsWV9/VIB47orZWvJcF5G8osTbWf/5Q8VICyGXzfDzGjUZAdj7B + Nf+JWDwONrMa7eJB8BiTtJ+4x+KXFR/QPbZCUh06RQjIb7VCQAYH+HkSQJINzHGRTvM5 + 3JlepXjUNzIdRvEYEBK1PfVwdh6ZewQ8wgZzkkeSo3vskPKUvFkGkOUSQK5FQHYmmbX2 + swjwOM7DZ94yHo/lgMdGWLn6uwmLq0rEpILDsoB8/fT+Deog11NAOjKA1MNnsbY2oXX8 + PAaPhkMIHj39AoIRjyoQWVuaUADIy2cz9++IQUDquLTaM3xZPK6N2cG6R8RjW1TV/nMV + ALIw7xSUWBGQ7UloHX8P4FGvjz7BoyPF43rGPd5/+hrdY/s11qZ3ygLyRfFtAOQ+AGQQ + U2IdinOQOq6yVg9fIR7PXr5T/ALx2CZRdeBkKUB+rakEB1mYe+pQauz64MV+nuPRQbY6 + m3X/REV4PJVXSPH4FYurHVBZG96qEJAZFJD+PpNgDhIBqftKa9Un4PA41Go0Ka4GgXvc + l3H28m3EYxvk1OFTmwPkrKnjHW3oHCSWWFuV0jp9kgSPNo7jPf0WB6+PTT10KpfgsbIG + 8dhhobW2AQkgf9FFOuUvHoGDzNiXGBMWBIAcOwoBqdMqa/XghXic5OMfFIZ4bK2EVHqe + EkDuio0MXoyAbHU66/yJQjxOnYV4VKnI2tKYFCA/vSeALMiRB2SP7jqfcvgBlEeAx+Oo + sQweE6l7fMQWV3/h0py2iKoD58oD8l5h7slDPCAtyCpWdJDKc7kzvMLh0QLcI8FjZOwu + dI8dUFUH3soD8mdjPQfI42mJm8KW+k/nHSQCsjPoTtln6N6DPBZg8FArCR7TMnIKbgMe + 33+qb/yJeOyAwNr4Vg6Q//7z68e3r9WVr5/cu5578uCubZGrFoGDdLBhAdm9m7IvE4/r + egQU4PHgydzCe0+wuNpGNangdAaQf37/++tnYx0A8vmjWwU5BJChHCAHkQcFICB1XXXK + xy/E43T/pWGbEtOO5xTcEuCR3dFKBemGTbQQAVlAvgJAXpQCpImhfm893H5OeT7r+Cvd + utOVqyYWNg7gHhetity26+DJi9cJHqvJ3CNuMNeChFT7MgdI2MBcCMgECsiJZA5ykEFf + BKSOi66Z4TN4HETdI+AxdFMCg8fn4B7rqHtEPKpWcs22pgSQKdsiVi2aOdUVHKSJYX8E + ZDMJrdsvwWSHXm99Q4JHV4LHCA6PrxCPzQpHXS/KAfLhrXxwkCwgx4yyMkVA6rbkmh09 + wWNfA4rHiYrwKHlMh7ryD9uVjoAMIN+yDpIB5BRXB2sLE9jAHB1ks1mtsy9SPPYfaGJh + 7eA6ZSbBYwrjHhGP0jLR3F8KAXlsb0J06BJ/74ljRloiIHVWbi0OnMWjqeXIMRSP0Ql7 + j+fk33rIuUfEo+aEyPYkAOR3mIMEQN69fvFEesrWiJULZyIgW0xpXT5BAR7TT5Di6qu3 + WFzVuBKFgmz6Q7afY0qsxEEe2xsfvWbJXCEgcU2ALktP4diFePSeuyQU8HgM8agtIQr1 + +H9k+7lf3+u/ACAf3712gQAynALS3tocHaTCbNb5gzwe7cE9LlwZsTUF8Hjt7mOCx/rv + OPeoHWUKHGRD3aeqsucPb/KAdOccJD76Uef1J/sBYKkcKa5S9wh4XBMdT/B4E92jdnTI + 9Sp0kAJA7iSA9J3iioCUzeNO8jeHR3NrwKPvwpXhBI8XGDx+QTxy8tD8f6UA+bGq7BkL + yI3EQTKAHNAXNw/oJDKUfAwBHt2l8FhW9amu4Se/RYDmM7Jr9ygLyFLiILPSGUDC9nPo + ICU53Il+EeJxMsXjTsSjKP4h4PRISqwNdQSQD27mnzm6J37jmkAApDOZg0RAdiIp0o9C + 8TiAukeKx43xe46dAff4rKzqoxQecQcdTauUEyRXYuUAuSV8xQJfAOQILLF2NjX+JcCj + C+BxRfiWnelZxD2Wvq1G96hpBUr3x+lRHpCrA+dMc3e2s4QNzNFBdipN8nh0dveeE7gG + 8HgU8PgA8SitDa38xQkS9melc5Clj4uuEgfJA9IYVrH2xPsgO40kAY89e/cfaGw+ArYn + 913A4bGIxSNsMMfeZ4VXq1pQJKdHCsha6iBvMA6SBeRwBGSnkSL9IAwejUwt7Zzdp80J + XI141ILslHfJCZIFZAUB5Pms9GQCyBnUQSIgO5Mg5fGYnJ51/iriUblENPoKp0cZQO6O + 30AA6eZsh4DsTHL8i8PjcDtnNx6PsD05FFdrhXOPeLmqUR1KOuMEKQFkCQAyMz15Mw9I + A3SQnUWTDB4NGPc4g7hHBo8lpRWkuIruUaILbf3C6VEIyEunj0gDsg8u0ukcipTF44b4 + 3UdP5yMetaU+Bf1ygpQG5AEA5PL51EGaGRv0wxJrp9CjNB7nLw/fnHwgE9wj4lGBMLR0 + iNMjB8g3zx7cIICM2xASONvLzYk6SARk59Aj3NjRZ4CRKeseQzbE7T5y+hLg8Q26Ry3J + T75bKsgmch8kzEF+rq54WVJ05XwmAHKdAJC9cA5S9xVJ8djPwNiMzD3O4PB4pajkJbpH + eV1o64gMIN8hIHVfeYo/AXGPDB6d3LxmBwrw+A6Lq9qSn3y/8oC8c+Vc5oEkDpBWxEEi + IBXnuA4dJXiEHXQkeFxH3OO5K3cIHj+zxdUmmgzyOYJHNBcBASB/NNR+ZACZfSQ1Lopx + kLZkDhIdpA4pT/FQZfEYJXGPBI8/+Psece5Rc+JT1JMcIIsJIPcnxYCD9PFwsbcyG4yA + VJzjOnRUgkcrcI8+85ev25yEeFQkB60fkwZkDQVkXvbh1O1RIQGzvSY42Q4zNdJHQOqQ + 9hQNVYJHW6cJxD1GxaUeyabF1Xc1iEeti1A4ABaQv6HE2lj/+UPFSwkgl83z8Rg3GgGp + KMF16xiLx8FmVqNdPAgeY5L2E/dY/LLiA7pHoRq0/7sQkN9qhYAMDvDzJIAkG5jjIh3d + UqD0aCke9Y1Mh1E8BoREbU89nJ1H5h4Bj7DBnOSR5OgeRSLIpiYOkOUSQK5FQEqnta7+ + JcDjOA+fect4PJYDHhth5ervJiyual+JzAhkAfn66f0b1EGup4B0ZACph89i1VU50hs7 + +ugbDiF49PQLCEY8ikV7Csch4yAJIC+fzdy/IwYBqbMSFA5cFo9rY3aw7hHxqFAQWj6o + AJCFeaegxIqAFKa1zv4O7lGPwaMjxeN6xj3ef/oa3aOWpae4e1lAvii+DYDcB4AMYkqs + Q3EOUmfVSB4qR5bmkOIqdY8Uj2cv3yl+gXhUrAdtH5UC5NeaSnCQhbmnDhFALvbzHI8O + UnfFCCOXxuPiYILHU3mFFI9fsbiqbfEp6l8hIDMoIP19JsEcJAJSZyXJ4XEog8cgwOO+ + jLOXbyMeFSlBHMeUATIWADlr6nhHGzoHiSVWXRSlBI82juM9/QCPsamHTuUSPFbWIB7F + oT+5UUgA+Ysu0il/8QgcZMa+xJiwIADk2FEISF2UIhmzEI+TfPyDwhCPctkvvgNKALkr + NhIBqatKZMYtxOPUWYhH8WlP4YikAPnpPQFkQU5Gmiwge3TX7ezscqPn8ThqLIPHROoe + H7HF1V+4NEehHrR9UB6Q96DEepAHpAVZxYoOUtcEzeHRAtwjwWNk7C50j9oWW2v65wH5 + s7GeA+TxtMRNYUv9p/MOEgGpW4rs3oPOPQ61kuAxLSOn4Dbg8f2n+safiMfWaEMb53CA + hO3nfnz7Wl35+sm967knD+7aFrlqEZRYHWxYQHbvplsJ2bVHqwCPB0/mFt57gsVVbYis + LX0ygPzz+99fPxvrKCBvFeQQQIZygBxEHhSAgNQlhQvxON1/adimxLTjOQW3BHhkd7Rq + S6bguZqIgCwgXwEgLxJARnCANDHU762H28/pkB67dacrV00sbBzAPS5aFbltF+DxOsFj + NZl7xA3mNKGsdvbBARI2MKeAfP6IAjKBAnIimYMcZNAXAalDcvyLweMg6h4Bj6GbEhg8 + Pgf3WEfdI+KxnWpR/9uUADKFAHLmVFdwkCaG/RGQOqRHmOzQ661vSPDoSvAYQfB4EfD4 + CvGofj11uAc5QD68lQ8OkgXkmFFWpghIHVLjXxSPfQ0oHicqwqPkMR0dTh1sQA0RkAHk + W9ZBMoCc4upgbWECG5ijg9QVTVI89h9oYmHt4DplJsFjCuJRDbpRW5MKAXlsb0J06BJ/ + 74ljRloiIHVFi2ScxD0CHk0tR46heIxO2Hs8J//WQ849Ih7VpiTVNCwA5HeYgwRA3r1+ + 8UR6ytaIlQtnIiB1SYswVgV4TD9B3eNbLK6qRjDqbkUJIOOj1yyZKwQkrgnQAW0K8eg9 + d0ko4PEY4lHdElJp+0JA1n8BQD6+e+0CD0h7a3N0kDqgRDpEHo/24B4XrozYmgJ4vHb3 + 8SvAY/13nHtUqXLU1Jg0IKvKnj+8mZ9zbC8DSHfOQeKjH3VAlLBUTuIeAY9rouMJHm+i + e1STdNTSrBJA7twavnKh72RXBKQOCJEZIodHc2vAo+/CleEEjxcYPH5BPKpFPWpoVADI + hrqPVWXPWEBuJA6SAeSAvrh5gPhlKcCjuxQey6o+1TX85LcIUEMSYZMqi4AsIEuJg8xK + ZwHpgoAUvxLpCIV4nEzxuBPxqDKVaK4hTo+/YRUrA8gHN/PPHN0Tv3FNIADSmcxBIiDF + L0qKxwF07pHicWP8nmNnwD0+K6v6KIVH3EFHc+JqT0+cIGH7ue+kxFr6uIgCckv4igW+ + k13sR2CJVfxqlMw9gnt0ATyuCN+yMz2LuMfSt9XoHtujC229h9OjPCBXB86Z5u5sZwkb + mKODFLkmeTw6u3vPCVwDeDwKeHyAeNSWrtrdLydIASCvEgfJA9IYVrH2xPsgRSxJcI89 + e/cfaGw+guBxAYfHIhaPsMEce58VXq22WyeaeiOnRwrIWlJifXCDcZAsIIcjIEUsRTo0 + Bo9GppZ2zu7T5gSuRjxqSj1q6IcTJAvICnCQV89npScTQM6gDhIBKW5ByuMxOT3r/FXE + oxrUov4mOT3KAHJ3/AYCSDdnOwSkuOVI91/tO8DIdLidsxuPR9ieHIqrtcK5R7xcVb+c + Ot4DJ0gJIEsAkJnpyZt5QBqggxSvJhk8GjDucQZxjwweS0orSHEV3WPHJaLRFjg9CgF5 + 6fQRaUD2wUU6YlUk5x45PG6I3330dD7iUaMiUmVnnCClAXkAALl8PnWQZsYG/bDEKlI9 + SuNx/vLwzckHMsE9Ih5VqRFNtsXpkQPkGyixEkDGbQgJnO3l5kQdJAJSrHqEGzv68O4x + ZEPc7iOnLwEe36B71KSKVNgXFWRT0x8KyM/VFS9Liq6czwRArhMAshfOQYpRkRSP/QyM + zcjc4wwOj1eKSl6ie1ShRDTalAwg3yEgxag8xWMi7pHBo5Ob1+xAAR7fYXFVoypSYWfy + gLxz5VzmgSQOkFbEQSIgFStCq0cJHnsJ8LiOuMdzV+4QPH5mi6tN9OtVYbpgU2qOgACQ + PxpqPzKAzD6SGhfFOEhbMgeJDlKrylPcuSweoyTukeDxB3/fI849qllDKm1eDpDFBJD7 + k2LAQfrAIh0rs8EISMWK0OpRCR6twD36zF++bnMS4lGlytBOY9KArKGAzMs+nLo9KiRg + ttcEJ9thpkb6CEitak9R5xI82jpNIO4xKi71SDYtrr6rQTxqR0sq6ZUF5G8osTbWf/5Q + 8VICyGXzfDzGjUZAKpKDto+xeBxsZjXaxYPgMSZpP3GPxS8rPqB7VIkwtNSIEJDfaoWA + DA7w8ySAJBuY4yIdbStQun+KR30j02EUjwEhUdtTD2fnkblHwCNsMCd5JDm6Ry3pqt3d + ygCynAXkjpi1CEhpEYjnLwEex3n4zFvG47Ec8NgIK1d/N2Fxtd2S0OobZQH5+un9G9RB + rqeAdGQAqYfPYhWPHOmNHX30DYcQPHr6BQQjHrUqIdV2rgCQl89m7kdAikiA0kORxePa + mB2se0Q8qlYb2mhNASAL805BiRUBKS0D0fwF7lGPwaMjxeN6xj3ef/oa3aM2FKTiPmUB + +aL4NgByHwAyiCmxDsU5SNFoEQaiEI9nL98pfoF4VLE0tNKcFCC/1lSCgwRAHiKAXOzn + OR4dpJjESPSoAI+n8gopHr9icVUrGlJppwoBmUEB6e8zCeYgEZAikiSHx6FWo0lxNQjc + 476Ms5dvIx5VKgotNqYIkLkAyFgA5Kyp4x1t6BwklljFIUoJHm0cx3v6LQ5eH5t66FQu + wWNlDeJRizJSXdcSQP6ii3TKXzwCB5mxLzEmLAgAOXYUAlIcUiSjEOJxko9/UBjiUXVC + EElLSgEZiYAUjxKZkQjxOHUW4lEkElLtMKQA+ek9AWRBjjwge3QXW3Z2ufHweBw1lsFj + InWPj9ji6i9cmqNaaWilNXlA3isEB7krlgOkBVnFig5S+/Ln8GgB7pHgMTJ2F7pHrWhG + rZ3ygPzZWM8B8nha4qawpf7TeQeJgNS2Irv3II8FGDzUSoLHtIycgtuAx/ef6ht/Ih7V + KhONNc4B8t9/fv349rW68vWTe9dzTx7ctS1y1SIosTrYsIDs3k3bCdm1+1eAx4Mncwvv + PcHiqsa0opGOGED++f3vr5+NdRSQtwpyCCBDOUAOIg8KQEBq998DIR6n+y8N25SYdjyn + 4JYAj+yOVhpJGuxEbRGQBeQrAORFAsgIDpAmhvq99XD7Oa3qsVt3unLVxMLGAdzjolWR + 23YdPHnxOsFjNZl7xA3m1CYQTTfMARI2MKeAfP6IAjKBAnIimYMcZNAXAalVOf7F4HEQ + dY+Ax9BNCQwen4N7rKPuEfGoaeGoqT8lgEwhgJw51RUcpIlhfwSkVvUIkx16vfUNCR5d + p85ctCqCw+MrxKOaZKG9ZuUA+fBWPjhIFpBjRlmZIiC1qsa/KB77GlA8TlSER8ljOrSX + RdizqiIgA8i3rINkADnF1cHawgQ2MEcHqT1NUjz2H2hiYe3gOoXiMYVxj4hHVYlATO0o + BOSxvQnRoUv8vSeOGWmJgNSeFknPxD0CHk0tR46heIxO2Hs8J//WQ849Ih7FJKcOj0UA + yO8wBwmAvHv94on0lK0RKxfOREBqV4vQuwweV0ZsS0k/QYqrr95icbXD2S/CBpQAMj56 + zZK5QkDimgCtaFOIR++5S0IBj8cQjyLUkaqGJARk/RcA5OO71y7wgLS3NkcHqRUl0k55 + PNqDe1y4MmIrweO1u48JHuu/49yjqmQgnnakAVlV9vzhzfycY3sZQLpzDhIf/agVUcJS + OYl7BDyuiY4neLyJ7lE8+lH1SJQAcufW8JULfSe7IiC1IkSmUw6P5taAR9+FK8O37kw/ + cYHB4xfEo6qlII72BIBsqPtYVfaMBeRG4iAZQA7oi5sHaEOWAjy6S+GxrOpTXcNPfosA + cWQSjkIVEZAFZClxkFnpLCBdEJDaUCLtU4jHyYhHVSS7DrTB6fE3rGJlAPngZv6Zo3vi + N64JBEA6kzlIBKQ2REnxOIDOPVI8bozfc+wMuMdnZVUfpfCIO+jogM5aPUROkLD93HdS + Yi19XEQBuSV8xQJf2J91BJZYtaFGydwjuEcXwOOK8C0707OIeyx9W43usdXprXMncnqU + B+TqwDnT3J3tLGEDc3SQGtckj0dnd+85gasBj0cBjw8QjzonsbYNmBOkAJBXiYPkAWkM + q1h74n2QGpUkuMeevfsPNDYfQfC4gMNjEYtH2GCOvc8Kr1bblu6iP5vTIwVkLSmxPrjB + OEgWkMMRkBqVIu2MwaORqaWds/s0xKPoVaTCAXKCZAFZAQ7y6vms9GQCyBnUQRobICA1 + K0mFeDx/FfGowrwXa1OcHmUAuTt+AwGkm7MdAlKzYoTeODwOt3N24/EI25NDcbVWOPeI + l6tilVX7x8UJUgLIEgBkZnryZgSkxoXIdMjg0YBxjzOIe0xOzwI8lpRWkOIqusf2J7sO + vJPToxCQl04fkQZkH1ykozlxyuJxQ/zuo6fzEY86ICZVDJETpDQgDwAgl8+nDtLM2KAf + llg1pkdpPM5fEb45+UAm4lEVqa4TbXB65AD5BkqsBJBxG0ICZ3u5OVEHiYDUnB7hxo4+ + A4xMOfe4IW73kdOXAI9v0D3qhKA6OkgqyKamPxSQn6srXpYUXTmfCYBcJwBkL5yD1Iwi + KR77GRibkbnHGfOXS/D4Et1jRzNdN94vAOSPhtqP7xCQmlGe4l6Ie2Tw6OTmNTswhMfj + Oyyu6oagOjpKeUDeuXIu80ASB0gr4iARkIr1o+KjBI+wg44Ej+uIezx35U4J4PEzW1xt + ol9YR791fL9YI6AQkNlHUuOiGAdpS+Yg0UGqWHmKm5PFY5TEPRI8/uDve8S5R7HKqePj + kgNkMQHk/qQYcJA+Hi72VmaDEZCK9aPioxI8WoF79Jm/fN3mJMRjxxNcx1qQBmQNdZB5 + 2YdTt0eFBMz2muBkO8zUSB8BqWLtKWpOgkdbpwnEPUbFpR7JpsXVdzWIRx2TVfuHywLy + N5RYG+s/f6h4KQHksnk+HuNGIyAViUf1x1g8DjazGu3iQfAYk7SfuMfilxUf0D22P791 + 7Z1CQH6rFQIyOMDPkwCSbGCOi3RUr0DpFike9Y1Mh1E8BoREbU89nJ1H5h4Bj7DBnOSR + 5OgedU1ibRuvDCDLJYBci4CUlow6/xLgcZyHz7xlPB7LAY+NsHL1dxMWV9uW2bp5tiwg + Xz+9f4M6yPUUkI4MIPXwWazqlCO9saOPvuEQgkdPv4BgxKNuqkkFo1YAyMtnM/fviEFA + qlWCwsZl8bg2ZgfrHhGPKkhxnWpCASAL805BiRUBKZSMWn8H96jH4NGR4nE94x7vP32N + 7lGnxKSKwcoC8kXxbQDkPgBkEFNiHYpzkGpVI3moHFmaQ4qr1D1SPJ69fKf4BeJRFRmu + W21IAfJrTSU4yMLcU4dSY9cHL/bzHI8OUr1ihNYV4fFUXiHF41csruqWnDo+WoWAzKCA + 9PeZBHOQCEi1SpLD41AGj0GAx30ZZy/fRjx2PLd1sYXmADlr6nhHGzoHiSVWdYlSgkcb + x/GefouD18emHjqVS/BYWYN41EVFdXDMEkD+oot0yl88AgeZsS8xJiwIADl2FAJSXVIk + 7QrxOMnHPygM8djBfNb1tysB5K7YyODFCEh1apHqkSuuAh6nzkI86rqaVDB+KUB+ek8A + WZCTkSYLyB7d1Z2bXbB9Ho+jxjJ4TKTu8RFbXP2FS3NUkOG61YQ8IO8V5p48yAPSgqxi + RQepjn8uOPdoweIxMnYXukfdko/qR8sD8mdjPQfI42mJm8KW+k/nHSQCUvWK7N6Dzj0O + tZLgMS0jp+A24PH9p/rGn4hH1We7+FvkAPnvP79+fPtaXfn6yb3rBJDbIlctAgfpYMMC + sns31Sdk125RAR4PnswtvPcEi6vil436RsgA8s/vf3/9bKwDQD5/dKsghwAylAPkIPKg + AASkqv/1EOJxuv/SsE2JacdzCm4J8MjuaKW+7x5bFl8EZAH5CgB5UQqQJob6vfVw+zkV + 67Fbd7py1cTCxgGKq4tWRW7bdfDkxesEj9Vk7hE3mBOfVjQyIg6QsIG5EJAJFJATyRzk + IIO+CEgVy/EvBo+DqHsEPIZuSmDw+BzcYx11j4hHjeS/2DpRAsiUbRGrFs2c6goO0sSw + PwJSxXqEyQ693vqGBI+uBI8RHB5fIR7FphANj0cOkA9v5YODZAE5ZpSVKQJSxWr8i+Kx + rwHF40RFeJQ8pkPDyYDdaT0CMoB8yzpIBpBTXB2sLUxgA3N0kKrUJMVj/4EmFtYOrlNm + EjymMO4R8ah1PWh9AAoBeWxvQnToEn/viWNGWiIgValF0hZxj4BHU8uRYygeoxP2Hs/J + v/WQc4+IR62rQnsDEADyO8xBAiDvXr94Ij1la8TKhTMRkKrWIrSnAI/pJ0hx9dVbLK5q + Twhi6VkJIOOj1yyZKwQkrglQkTaFePT2XxIKeDyGeBSLHLQ+DiEg678AIB/fvXaBADKc + AtLe2hwdpIqUSJvh8WgP7nHhyoitKQSPdxk8fse5R60rQssDEACyoe5TVdnzhzfzc47t + ZQDpzjlIfPSjikQJS+Uk7tF77pI10fGIRy0rQFzdKwHkTgJI3ymuCEgVCZFphsOjuTXg + 0XfhynCCxwvX7j4G9/ilHvEoLm1oZTRSgPxYVfaMBeRG4iAZQA7oi5sHqEaWAjy683i8 + +fB5WdWnuoaf/BYBWkkF7FQEEZAFZClxkFnpDCAnuyAgVaNE2ooQj5MpHnciHkWgATEN + gdPjb1jF2lBHAPngZv6Zo3viN64JBEA6kzlIBKRqREnxOIDOPVI8bozfc+xM/s2Hz8qq + PkrhEXfQEZNENDsWTpBwH+R3UmLlALklfMUCXwDkCCyxqkaNkrlHcI8ugMcV4Vt2pmcR + 91iK7lGzOS/m3jg9ygNydeCcae7OdpawgTk6SBVoksejs7v3nMA1gMejgMcHiEcx60Pj + Y+MECfuzsoAsukocJA9IY1jF2hPvg+ygJME99uzdf6Cx+QiCxwUcHotYPMIGc+x9Vni1 + qnENiKlDTo8UkLXUQd5gHCQLyOEIyA5Kkb6dwaORqaWds/u0OYGrEY9iUoGIxsIJkgVk + Renjoqvns9KTCSBnUAeJgOy4IOXxmAzu8SriUURKEMdQOD3KAHJ3/AYCSDdnOwRkx+VI + 91/tO8DIdLidsxuPR9ieHIqrtcK5R7xcFYcstDcKTpASQJYAIDPTkzfzgDRAB9kxTTJ4 + NGDc4wziHgGP568WlZRWkKU56B61l/6i65nToxCQl04fkQZkH1yk0xFFcu6Rw+OG+N1H + T+cjHkUnBjEMiBOkNCAPACCXz6cO0szYoB+WWDugR2k8zl8Rvjn5QCbiUQzJL8IxcHrk + APnm2YMbBJBxG0ICZ3u5OVEHiYDsiB7hxo4+Ave4IW73kdOXAI9v0D2KUBDaHhIVZFPT + HwrIz9UVL0uKrpzPBECuEwCyF85BtleRFI/9DIzNyNzjjPnLJXh8ie5R27kvxv5lAPkO + Adle5Sl+H3GPDB6d3LxmB4bweHyHxVUxCkLbY5IH5J0r5zIPJHGAtCIOEgGpWG0tHiV4 + 7CXA4zriHs9duVMCePzMFleb6Feg7TzA/sURAQEgfzTUfmQAmX0kNS6KcZC2ZA4SHWSL + ylN8giweoyTukeDxB3/fI849ikMO2h+FHCCLCSD3J8WAg/TxcLG3MhuMgFSsthaPSvBo + Be7RZ/7ydZuTEI/aT3lRj0AakDUUkHnZh1O3R4UEzPaa4GQ7zNRIHwHZovYUnSDBo63T + BOIeo+JSj2TT4uq7GsSjqGWhvcGxgPwNJdbG+s8fKl5KALlsno/HuNEISEVSa80xFo+D + zaxGu3gQPMYk7SfusfhlxQd0j9rLeHH3LATkt1ohIIMD/DwJIMkG5rhIpzUKlD6H4lHf + yHQYxWNASNT21MPZeWTuEfAIG8xJHkmO7lHcEtHs6GQAWS4B5FoEpLTA2vaXAI/jPHzm + LePxWA54bISVq7+bsLiq2VzXhd5kAfn66f0b1EGup4B0ZACph89ibZsc6Y0dffQNhxA8 + evoFBCMedUENIhijAkBePpu5f0cMArKNEhSeLovHtTE7WPeIeBRB0ot4CAoAWZh3Ckqs + CEihwNr4O7hHPQaPjhSP6xn3eP/pa3SPIhaDGIYmC8gXxbcBkPsAkEFMiXUozkG2UY3k + oXJkaQ4prlL3SPF49vKd4heIRzHkvJjHIAXIrzWV4CALc08dIoBc7Oc5Hh1kW8UI50vj + cXEwweOpvEKKx69YXBWzHLQ/NoWAzKCA9PeZBHOQCMg2SpLD41AGj0GAx30ZZy/fRjxq + P9vFPwJlgIwFQM6aOt7Rhs5BYom19aKU4NHGcbynH+AxNvXQqVyCx8oaxKP4FaHlEUoA + +Ysu0il/8QgcZMa+xJiwIADk2FEIyNZLkZwpxOMkH/+gMMSjljNct7pXAshdsZEIyLYp + kTlbiMepsxCPuqUGEYxWCpCf3hNAFuRkpMkCskf39mRnl3sPj8dRYxk8JlL3+Igtrv7C + pTkiyHkxD0EekPegxHqQB6QFWcWKDrJ1/7RweLQA90jwGBm7C92jmNNffGPjAfmzsZ4D + 5PG0xE1hS/2n8w4SAdkaRXbvQeceh1pJ8JiWkVNwG/D4/lN940/Eo/jyX2wj4gAJ28/9 + +Pa1uvL1k3vXc08e3LUtctUiKLE62LCA7N6tNQnZtc9RgMeDJ3ML7z3B4qrY0l6842EA + +ef3v79+NtZRQN4qyCGADOUAOYg8KAAB2fK/NUI8TvdfGrYpMe14TsEtAR7ZHa3Emw04 + Mm1HQBaQrwCQ/7+9c3+oaev6+ME5HLdEKqko5VYpROVSoVCEKNcuRBclhVRUlFshVC7J + raTcKUohSipKN1QKx3OO4/1H3jHnWmvvtXflnEN7t9daY/3wPJ7zPNaec6wxns/8jjnm + HFcIIIM5QOqP0Bo4ANvP/WM89ulLK1f1jc0tQT2u9A3Ztf/omSs3CR4byN4jNpjrbV8X + xO9zgIQG5hSQzx5RQMZSQDqQPciR2oMRkP8Yjr8weBxJ1SPgMWB7LIPHZ6AeW6l6RDwK + IiJ6d5DdAHIfAeQiR1tQkPojhiIg/zEeYbNjwECtEQSPto6LVvoGc3h8gXjsXQ8X2K93 + AmTx3VxQkCwgbSaZGiAg/zEaf6F4HKxN8ejQFR5l13QIzD1wuGq2gBIgX7EKkgHkXFtL + M2N9aGCOCvL7MUnxOHS4vrGZpe1cisd9jHpEPKrZnwX/c10C8uTB2PCA1R7zHWwmmiAg + vx+L5L8l6hHwaGAy0YbiMTz24Kms3LvFnHpEPAo+TtQ1AR4gP8IeJACy8OaV0yn7dgb7 + eC5CQP5zLML/ogs8ppwmydUXrzC5qi5HFsvvdAPImPBNq5fyAYk1Ad3GJh+P8z1WBwAe + TyIexRIgap4HH5Bt7wCQjwtvXJYD0sLMCBVkt5FI/ws5Hi1APXr6BO/cB3i8UUjx2PYR + 9x7V7NEC/zlFQNZVPSu+k5t18qASIPHqx26DEkrlZOpx/tLVm8JjCB7voHoUeGD00vC7 + AWTCziAfz4VzbRGQ3QYi819weDQyAzwu9PQJIni8fKPwMajHd4jHXvJqAf8sD5DtrU11 + VWUsILcRBWlPU6zDBmPzgO7CkodHewU8VtU1t7Z/lrcIELCL4NDVaAFlQFYQBZmRwgBy + znQEZHeRSP85H49zKB4TEI9q9F7x/RQXj1+hipUBZNGd3PMnDsRs2+QNgLQme5AIyO6C + kuJxGN17pHjcFnPg5HlQj2VVdU0KeMQOOuILHdXMiAtIOAf5kaRYKx4XUEDuCNqwYiEA + chymWLuLRtneI6jH6YDHDUE7ElIyiHqsQPWoGm8V/1u5eOwMyI3eS+bZW08wgQbmqCC7 + jEk5Hq3t5y/x3gh4PAF4LEI8ij9uVDZDLiChPysHyOtEQcoBqQdVrP3xHGSnkAT12H/g + 0OF6RuMIHldweCxg8QgN5thzVrhaVZn3iu/FXDxSQLaQFGvRbUZBsoAci4DsFIr0HzB4 + 1DEwmWBtPw/xKL7Q6J0ZcQHJArIGFOT1Sxkp8QSQrlRBIiC7Csgu8XjpOuKxd9xYNL/K + xaMSIJNiwggg7awnICC7Ckfaf3XwMB2DsROs7eR4hPbkkFxt4e894nJVNLGilolwASkD + ZCkAMj0lPlIOSG1UkMoxyeBRm1GPrkQ9xqdkAB5LK2pIaQ6qR7X4rhh/hItHPiCvZqYq + AnIQFukoRiSnHjk8hsUkncjMRTyKMULUPCcuIBUBmQyAXL+cKkhDPQSkYjTSDjoDh3J4 + XL4hKDI+OR3xqGbPFefPcfHIAfIlpFgJIKPD/L3dne2mUAWJgFSISILHQTz1GBadlJp5 + FfEozghR86xoQH779jcF5NuGmvLSgvxL6QDIQDkg4W5k3IOUhSRVj0O09QzJ3qPr8vUy + PJajelSz84rw55QAWYuAlAVeN3+Q43GKnbO7t78Mjy9rMbkqwghR85Q6A/J+/sX05DgO + kKagIBGQ8tgkePydh8dAoh4v5t8vBTy+ZZOr36hR1fwh8edEYQEeID+1tzQxgDyXmhgd + yijI8WQPEhWkLCCV8RjKqUeKx0/yc4+49yiK+FD7JDoBsoQA8khcBChIFyjSMTXURUDK + w5HDoymoR5fl6wMj4xCPavdZMf+gIiAbKSBzzh0ngPRyd545ZfwYBKQsHGlpDk2ujp8y + k6jH0OjE1HMkufqytrGlHfEo5khR09xYQH6FFGtH29v6mnIZINctc5k9bTICkheODB51 + DQGPswkeI+KOEPVYUl5Tj+pRTQ4r8p/hA/JDixyQu0P9vNycCCBH6WihgqRBSdWjlo7B + GIpHL//Q3YnHz+UgHkUeIuqdnhIgq2WA3IKAlKOR/IlNrgIeJ0+b7bJsnRyP1YDHDqhc + /foNk6vq9V7x/ZoyICvLHt4mCnL3VhkgR2gNGoB3sTLqUWvEKIJHJzcvPwU8Qv9VWccO + TK6KL0zUN6MuAJl3If3I3ggEpAIflfG4JWIvqx4Rj+rzVvH/UidAPn14K+esDJBWoCAR + kGS52o+2Jwc8WlE8bmXU48OnlZBcRTyKP1DUNUNlQD4vuQeAPAyAXMukWEfjHmRn9Ujx + eCEPkquIR3V5qjR+RwGQ7xtrKykgjxEFucrNaQYCki5bu8Lj2ZxbFI/vEY/SCBX1zLJL + QKZRQHq4zII9SASkLLk6mkmurgU8Hk67kHev5DniUT1eKp1fUQLkawLI7LPHEqMAkIsB + kOaoIOXq0dxqhpPbKr+tUYnHzmYTPL5uRDxKJ1bUMtNOgHwECjLt8J6IzWsBkFMnISC5 + 5CrB4ywXj7WbEY9q8Uxp/ki3gAwBQDoiIHnJVcCj42LEozTjRG2zlgHyC1SxNtdXP390 + 71pWZ0D266uwIyed/yDH46SpDB73UPX4iFWPX7A0R22+KoUf6gzIB0RB7o/iAGks7T1I + WXKVxWNI1H5Uj1IIjN6aoxyQnwGQbxhAnjq0Z/vmNR4L5ApSqoDs249cC6A72lSGx0Np + WdfuAR7fNLd1fEY89pbfivV3OUBC+7lPH943vK588uBm9pmjAEhfoiAtzVlA9u0jnUWq + fKYcHo3leDx6JvvWgyeYXBVrQPT2vBhA/v31ry+fO1opIO9eyyKADOAAOZJcFCBNQPLx + uMBjzebtew6dUsQj29Gqt78i/r5YLKAMyBcAyCsAyF0hvisZQOqP0Bo4QJJXP/bpSytX + 9Y3NLSG5utI3ZNd+wONNgscGsveIDebEEgUaNA8OkNDAnALy2SMKyFg+IAdLE5AMHkdS + 9Qh4DGDwePfRM1CPrVQ9Ih41yJPFMZRuALlvV7DvykWOtqAgpQpI2OwYMFBrBMGjLcFj + MMHjFcDjC8SjOHxfI2fRCZDFPEA62EwyNRipLUlAEjwO1qZ4dKB4jCXqUY5H2Tlkjfys + OCiBWkAJkK9YBckAcq6tpZmxPjQwl56CpHgcCng0s7Sdu4jgcR/iUaA+LqhhdwHI3KyT + B2PDA1Z7zHewmWgiTUCyeDQwmWjD4vHgqazcu8WcekQ8CsrLhTNYHiA/wh4kALLw5pXT + Kft2Bvt4LpIsIBk8DufjMeU0VY+vMLkqHOcW4ki7BeSm1Uv5gJRUTQAfj/M9VgeExx48 + iXgUonsLbsx8QLa9A0A+LrxxWQ5ICykqSDkeLUA9evoE79xH8Fj45AXB40fcexSclwto + wIqArKt6VnyHKMiYcAVASurqxz5McpWqx/lLV28Kj0E8CsijhT3UbgCZsDPIx3PhXFsL + MyOppVg5PBqZAR4XevoEETxevlH4GPD4rg3xKGx/1/jR8wDZ3tpUV1XGAnIbAaQ9TbEO + G/y7hAAJleRk75Hg0V6OxzvFz6rqmlvbP8vvQNb4T4sDFKAFlAFZwShIBpDQfk5qgOTj + cQ7FYwLiUYB+LdQhc/H4FapYGUAW3ck9f+JAjEQBSfE4TI7HbTEHILl6p7isqq5JAY/Y + IkCoLq/Z4+YCEs5BfiQpVgrIjJSEHUEbViyUHCB5eJwOeNwQtDMhJYOoxwpUj5rtx2IZ + HRePnQG50XvJPHtrUqQjHQUpx6O1/fwl3psAjyfO594pQjyKxd81fh5cQEJ/VhaQBdcv + 8wA5zkgPqlj7S+IcJOCx/8Chw/WMxkF78oUrNgTtoHgsYPEIDebYc1a4WtV4vxbqALl4 + pIBsISnWotuMgmQAOcEEGphLJMXK4FHHwGSCtf28Jd4bEY9C9WoBj5sLSBaQNRWPC65f + ykiJ5xSkdADZNR6vIx4F7N3CGzoXj50AGUYAaWc9YaxUAMnhcewEazs5HqE9OSRXW/h7 + j7hcFZ6bC2fEXEDKAFkKgExnAOkKKVYApLYUFCSDR21GPboS9RifknGJ4LGGlOagehSO + Swt6pFw8KgAyMzUphg/IQeIv0lHGY1hM0onMXMSjoJ1biIPnAlIRkMnxkUHrl1NAGkoB + kJ3wGBmfnA54LEU8CtGpBTxmLh45QL6EFOtVAGR0mL/3EmdWQYoekASPg4bpGHDqEfCY + mnkV8Shgxxbq0GlAfvv2NwfI8tKC/EvpioAcIvI9SIrHIdp6hmTv0XX5+iDEo1DdWfDj + VgJkLR+Q7s52U2iKVeSAlONxip2zu7d/WDSLx5eYXBW8gwttAgqAfNtQU156P/9ienJc + ZCCjIE1BQULzABEX6RA8QgcdGR4DCR4v5heUltc0vGWTq9+omYT2bXG8wrMAD5Cf2lua + OEAmRof6exNAjid7kKIGZPd4rIW9x0/yc4+49yg89xbeiDsBsoQBZAQA0gX2IMUOSBke + TUE9uixfHxgZR/B4H/EoPF8Ww4gVAdnIAPLccQJIL3fnmVPGjxE3IGV4HD9lJlGPodGJ + qedIcvUl4lEM/i24ObCA/EpTrG/ra8opII/ERQSuW+Yye9pkU0NdEStIFo+6hoDH2QSP + EXFHCB5LymvqUT0KzplFMGA+ID+0MIDMAUDuDvVjATlKR0u0CpLiUUvHYAzFo5d/6O7E + 4+dyKB4bUT2KwL2FNwUeIDva3tZXywC5RfyAlONx8rTZLsvWKeKxAypXv37D5KrwfFrI + I+4MyIe3KSC3+nm5OREFOWqEWAHJ4HHEKIJHJzcvPwU8Qv9VWccOTK4K2cWFNfYuAJl3 + Mf3I3gjRA1IZj1si9rLqsRrUI+JRWH4sltEqA7LyqQIgrRhADhDhXayAxwGDtAgerSge + tzLq8eHTytrGFsSjWBxcaPNQBuTzknt5F/iAHC3OFGuXeLyQB8lVxKPQfFhM41UA5PvG + WgDkrZyzkGLd6rcKFKRoAdkVHs/m3EI8ism5hTiXLgGZdhgU5FoPugcpSkByeBxtSpOr + a0E9Hk6/kHev5DniUYheLJ4xKwHyNQFk9tljiVEAyMVOM6zMaYpVbAqSh8cZTm6r/LZG + JR47m03w+LrxPapH8bi38GbSCZCPQEESQG4GQM6aNslUhIDk43GWi8fazYDHNMSj8JxX + hCPuFpAhAEhHcQJShkdzqxmOixGPInRr4U5JBsgvf3S0NddXPwdAZqUd3sMAcqockL+I + 5ZHjcdJUBo97KB4fserxC5bmCNedBT/ybgC5P4oDpDEp0hGTglTGY0jUflSPgvdj0UxA + DsjPAMg3BJDXstIO7dkOCnLBLBkg+/UVCx/7QnvyIdq6o01leDyUlnXtHuDxTTOU5iAe + RePagpyIDJD/+/Lpw/vG15VPHtzMPnO0EyD79hFHQHJ4NGbVI+Dx6JnsWw8wuSpI9xXf + oBlA/v31ry+fO1opIO9eyzpFALmGBeRIcg5SLIDk43GBx5rN2/ccOiXH42fEo/g8XFgz + 4gAJ/VkBkA0UkFcAkLtCfFdCitXS3FifKEiR3GzVpy9TuWpsbgnJ1ZW+IbsIHm8+eFL5 + uoHsPWKDOWF5rwhHywESGphTQD57RAEZuz1AhIBk8DiSqkfAYwCDx7tUPbZ2EDyyDR9F + +KFxSoKwgDIgX4CCBEDu2xXsu3KRoy0DyIHiACRsdgwYqDVCXwGPVwCPLxCPgnBWKQyy + EyCLeYB0sJlkajBSe7A4FCTB42BtikcHisdYoh7vPnoGyVWCR9k5ZCl8d5yjZlrgu4Cc + a2tpZqwPDczFAEiKx6EUj7aOi1b6Bu/ad/QM4lEz3VK6o+oCkLlZJw/GhoOCnO9gM9FE + LIBk8WhgOslGjsfcu8WIR+k6vwbOXAmQr6iCPJ2yb2ewz8pFIgIkg8fh+sZmlrZzFfD4 + CpOrGuiXkh1S94BcvZQPSIHXBHB4NJlo4zDfY3VAeOzBk1mIR8m6vcZOnAfIj23vG169 + eFx44woDSE8ApIU4FKQiHj19gnfuSzl95WbhkxcEjx9x71Fj/VNyA1MEZF31s+I7REHG + hG/iA1LgN1v1YZKrBhSPS1dvCo9BPErO04UxYQVAvmMAefl0SsLOIB/PhQSQRsJPsXJ4 + NDKzAPXI4vHyjcLHgMd3bYhHYXiqREbJA2R7a1NdVRkfkPZcilXQgIRKcrL3qITHO8XP + qupw71Eibi6YaSoDsgIUpAyQ0H5O+IBUwONCT5+gnQkppxGPgvFQaQ2Ui8evUMUqA+T5 + EwdithEFyQBy2ODfBQxIisdhFI/280E9bqPq8U5xWVVdU2s7luZIy981frZcQMIxj49t + oCApIDNSEnYEbfBcKAJA8vA4fQ6LxwyCxwpUjxrvnBIcIBePckAW3cmlgNzovWSevTUp + 0hEyIOV4tAY8egMeD5w4n3unqBMesYOOBL1fA6fMBSTtz0oBWXD9MgPIFQSQ44z0oIq1 + v0DPQcrwOA7aky9csSFoR0IK4lED3RCHxFqAi0ceIG/zATnBBBqYC1ZByvA4wdp+3hLv + jYhHdHwNtwAXkHxAXspIiQcFKXhAAh77Dxw6XM9IAY/XC1j1CP1X2WPIuFrVcC+VzvC4 + eKSAbCF7kEU8QNpZTxgrXEAyeNQxGKuAR2hPDsnVFn5yFeNROg6v6TPlApIFZE1FacF1 + DpCujILUFqaCVMCjK1GP8SkZlwgea0hpDuJR011TkuPj4lEBkJknkmLCSIpVyIDk4dGO + qMewmCRIriIeJenmwpk0F5AKgExPjo8MWr9cyIBk8KjNqEcWj+mAx1LEo3CcU4Ij5eKR + A+RLUJBXM1OTosP85YAcJLwinS7wmJqJeJSghwtsylxAcoAsLy3Iv8QHpKGe9hDB7UFy + eDQkyVXX5euDIuOTEY8Cc01JDpeLRxaQtXxAujvbTaEpVsEBkuBx0DCaXLVzdvf2D4tO + Ss28CurxJSZXJenmwpk0Dchv3/6mgHzbUFNeej//YnpyXGQgqyAJIH8XVpEOxeMQbT1F + POYXlJbXNLxlk6vf6MSF851wpNKwAA+Qn9pbmuSADPX3JoAcT/YgBQZIOR6nKOGxFvce + peHWwp1ld4CMAEC6wB6kqeAASfAIDeb0DE0Z9RgYGZecfjH/PuJRuF4qnZErArKRAeS5 + 1MRoCsiZAgSkDI/jGTyGRiey6pHg8ZP8SnIszZGOmwtnpoqArK8pLyEK8kgcBeRsAkhd + QSlIBTy6LF8fGMHgsQTVo3CcUsIj7QqQOeeOJ+4O9fdydwZAjjHQ0RKQguThcaazu5c/ + 4PH4OZpcrW1EPErY0YUydRaQXyHF2tH2tr5aDsh1y1xmT5ssLECyeNQ1NJ08bbbLsnWA + xyNEPQIe65nk6tdvmFwVim9KcZx8QH5oYRQkA0g/LzcnAshR0J9VKEU6FI9aOqPGjJ9C + 8OgXuhvwmEP2HgGP0H9V1tAK1aMUnV0Ic+4GkHsjtggPkMp43CLDYzXgsQMOdiAeheCT + Uh6jMiArnz68TQG5lQLSigHkAEFcNcfgcQTFo5Obl99WBo8PyyoRj1L2cUHNXRmQz0vu + 511IPyJAQHbG415Qj3mgHhGPgnJJSQ9WAZDvG2sBkLdyzkKKVXCABDwOGKRF8Gg1k4/H + p4hHSXu4wCbfGZD38i6kHQZArmVSrKOFsQfZBR4Pp1/Iu1fyHPEoMJeU9HCVAPmaADL7 + 7LHEqK1+q9ycZliZ0xSr5itIRTyuIurx2NmcWw8JHt9jclXSPi6oyXcLyM1rPVxmwR6k + IADJ4XE03Xv0WLslYu/hNMSjoDwRB0ss8D1ALnYUCiBleDS3muHkBniMAjxmEzy+Rjyi + owvJAjJAfmGKdJ4/ogpyTwQF5NRJQgAkH4+zXDzWbmbx+AjVo5BcEccKFugGkPujQvxW + CQWQfDw6Ll7lF4J4RN8WqgXkgPzc0db8phoAeS0r7ZAyIPv1/UVjH+bco+5o00lTGTzu + OZyWde0ewWMzlOZ8wdIcoTqnBMfdCZBPHtzKPnNUDkhjUsWqySlWDo/GoB4ZPO5H9ShB + TxbJlBlA/v31ry88QJ46tGf75jUeC2bJFKTmArJvP3ItgByP2/ccYvH4BvD4GfEoEkeV + yDQ4QEJ/1k8f3je8rnzy4CYB5K4Q35WgIC3NWUD27aOZC9Y+fZnSHBaPviFR+4+eyb75 + 4AmbXMUOOhJxZLFMkw/IVlCQzx7dvZZFABnAAXIkuShAUwHJx+MCjzWbAY+nsq7dBfXI + 4ZHtaCWW74XzELcFlAH5AgB5hQAymADSFgCpP0Jr4AAN7c8Kmx2kclXf2NwS1ONK35Bd + gMcrFI8NpDQH8Shu7xXh7DhA/gkKkgfIWApIB6IgR2oP1lRAMngcSZOrgMeA7bEMHp8B + HltBPcrOIYvww+GURGmBbgC5jwBy0VwGkEM1FJAEjwMZPNo6LlrpG8zh8cVrxKMovVUC + k+oEyOK7uVmnDrKAtJlkYqCxgCR4HKxN8eiAeJSAr0phikqAfMUoyBQZIM2M9YdrJiAp + HocO1zc2s7SdS/G4j1GPiEcpOK5Y59glIE8ejA0PWO0x38FmosYCksWjgclEG4rH8NiD + p7Jy7xajehSrq0piXjxAfoQ9SABk4c0rp1P27Qz28SQKUlMB2QUeU06T5OqLV6geJeG5 + Ip1kN4CMCd+0eikfkBpWE8DH4/ylqwMAjycRjyL1USlNiw/ItncAyMeFNy7LAWlhZqSJ + ClKORwtQj54+wTv3AR5vFD4meGz7iHuPUnJhcc2VB8j21ua6qmfFd3KzTh5kAGnPKUgN + u/oRKslJcpWqR8DjpvAYgsc7qB7F5ZtSnE03gEzYGeTjuXCOrUYCksOjkRngcaGnTxDB + 42UGj+8Qj1J0Y9HMmYtH2sC8tamuqowF5DaiIBlADhusWc0DeHi0V8BjVV1zazuW5ojG + OaU4ES4g4ZjHR6IgK4iCzEhhATldAwHJx+MciscExKMUXVeUc+biUQ7Ioju5508ciNm2 + yRsAaU32IDULkBSPw6h6pHjcFnPg5HlQj2VVdU0KeMQOOqL0WJFPigtIaD/HArKAAnJH + 0IYVC6GB+TgNS7Hy8Dgd8LghaEdCSgZRjxWvGlA9itxZJTA9Lh47A3Kj95J59tYTNAyQ + cjxa289f4r0R8HgC8FiEeJSAs0philxA8gB5nShIOSD1oIq1v4acgwQ89h84dLie0TgL + wOMKDo8FLB6hwRx7DBlXq1JwXhHOkYtHCsgWkmItus0oSBaQYw10NEdBMnjUMTCZYG0/ + D/EoQn+U/JS4gGQBWVPxuOD6pYyUeAJIV6ogNQeQXeLx0nXEo+S9WDwG4OJRCZBJMWEE + kHbWEzQIkBwex06wtpPjEdqTQ3K1hb/3iMtV8Tio1GbCBaQMkKUAyPSU+Eg5ILU1Q0Ey + eNRm1KMrUY/xKRmAx9KKGpJcRfUoNdcV5Xy5eFQAZGaqBgJSGY9hMUknMnMRj6J0S+lO + igtIRUAmAyDXL6cK0lBPIwDZCY+R8cnpiEfpOq5IZ87FIwfIl5BivQqAjA7z93Z3ZhXk + oN6vYiV4HDRMx4BTj4DH1MyriEeRuqV0p0UD8tu3vykg3zbUlJcW5F9KVwTkkF7fg6R4 + HKKtZ0j2Hl2Xrw/i8FiO6lG6vivGmSsBslYJkFNoirXXASnH4xQ7Z3dv/7BoFo8vazG5 + Kka3lO6cOgPyfv7F9OS4yEBGQZqCgoTmAb1apEPwCB10ZHgMJHi8mF9QCnh8yyZXv9GJ + SPc74szFYQEeID+1tzQxgDyXmhgdShXklPFkD7KXAfl9PH6SX0mOe4/icEopz6ITIEsY + QEYAIF2gSKf3ASnDoymoR5fl6wMj4wge7yMepey3Yp27IiAbWUAeJ4D0cneeqQGAlOFx + PKMeQ6MTU8+R5OrL2saWdsSjWD1TovNiAfmVSbHW15RTQB6Jiwhct8xl9rTJpoa6vaog + O+ExIu4IwWNJeU09qkeJOq2Ip80H5IcWBpA5544n7g71YwA5ZpSOVi8qSIpHLR2DMeOn + zHR29/IP3Z14/FwO4lHELintqfEA2dH2tr5aBsgtmgBIFo+6hqaTp812WbYuUAGPHVC5 + +vUbJlel7cHimr0yICvLHt7mAOnmBApyzKgRvQdIBo8jRhE8Orl5+SngEfqvyvo9YnJV + XG4p3dl0Aci8i+lH9kZoACCV8bglYi+rHqtBPSIepeu14p15J0A+ZQG51c8LAGnFAHJA + r1xWDngk7ckBj1YUj1sZ9fjwaSUkVxGP4nVKKc9MGZDPS+7lXUg/rAGA7BKPF/IguYp4 + lLLHinvuCoB831hb+fThrZyzxxJ3b/Vb1buA7AqPZ3NuIR7F7ZBSn12XgEwjgFzL7EGO + 7p09SA6Po5nk6lpQj4fTL+TdK3mOeJS6z4p5/kqAfE0AmQ2AjKKAnGFlTlOs6leQinhc + 5Qfq8RiDx9eN71E9itklpT23bgG5ea2Hyywo0ukVQPLxOMvFg+IxDfEobVeVxOy/B8jF + jr0FSBkeza1mOC0GPEYBHrOJekQ8SsItpTtJGSC//NHR1lxf/fwRpFjTDu+JoICcOqk3 + ACnH46RpBI+bI/YcJnh8xKrHL1iaI12HFfnMuwHk/qgQv1UUkMakSEe9CpKPR0fAYwji + UeReiNOTWUAOyM8AyDcEkNey0g4pA7Jf31/U9jDXAuiONp00VYbHrGsUj81QmoN4lH08 + /IPoLNAJkE8e3Mw+c7QXAcnh0RjUI4PH/ageRed3OKFuLMAA8u+vf33hAHn3WtapQ3u2 + b17jsWCWTEGqD5B9+5Fbcxg8LgD1uH3PoTQGj28Aj58Rj918SPzHorAAB0hoYP7pw/uG + 15UsIHeF+K4EBWlpzirIvn3Us2Dt05epXGXx6BsStf/omeybD55Uvm4ge4/YYE4UboeT + 6M4CHCD/BEC2goJ89ogCMnZ7AAfIkeSiAHUBksHjSKoeF3isIXg8lXXtLiRX3zS3Ujyy + DR+7mw/+c7SAkC2gDMgXAMgroCB3BRNA2gIg9UdoDRygpqsfYbODHOzQNza3BPW40jdk + F+DxCuJRyB6GY/9PFvguIB2IghypPVhdgFTCY8D2WAaPz1g8ys4h/6cp4v8YLSAYC3QD + yH0EkIvmMoAcqiZAEjwOZPBo67hopW/wrn0MHl+gehSMQ+FAf84CnQBZfDc369TB2HCi + IB1sJpkYqA2QBI+DtUcawN6jA6hHDo/FiMef+8T4twVkASVAvqIK8nSKDJBmxvrD1QNI + isehw/WNzSxt5yIeBeRDONQetECXgDxJALnaY76DzUS1AZLDo8lEG4f5gMfw2IOnsnLv + Ih578GPjqzTeAjxAfoQ9SABk4c0rAMidwT6eREGqC5BKePQB9ZhymiRXX7zCvUeN9yIc + YI9ZoBtAxoRvWr2UD0gV1wQo4HHpaoLHk4jHHvvK+CKhWIAPyLZ3AMjHhTcuywFpYWak + DgUpx6MFqEdPn+CdBI83Ch8TPLZ9xNIcobgTjvNnLcADZHtrc13Vs+I7uVknDzKAtOcU + pIqvfoRKcppcpepx6epN4TEEj3dQPf7s18W/LzQLdAPIhJ1BPp4L59qqBZAcHo3MAI8L + PX2CCB4vM3h8h3gUmkvheH/CAlw8foUq1vbWprqqMhaQ24iCZAA5bPDvKgUkD4/28/l4 + rKprbm3/LG8R8BPzxL+KFhCEBbiAhGMeH4mCrCAKMiOFAST0Z1W9guTjcQ7FYwLiURC+ + g4PseQtw8SgHZNGd3PMnDsRs2+QNgLQme5CqBSTF47CRBqAeKR63xRw4eR7UY1lVXZMC + HrGDTs9/fnyjplmAC0g5IAsoIHcEbVixEAA5TsUpVh4epwMeNwTtSEjJIOqx4lUDqkdN + 8xYcj6otwMVjZ0Bu9F4yz956gooBKcejtf38Jd6bAI8nAI9FiEdVf3l8v0ZagAtI2sCc + KsiC60RBygGpB1Ws/VV0DlKGx3EWgMcVHB4LWDxC/1X2GDKuVjXSe3BQPW0BLh4pIFtI + irXoNqMgZYDUUZ2CZPCoY2Aywdp+3hLvjYjHnv6++D6BWYALSBaQNRWPC65fykiJJ4B0 + pQpSdYAEPPYfOHS4npECHq8jHgXmQzjcnrMAF49KgEyKCSOAtLOeMNZAZYDk8DhWAY+3 + qXps4e894nK15z44vkmzLcAFpAyQpQDIdAVAaqtGQTJ41Gbw6ErUY3xKxiWCxxqSXEX1 + qNmOg6NTiQW4eFQAZGaqGgDJw6MdUY9hMUknMnMRjyr5zPhSoViAC0hFQCbHRwatX04V + pKGeSgDZCY+R8cnpgMdSxKNQXAfHqQILcPHIAfIlpFivAiCjw/y93Z1ZBTmo56tYu8Bj + auZVxKMKPjG+UkgWoAH57dvfFJBvG2rKSwvyL6UrAnJIj+9Bcng0JMlV1+Xrgzg8lqN6 + FJL34Fh72gJKgKxVAuQUmmLtcUASPA4apmMwdsIUO2d3b/+w6CQGjy/rmjC52tPfGN8n + IAt0BuT9/IvpyXGRgYyCNAUFCc0DerRIh+AROujocXgMpHjMLygFPL5lk6vf6MAEZEgc + KlqgByzAA+Sn9pYmOSBDqYKcMp7sQfYwILvHYy3g8ZP83CPuPfbAF8ZXCMoC3wOkCxTp + 9DwgZXg0ZdRjYGRccvrF/PuIR0E5Dg5WJRZQBGQjA8hzqYnRFJAzVQBIGR7HM+oxNDox + 9RxJrr6sbUQ8quQj40uFYwFFQNbXlJcQBXkkLgIUpMtsAkjdHlWQCnh0Wb4+MILBYwmq + R+E4DY5UZRbgA/JDCwPInHPHE3eH+nu5OwMgxxjoaPWgguThcaazu5c/4PH4uRzEo8o+ + ML5YWBZgAfkV9iA72t7WV8sBuW6Zy+xpk3sWkCwedQ1NJ0+f7bJsHeDxCFGPgMd6Jrn6 + 9RsmV4XlQTjanrRA94D083JzIoAcNaLnAEnxqKUzasz4KQSPfqG7eXiE9uSyfo+YXO3J + j4zvEo4FugHk3ogtPQ9IHh6nETxukeGxGvDYAQc7EI/C8RwcqSosoAzIyqcPb1MFuZUC + 0ooB5IAeuYuVweMIikcnNxkeH9LkKuJRFZ8X3yk0CygD8nnJ/bwL6UdUAMjOeNwL6jEP + 1CPiUWheg+NVlQW6AOStnLOQYu1xQAIeBwzSIni0mknwuJVRjw+fVsLeI+JRVR8Y3yss + C3QG5L28C2mHAZBrmRTr6J7Zg+wSjxfy7pU8RzwKy2NwtKq0gAIg3ze+BgV5K/vsscSo + rX6r3Jxm9JiC7AqPZ3NuUTy+Rzyq8hPju4Vkge8A0sNlFuxB9gggOTyONp1Mkqtrt0Ts + PZyGeBSSo+BY1WKB7wFyMQDSnO5B/myKlYfHGU5uq/y2RiUeO5tN8Pi6EfGolg+NPyIM + C8gA+YUp0nn+iCrIPRGb1xJATuoJQPLxOMvFY+1mxKMwnANHqXYLdAPI/VEhfqsWO/YM + IGV4NLea4bgY8aj2j4w/KBwLKACyub4aAHktK+0wC8ipckD+8qOPHI+TpjJ43EPV4yM2 + ufoFS3OE4y44UhVboBMgnzwgKVY5II1JFevPKEhlPIZE7Uf1qOKviq8XrAXkgPzc0db8 + hgHkqUN7tm9eAwpSBsh+fX+Uj8ytObqjTXl4zLp2D/D4prmt4zPiUbCugwNXgQU4QEJ/ + 1k8f3je8rnzy4Gb2maMASF9GQbKA7NvnxwKSw6Mxqx4JHs9k33rwBJOrKvia+ErBW4AB + 5N9f//rCAfLutSwCyIA1Hgt+HpB9+5FL5Rg8LvBYs3n7nkOnEI+C9xqcgKos0BUgrwAg + d4X4roQUq6W5sT5RkD949WOfvkzlqrG5JUmu+gIej57Jvknw2ED2HrH/qqq+K75XoBbg + APknALIVFOSzRxSQsXJAjiQ36fyYgmTwOJKqR8BjAIPHu1Q9tlL1yPZDFqjxcNhogR62 + gDIgX4CCpIAM9l25yNGWAeTAHwMkbHaQgx36DB5X+obsAjxeQTz28CfE14nJAp0AWcwD + pANJsY7UHvxjgOyEx1iiHu8+egbJVYJH2TUdYrInzgUt8DMW6AaQ+3YRQM61tTQjCvKH + AEnwOJDBo63jopW+wRweX6B6/Jkvhn9X1BboApC5WacOxoaTFKuDzSQTgx8EJMHjYG2q + Hh2oemTwWIx4FLU/4eR+zgI8QH6EPchXVEGeTuEDcvjQHwCkDI9mlrZzKR73MeoR8fhz + Hwz/trgt0CUgTxJArvaY72Az8QcByeLRwGSiDcVjeOzBU1m5dxGP4vYmnN3PWoAPyDYK + yMIbVwCQO4N9PFkF+QOApHgcOlzfmMGjT/CufSmnSXL1xSvYe/yIe48/+9nw74vVAp0B + eSc36+TBmPBNq5fyAfmfiub4eJzvsToA8HgS8ShWF8J59aAFFAD5DhTk48Ibl+WAtIAU + 638GpCIePX2CdxI83iikeGxDPPbg58NXic0CPEC2tzbXVT0r7gqQ/+lu5D5McpWqx/lL + V28KjyF4vIPqUWy+g/PpeQt0A8iEnUE+ngvn2lqYGf1XQMrxaAHJVRaPl28UPgb1+A7x + 2POfEN8oIgtw8fgVqljbW5vqqspYQG4jCtKepliHDf79PwASDlqRvcfOeKyqa25tx9Ic + ETkPTqXnLcAFJJyD/NgGCrKCKMiMFAaQ0MD8vwKSw6ORGeBxoadP0M6ElNOIx57/cPhG + UVqAi0c5IIvu5J4/cSDmBwFJ8TiM4tGeqMdtMQdOngf1WFZV16SAR2wwJ0p/wkn9pAW4 + gFQG5I6gDZ4L/zMgeXicPofFYwbBYwWqx5/8UPjXJWEBLh67AKT30nn21qRI598ryC7w + eALwWIR4lIQz4SR/3gJcQEIDc1ZBFlwnChIAuYIActx/SLEq4XFD0I6EFMTjz38jfIN0 + LMDFIw+QtxkFudF7CQBygomBzr8FpByP1vbzlnoT9Yh4lI4r4Ux7wgJcQLKArKl4XHD9 + kgIg9aBIp/+/uElHhsdxFqAeV7B4vF7AqkdoT87e0oHJnJ74cPgOUVqAi0cKyBayB1n0 + g4Bk8KhjYDIB8LjEeyODx9uoHkXpNzgpFVmAC0gZIEspIOOJgnSlClJP+98AEvDYf+DQ + 4XpGMjzGp2RcInisIaU5iEcVfT98rbgswMWjAiAzYQ8yjChIO+sJY/+dgvweHlv4pTm4 + XBWXA+FsetYCXED+HCAZPGozeHQl6pHBYynisWc/F75N5Bbg4pEPyKuZJ5L+GyA5PI6d + YG1H1GMYJFczcxn1iHgUuQvh9HrSAjQgv337WwGQ6cnxkUHrl1MFafjPCrIrPKaDekQ8 + 9uSXwndJwQJKgKx9CSnWq5mpSdFh/nIFOej7xzwIHgcN0zGQ4zHpROZVxKMU/Afn2MMW + UADk24aacpJiJYAM/LeApHgcoq1nSJKrrsvXB0XGJyvj8Rv9mR4eOr4OLSA6C/AA+am9 + pUkBkO7OdlNoivW7gJTjcYqds7u3f1h0UirF48vaJlSPonMYnJBqLdAZkPfzL6Ynx3GA + NAUFCd11ui3SIXiEBnN6hqYMHgMj4wCP+QWl5TUNb9m9R8Sjar8hvl08FugGkInRof7e + BJDjx8Ae5HcASfGopWMwZjyDx9DoRB4eP8k7duDeo3icBmeiOgt0DcgjcRGB65a7zJ42 + 2dRQlwFk1/2S2fbkhqaTp812Wb4uMCLuSPrF/PuIR9V9MXyzmC2gCMhGRkGeO564O9TP + y81pptX4MaNIf9ZubtLh+q+OGW8108nNyy90d+LxcyS5StUj4lHMnoNzU4kFFAFZX1Ne + QhTkkb0RW9Yto4AcTQHZdX9Wcskj2558tsuydVsi9lI8lpTX1KN6VMnnwpeK3AJ8QH5o + IYB8eDuHAHKr3yo3xxlW5gSQ3XTXgWwO12COtCf32wp4PJtz+yHgsbGlHfEoctfB6anC + Aiwgv0KRTkfb2/rq8pJ7eRfSDu+N2LzWY8Esrj9rlwtWWirHXvK4wGPt5og9h9Mu5N0r + eV6NeFTFp8J3SsACyoCsfPrwVvbZY/ujQmT9WZnmAZ0zOtxBK3LJIzSYg/bkx85m33r4 + tBLw+AHxKAHnwSn2vAWUAfn80b1rWacO7dkesIa9G7mbLQ+yXB0Emx1QKgfXdECLALY9 + OcVjB5x7/PoN9x57/nvhG8VtAQVAvm98Xfnkwc0rZ46S9nPMzVZMTUC/Tt2uyHIVsjlM + LQActKIddEiDOaY9+Z9f8ZoOcXsOzk4lFlAAZPOb6mfFd2n7uW0boSZgJtQE0C2PX/sq + L1iZ5eooY3Oy2QGlcswtVrSDThviUSWfCl8qAQsoApJtP5eRAlXlsOXRfUYHNh/hZAfT + sgOyOVsiSKkcc4vVe1SPEvAbnKKKLMAHJGk/xxy7olsei+faWpoZwU1zv/dXXrAyy1Xm + ZMdCT9+QKKgFyGFvser4/AXVo4q+Fr5W7BbgAxK668CxK1ITcBgyOqvJXeXklAfU6Cgv + WKEYYODQEaOgdHUmnOwg2Ry62QGV5KTBHKpHsXsNzk9lFpABEtrPwbEruuVBMjpBG5gi + VqjRge5zfRUzOiS7CgeRTSZNneXisS4Q7iQ/fQWyOZWvoRYA8aiyb4UvFr8FZIAk7eeg + JoDJ6MBNc36rFjvSBSsUlSuXBIB8HAzZVXrLo6cv1OakwrU5xWXVb5rxkkfxuwzOUJUW + YAD5N9xs9enDe9IOktwTAEWsZMFKjiWTmjklAUlqV4cO14fs6gwnd++NdLnKnOx4R7M5 + uPeoyg+G7xa1BThAkqI56JdMLtLJgRqdnUHrmQwrNLtSFpBEPmqNoMUA8z3WbImMp8tV + WpvT/hlbBIjaXXByqraAHJB0wfr8EWxBnjgQvdVv5SJyVTmcgiQCkr8D2YfIx5EGzMnH + DcG72JNWVWS5Ks/mqHrc+H60gBgtIAPkl8/sgpVmWMOhJMAJDnkY6w8HAakQj31/lcnH + RSv9QqEhcta1e3DSqoEuV7E0R4xegnNSmwU4QNIFK8mw3rxyGkoCtpAaVtjxGDUCWnko + 7EBCOgcuzqHy0Q3k454jUAxQ+PgFza7KlqtqGz7+EFpAVBZgAQkZHViwvqkmJQFwCnIX + KyChGSQcglSOx6HDye6jHdSSb44g8hGOdsDFAK1QK4eV5KJyDpyM+i3AAvIvyLC+a6h+ + XgKHPE6CgPT1hJvKJ5uOpgkd3gYkm84xmWjjsMBjXdDO/cfoRR3Vb+BiAJCPeLBD/V8Q + f1FMFuDi8X+f25kdj/yLaYdiyQ7kXFs2ocOv0IF4JGetTCeRi6w2BO9OhI7Idx/BUat3 + bZ++cA1YxWQfnAtaQK0WYALyKwhIUqLz5MGNyxnJe7dv9HajCR1yD6tiPJL06mjTydAS + GaoBYg6eysqj98q9/0B2O+jL1Dp8/DG0gKgswMYjCEioKYdrdG5ln0mJh4oA6AUJZ66U + E6yw3cFW58DVAH5hsYfgnscCLp3zFeNRVK6Bk+kNC5CA/PY3P6FzbN+OLWs9SIIVzkAO + Vdjw6EO2O/QM4aoOx8Ve/ttIevVGIRSvknQOKx97Yw74m2gBsViAiUdI6DAlrHdyM0mC + dZ3HAgebiWMNYMPjt37yggCIR9juMDK3pNVy2+OSSTE5VOdgPIrFHXAevWwBumD9+y+o + CCAJVqjQOX8iMYq/4dFfOR5J9So9bAXbHWfgJquyl3W0OoepBujl6eDPowWEbQESkNCc + lVTowL3IZMMjaXcwPXI1yZRsQPLjkZQDQDX5eCs75yWrAyISSDwWlVXx4lHYtsDRowV6 + 2wJ0wfqVxuOripL7eVmnDkSH+KxwnQN9PAxGag3iF+hw8QjlAEugHCDhKFyEDPHIbD9S + Pvb2bPD30QICtwAE5DeIR7oBWXo/78Kpg7x4HNZdPJLyHIV4pNuPAjcFDh8t0OsWYOLx + D148xmwlfGQLdLrhI8Zjr384HIAoLfBj8YjrVVE6A06q1y3wnfUqKWBV4CPsdyjlc+jd + cvL8aq/PBgeAFhC2Bdh4pCcgO+dzFA5c0f3H7+53CNsUOHq0QG9bAMLx/779y/0O5XoA + clsHd9wK63N6+0vi74vBAiQe2XqAGubGju/WA0C9nJGZ5QxHNy9/OI6ccfkGXPaI9Tli + cAScgyZYgOIR4vFT27v66ufFUC+X+r16ObaeHFrNrYJ68sPpF6FXAL0egBxHJu/ShDnh + GNACQrUAE49w5aPsgoBj+3cEMvXk5MIOxXryfsx5KwvZeasLcN6q4lXDe3q7HHkXBqRQ + PQHHrQkW4OLxYxvTwwPOWyVEbKY3sHZx3grOI5Pr5bjzyEknsq7BeWTmOiv2PLImTArH + gBYQpgUo0r59/ZPcwAoXWkETyIzkuO2b4IK5meSCOaXzyL+Q61ehW8BE6BawbF0QXPeY + mXsHes2RTuVf/mSvlxOmIXDUaAENsACNx7/hfoD2lsbXL+gN5Ydjt/mTjgEW0OIKLmDl + 3w/wCxSwkm460Bt5/lK4Djnh6Jls+QYk3p+jAR8UhyBkCzB4pOlVeryDlJMfjAn181wI + FyKbQkcduKCcd58ViUdSEMAeuNq+N5lNsEI3HUjoICCF7As49t63ABuPNJ1DOurcyT2f + mhglP24FDQMU73ukFwQYjbOwnbsYEqwx0G0O2neQhM4H6G6F8dj7XxRHIGAL0HCE0x3k + ug4mnZNz9ug+kl5lrwdQbqjD9AugF1q5rvAJ2Z3Ev2COu/ARU6wCdgkcei9agFuuMvKR + vV4uLiLAe4nzTHqdleJ2xy+/9Ok3gEno2JD2j0E79x1jTkDWNdMLkRGQvfgx8aeFbgEW + j6QagN4OQE4/ph2KCaPpHNKxnDSAVOjfQS5E5i4oJzcExBEBWfiEaRggW7AiIIXuGTj+ + XrAAF45cuwAiH6E6Zze9HYCeftQerNSwvA9J6NCKObhhjghIuIGVbagDNyJju/Je+Ir4 + k2KxABePTP9HtmF5SsKOQOZ2OaZ9h0J69ZdfaIMrciPytDkgIIOjuIZzcIUOdPDgMjoI + SLH4CM5DbRbgwlHWH7n0PtNuDqoBnGdajR8zavjQ3/mnH8m9j5DQIQJyLNmB9Fi7hV2w + 0hJWyLBCRofp4YEBqbbPiD8kDgsw4Qh3IQMeaXcruttxIknejpXKR4XtDpLQkTcshyMe + 22IOQgdI2sKDlOjIAYkBKQ4vwVmoywI8PH4mxTmQXSXdHxN2BK3rtl05xCMISNIywBRK + ysmCddf+Y2fhDlbScq6lHbYguZoArCtX13fE3xGFBXh4hM1HOGsFd6/mZUFzq/BNsNtB + mnfQbqxK8pEIyAGkxRWzYF2zOWLvEejhQXvqvGMBiStWUTgITkKdFpCFI6hHwGNT7Uto + xpoDzVijQnxpsdw4ZrdDablKF6yw46FvbGZJM6yh0YmpmVdpDWtTKwBSnmLFFas6vyf+ + lqAtwIUjUY8Ej3A1Oc3mJMdFQC8dUpxjYjBy2GDYfeRXr9KETj+6YCUZ1tkuy9YHRsan + ZFy6XlBaAYeuFAGJASloF8HBq88CbDhCMofisbWpror0Ks9MTYoO8/diej+OGgHNyhUO + d9C+On3ognWEAfQsJ008Nm6LOQA1cwwgiYIkgGRXrBiQ6vui+EtCtgCHx7+/0uQq4LGi + tOD6pYyU+B1B65e7zIZWAYa6UJyjvNtBIrIvZFihJMBwHM3obAjakcAHJOxBylM6GJBC + dhIcu7oswIUjrFbhZAenHq+eP3EgZttGms2ZMNZAB852dF6uEgEJC1a4JMAEtiDnLfXe + RABJFeTL2saWD59gzwMDUl0fEn9HBBaQLVaZ1WoHafxYXnI/n8HjBtIpwAKyOcOHDuzf + eblKdjygJAAyOkZm9BYdHwJIkmIteV5dD1XlsGKFKh1uxYqEFIHD4BRUaQF+ONLV6vuG + 15VPH1L1SPE4z96ay+Z0yq4yGZ3+sAUJgJxoA7cEEAWZlHou59bDJy9ekZTOHwoSEgNS + ld8S3y14CyiGI12tvql+9uhe3oX05PjIoA0ryM0AZkb6gEfI5ihnV2k8Mhkd2PIggFyx + ISgy7kgaFOkUQyPIppYPHxkJiYQUvKvgBFRvAS4cSWoVxONnWK1CMudx4c0rZ48lRoeB + emTxCDfndJXNkWd0CCBBQS7x9g/dnXj0zGXY8yivftPc2s5KSFlAIiJV/1nxF4Rpgc7h + +A5Wq7DXkZt16tDeiMD1y4l6JHjU6jqbwyxYf4ManRGMgnRdvm5LROxBktKhK9a3bbDp + QXM6GJDC9BEctdoswIXjN4aOUEf+vrGuClar+RczUvbtCvHzcne2s54AtQBks6OrbA67 + YCVbHiTFOsXOyW2Vb/DO+OT0C9fuFJVVvgYJCTkdDEi1fVP8IaFagIvG/5OF44eWpjfV + 5aUFN7LPHk+KCQ9Y4+EymyRXGTx2mc1hAfn7IC1IsY6bPG3WAo/Vm8KiE4+evpR/79Gz + qtrGrgIS16xCdRoct6os0FU4NtfXVDyB3Or5k4f2Rgb5eC5ynGE1fizgEdRj19kcGSAH + a+tAkY7VjLkLV6yHFeuB1HPZNwpg06Ou8T1HSN6+B573UNVnxfcK0gKyaOTRsbW5/tWL + p0V3crPSkxN2bfXzXjLfYeokU0Oy9zjgt27xSGsCYA9SW2+06SQb+3nuq3yDdsQdPnX+ + 6q1CyOlwAfknKQyQi0hEpCD9BgetCgsoRCOpkvvyx8cPrc0NryCXczfvYsbRxOhtAWuW + uUAyx3zMKB2oJP8OHmlNAOxBDhsxiux5zHbx8PbfuishOS0r9/aDxyQg37W1f4J9SBqQ + GJGq+KD4TgFbQB6NFI5f//zfl88fP7QAHSvLiu/lXz5zPCl2+5b1sFqdOQWSObra/4BH + Akgo0iEpnbHjrWznui5bG7Bt9/6U9Au5tx+SgISkTjsUBvzvz6+KiMRVq4C9CIfeIxbg + BSOJRrrtSPYd2XC8f/3KudSDIB59V7nPc5g62dRIf4TWINh77LIWgKhH8vTpC1WskNLR + MzSdaG3ntNhz/ZbwmMSjGTQgn1fXNrxt+QBpVopIhUUrjKZHZoUvQQsI0QKK0Ui3OWCt + +qm97X3Tm5oXZcUkHE8eitsR4ue9lORWzcYY6MBex4Bfv6MemYD8lVmxQo51qgNISJ/A + iJjEYyQgH5Q+f/m6vvk9XbP+jyxalSMSQ1KIroRj/kkL8GPx/4CNEI1UOX7u+ND6tqGu + uuJp0b3r2ZknD8fv2rpxtYfrHFur8bLV6nfxyACSWbGOMQcJuWCJl29QRGwSDcjCkrLK + mjeN71o/dICK5CKSpyPZgf3k9PCvowWEYgHFUGSCkaxU//cFlCPAsbnhdVX5k6K7+Vcy + Tx5JiArdtHb5QseZ1hNJbpWsVrvf66DLVbpi/RVWrEO1dUebgIScAzkdPxKQR9Ozcm8W + FD+tgDVr83smImlIUkrC/ycojwz/M1pAUhaAGADVSNAI0fgHicaWt41val4+K314J+/y + uZOHE6LCAtatWOxkbzN5nBHNrf7japWuWPuRFSsjIafM4AIyEQIy58a9osfPX9bUQUS2 + fej4+PmPLyQk//oK2R180AIStwCEwV9//UnQ+McnEo3vmupfV1eUPSq8fe3SmROH40k4 + ero5O0wj4hEK5cjW4z+sVhkF2e+3Ab8PGQZ1rKaTrGfOdaWEjElMScu8knf7waOyiqpX + bwgj29o7PpGQ/PK///0Jz1/4oAUkawESAf+DUAQyQjB+ADY21ddWVz57XHTvZu6F08cP + xe8KC1jv6TZvFuw8jiVbHf9qtSoLSNj00BllPI4XkPuPnDx7MfcmILLsRTVEZNNbCMkP + 7R0fP33+/AcJS3zQAhK2wB9/QCh++tjR3gZobG6sr615Wf4U4JiffT796IG9O0MJHSEc + LcePHa07XItUAvxDbpUEI3ngpoD+REKONDAeN9mGEtI3MHx3wqHjGVnZ+bcLi588e1H1 + qraehGRLaxsEZXtHx0fyfMIHLSA5C1DX7+joaP/woa2t9T0EY8Ob19Uvy8tKHgIcL507 + lZIYGxmyce0KJhxNRuuNgMKcf7daZQKSSEheQLos9fLZHLZrb1JK2rmLuTfuFBY/Lqt4 + Wf2qrr6hsentu/cQla0Ql/igBSRrgdbWlpb37942NzVAMNZUvXj+tKTo/q287KyM1MP7 + YiKC/NesWEzpaGII4fhvxSMNR1oVAAGppa3LEHKOy5KV6zdtjYzZfzg14/zl3Jt3CotK + y55DSNa8rn1T39DQ2NjU1AzPW3zQApKzAPH85qamxgYIxbraV9VVleVlTx49vH87P+fC + 2VMpSXFR2wL9vJctcnaAxaqJoT4Tjv+81cEEI/lXUqYzAJKsEJBjQEPOmDPfbcUav6Dw + qLiklJOns67k3rhd8PDR46fPyl+8rKquefX6dW1tXV3dG3zQAlK0APh+be3rVzXVEIsV + z8uelBQV3r2Vl3PxXPoxgGPk1oANXh6uTvbTLGg4Qmr13+w8ysORF5BEQ06cYjtr3qJl + 3j6bQyNjEg4ePXUm63Ju/q27BQ+LSx4/LXtWXvHiReXLly+ryFOND1pAQhagTl/18mVl + 5YuK8ufPyp6UPip6cO/2jWvZF86lpx5J3LsrPMh/recSl7l2Uy3MTUZTOtJcTld3WPFj + UOHPMkJCQJpOsJru4Oi6dOU6/6BtO2P3Hzp68nTmxezc67fu3Ct8WFT8qPTxkydPn5bh + gxaQqgWePn3yuLTkUVHRg/t3b9/Iu3o562z68eSkuOiIrQE+3ssWz58zw2ay2ViSyqF0 + /Dc7j0oB+SuzZB05ysh0vMXUmXMWLF7mvWFT8PZde/YfTElNP5sFIZl3/ebtO3fvFxQ+ + ePDwYRE+aAFJWuDhw4cPCgvu37t7+9aN/Nycy1mZGaeOHUmMi44MDfRbu3Kpq5PD9CmT + xo0x+OFwpLseEJCQZdXRNxxrNtnadpaT6xLP1b6bQ7bviklIOnL0RPqZzKxLV3Jyr+Xl + X79x8+atW7fxQQtI0AK3bt26eePG9fxruVezL1/IOptx6njKwf17oyPDgvzXe3kAHGdO + s5xgamygO/wH6UhQySxZISBH6EFWZ6LlNLs58xd5rFzjGxAcviN6774DR1JST6WfPpuZ + lXXx0uXLV7Kzs3PwQQtIzgLg+FeuXL508ULW+XNnMtJOHEs+lBgfsysiNNB/vfdydxdH + h+nWk81NDEeN1NYaTFI5/3mxyqxcSUDCtseQYcN1DYxMzSdbT3eYu2AxRKTPpqDQiJ3R + exISDx5OOXr8xKm09PSM02fOnDmLD1pAchYAxz99OiM9Pe1k6rGU5ENJ++NioiK3BW/2 + WwfR6Oo8e+ZUy4njxozW14Fw/H3AD4cjJSQUBgwcoqU9Un80INLCxnaWI0Skp/d6v4Cg + 0O2RUTF74vYlJh08dPhIcgo8R5nnGD6aaAH26+C/8S3wMx+KfQ/x/OTkI4cOHkjcn7A3 + ZvfOiG0hWzb6rFm1DKJxjt30KQBHIwPdEaRmdcBvP0pHdsn6a3/YiKRrViMTs0mWU2fM + cpy/cMmyVavX+20KDA4Nj9ixa3d0TOyevXHx8fEJ8OzDBy0gGQsQj0+Ij4+L27snJmZ3 + 1M6I7WEhQZv9fdZ6e3osdiHRaG0xwRTgOJKRjr9BGcB/2uhglqrcv8KSlaRZyZoVEGls + aj7Jaqqtwxxnl8VLl6/0XrvBzz9gS1BwSGjYtvDt2yO4JxIfTbQA93nw33kW+JkPJX/N + 9vDwsNCtIUGBmzf5+axb7bXCw33hfMfZNBrHjTWkcOSk40+EIyxZGRE5kCBSd9ToMRCR + ljbT7WY7znNZ5O6xfKWX95p1G3z8/Pw3bty0KQCezfigBaRkAeL0mzZt9Pfz8/VZv3a1 + 96oVy5a6uS5wmuMwY9oUiwnjYKmqp6M9bMjPrlVZRPaRIVJLW0d3lCFE5ESLKdNs7WbP + dV7gsshticey5StWrvLy8oZnNT5oAclZgHi+t9eqlZ4rlnksdV/s6jLPcY7DzOk2lpNp + NJKl6lACR1ir/ovzx9zStNt/pwHZ/3eyaNXW0Rs12tjEbMJkS+tp02c6zJ7j6DzfxcV1 + 4aLFbm7u7kvgWYoPWkBCFiA+v8Td3c1t8aKFri4L5jnNnTPLbsZ0GyuLieamY40MIBrp + LgeTyPmptSoXoRwiYdEKEak7ysBorCmEpIWVzbTpM+zsZ82eM2euo6OTk7Oz8zx80AIS + swC4vbOTk+PcOXNmz3Kwm2k7faq1pcXE8eNMjA0N9Eg0kqVqf7iboyfgyMRkn75MXodG + 5IiRegajjceajhs/cbKF5RRriMrp021nzJg5c6YdPmgByVkAHH/GDFvb6dOnTbWZYmUx + edIEM1OTMYYGo3R1ZNFIdjl6BI4sJGlEwtbHwEGwGzlcR1ffYLTRGIhJs/ETJk6abGFh + YWlpZWU1BR+0gOQsAI5vZWkJMTB50sTx5uMgFoGMo/RGjtCmuhHY2NPRCEEJi1aGkb8P + GjwUQhIoCTFpCEFpYmoKcWlmZm5uPh4ftIDkLACOb25mNm6cqanJ2DHGRqMNRunr6kAw + wkJ1IFmpqiAaCSXZiKSQJCEJmBypq6c/ygDC0tDIyMgYnjH4oAUkZgHi98ZGRoaGo0cb + QCjqQSwOBzIOgZQqKcdRUTSSiKS7kb/++huEJFByyFCtYRCUI3QgLHX19PT0uWcUPmgB + aViAc3lwf13dkSMhEodr01gEMrJo7FndSMOQ9y8MJJmQBDEJQQlRCWFJn+H4oAWkaAHG + /YcN09ICLA5mYpGsU3s4icOLQ94faUhCGR1gEjj5+8CBgwYNGjx48BD5MxQfTbSA/APh + n2QW+JkPJXvJEIhCiMNBA4GKvw8Y0B9iEZapqiUjLyJh3UqyO/36wdr1t/79IS5JYOKD + FpCyBSAKIBZ+I6FIY7Entzf4wdfdn2lQsmH5Kzy/4YMWkKgFiP+TOCSRCFjs0a3G7uKv + m39Ofh0eGAc+aAHpWoAJA3VDsZugxH+MFkALoAXQAmgBtABaAC2AFkALoAXQAmgBtABa + AC2AFkALoAXQAmgBtABaoFct8P/b5umcCmVuZHN0cmVhbQplbmRvYmoKMjQgMCBvYmoK + NDQ4NjUKZW5kb2JqCjIwIDAgb2JqCjw8IC9MZW5ndGggMjEgMCBSIC9UeXBlIC9YT2Jq + ZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMjU2IC9IZWlnaHQgMjU2IC9Db2xvclNw + YWNlCi9EZXZpY2VHcmF5IC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVE + ZWNvZGUgPj4Kc3RyZWFtCngB7dCFQQIAAABBsTswEAxsBQWD/XdzkL8b4Vartj8MGDBg + IDnwiwEDBgwkB34wYMCAgeTANwYMGDCQHFhiwIABA8mBBQYMGDCQHPjCgAEDBpIDnxgw + YMCAgeDAHAMGDBhIDswwYMCAgeTABwYMGDCQHHjHgAEDBpIDbxgwYMBAcuAVAwYMGEgO + vGDAgAEDBoIDzxgwYMBAcuAJAwYMGEgOPGLAgAEDyYEHDBgwYCA5MMWAAQMGkgP3GDBg + wEBy4A4DBgwYSA7cYsCAAQMGggM3GDBgwEByYIIBAwYMJAfGGDBgIDpwjQED2YERBgxk + B64wYCA7cIkBA9mBCwyEB84xYCA7MMSAgezAGQYMZAdOMWAgO3CCAQPZgWMMGMgOHGHA + QHbgEAMGsgMHGDCQHdjHQHhgDwMGsgO7GDCQHdjBgIHswDYGDGQHtjBgIDuwiQED2YEN + DBjIDqxjwEB2YIABA9mBNQyEB/4BLgXylgplbmRzdHJlYW0KZW5kb2JqCjIxIDAgb2Jq + CjM2NgplbmRvYmoKMjUgMCBvYmoKPDwgL0xlbmd0aCAyNiAwIFIgL1R5cGUgL1hPYmpl + Y3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAyNTYgL0hlaWdodCAyNTYgL0NvbG9yU3Bh + Y2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURl + Y29kZSA+PgpzdHJlYW0KeAHt3Qs/k2EYB+A5j2HOZpjz+TxzNkQSSSJJJJEkkkgStq/e + B+n6CPtte9/nuQ//K51KPT483N//vbv7c3v7++bm1/X11dXPy8sfFxffz8/Pzr6dnn49 + OflyfPz56Ojw8NPBwcf9/Q97e7u773d23m1vv93aerO5ubHxen391dray9XVFysry8vP + l5aeLS4+XViYn38yNzc7OzOTTCanp6empiYnJyYmxsfHxsZGR0dGEonE8HA8Hh8aGhwc + HBjo7+/v6+vt7e3p6e7u6urq7Ozo6Ghvb2tra21taWlpbm5qampsbGiIxWL19XV1dbW1 + 0Wi0piYSiVRXV1VVVVZWVJSXl5eVlZaWlpSEw+Hi4qKiosLCUChUUJCfHwwG8/Jyc3Nz + crKzs7OyMjMzMzICgbTP7/v3+/f/9/zz/Pf+8/53/nH+c/51/nf/cf9z/3X/V/9Q/1H/ + Uv9T/1T/Vf9W/9f/0P/R/9L/0//U/9X/1v83/2D+w/yL+R/zT+a/zL+Z/zP/aP7T/Kv5 + X/PP5r/Nv5v/t/9g/8P+i/0f+0/2v+y/2f+z/2j/0/6r/V/7z/a/7b/b/5d/IP9B/oX8 + D/kn8l/k38j/kX8k/0n+lfwv+Wfy3+Tfyf+Tfyj/Uf6l/E/5p/Jf5d/K/5V/LP9Z/rX8 + b/nn8t/l38v/5x/wH/gX/A/+Cf+Ff8P/4R/xn/hX/C/+Gf+Nf8f/4x/yH/mX/E/+Kf+V + f8v/5R/zn/nX/G/+Of89IxBIp1L8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8 + d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/ + zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf + +e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47 + /53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/n + vwfSqdTjw3/sv/8DK6twSQplbmRzdHJlYW0KZW5kb2JqCjI2IDAgb2JqCjgxNwplbmRv + YmoKMTcgMCBvYmoKPDwgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMCA+PgplbmRvYmoKMTgg + MCBvYmoKPDwgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgplbmRvYmoKMjcgMCBvYmoK + PDwgL0xlbmd0aCAyOCAwIFIgL04gMSAvQWx0ZXJuYXRlIC9EZXZpY2VHcmF5IC9GaWx0 + ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4AYVST0gUURz+zTYShIhBhXiIdwoJlSms + rKDadnVZlW1bldKiGGffuqOzM9Ob2TXFkwRdojx1D6JjdOzQoZuXosCsS9cgqSAIPHXo + +83s6iiEb3k73/v9/X7fe0RtnabvOylBVHNDlSulp25OTYuDHylFHdROWKYV+OlicYyx + 67mSv7vX1mfS2LLex7V2+/Y9tZVlYCHqLba3EPohkWYAH5mfKGWAs8Adlq/YPgE8WA6s + GvAjogMPmrkw09GcdKWyLZFT5qIoKq9iO0mu+/m5xr6LtYmD/lyPZtaOvbPqqtFM1LT3 + RKG8D65EGc9fVPZsNRSnDeOcSEMaKfKu1d8rTMcRkSsQSgZSNWS5n2pOnXXgdRi7XbqT + 4/j2EKU+yWCoibXpspkdhX0AdirL7BDwBejxsmIP54F7Yf9bUcOTwCdhP2SHedatH/YX + rlPge4Q9NeDOFK7F8dqKH14tAUP3VCNojHNNxNPXOXOkiO8x1BmY90Y5pgsxd5aqEzeA + O2EfWapmCrFd+67qJe57AnfT4zvRmzkLXKAcSXKxFdkU0DwJWBR9i7BJDjw+zh5V4Heo + mMAcuYnczSj3HtURG2ejUoFWeo1Xxk/jufHF+GVsGM+Afqx213t8/+njFXXXtj48+Y16 + 3DmuvZ0bVWFWcWUL3f/HMoSP2Sc5psHToVlYa9h25A+azEywDCjEfwU+l/qSE1Xc1e7t + uEUSzFA+LGwluktUbinU6j2DSqwcK9gAdnCSxCxaHLhTa7o5eHfYInpt+U1XsuuG/vr2 + evva8h5tyqgpKBPNs0RmlLFbo+TdeNv9ZpERnzg6vue9ilrJ/klFED+FOVoq8hRV9FZQ + 1sRvZw5+G7Z+XD+l5/VB/TwJPa2f0a/ooxG+DHRJz8JzUR+jSfCwaSHiEqCKgzPUTlRj + jQPiKfHytFtkkf0PQBn9ZgplbmRzdHJlYW0KZW5kb2JqCjI4IDAgb2JqCjcwNAplbmRv + YmoKMTIgMCBvYmoKWyAvSUNDQmFzZWQgMjcgMCBSIF0KZW5kb2JqCjI5IDAgb2JqCjw8 + IC9MZW5ndGggMzAgMCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIg + L0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Aa2TzWsTQRjGn02QCtZQi0jx4oJSPERdkhbb + W9t8SOwSlySlfhw02d1soslm3d1ErR561H+gFEQQPOjBmxc9tSeR4ieC9OBdUU9a6qGU + 9Z0Zd4Ng8eK7zMxvH555Z+adXSC+UXWcVgxA2/bd0qkZ+ey58/LAOiQcQgJJyFXdc6Y1 + TSXLDrH5gdwU74+xXF9iz7c+fdtz58Li7bXDSw9bO0wK5YRLCwJSkoT9luApxjXBFcbX + fMcnT4Ox3qgaxLeIk26llCF+RJywBD9lXBP8gnFPt9jcdWLFNpo2ENtNPGGYnk48RWwZ + nt4mpjwS2u0O5Y+znEd1x6W58bfER1hdaKS4mgAmv5J+r69dPAE8WQMOaH1tdBkYvgQ8 + m+xrP97wWkkjNa+eTvF00uBBYNdqEHwPgAHyb78Ogq3lINheojU2gJWi3nV73EsblF4B + /3oXZxbZgXd0Bgp+R39nURfuUoAHq0BlEVBpvEvj6E9g301AA+lUpnQ6bKKGJAN7szlV + lVNjynhem+fKf+zarS7dFY9h6gftWvEMjSPUPju+RpsS7PXKuZDrzXwhZKOanQ15oZEp + hlx386WQL1dPswPynKY9Vw7ZafFv//daM5Hf9HKRZ6FRYWfmHrdbmgv5Smc28htmNtqb + 3Sqy/4n7m34h2j+yyEGlR0YKY1AwjjyVfV58kzQDQ4+B+0PKyXR55eNLJvwRvnmd33Wm + 49xwm1bDl6fpjzSTcsHWjyfllKJM4BeDarM/CmVuZHN0cmVhbQplbmRvYmoKMzAgMCBv + YmoKNTY1CmVuZG9iagoyMiAwIG9iagpbIC9JQ0NCYXNlZCAyOSAwIFIgXQplbmRvYmoK + MzEgMCBvYmoKPDwgL0xlbmd0aCAzMiAwIFIgL04gMyAvQWx0ZXJuYXRlIC9EZXZpY2VS + R0IgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBhZRNSBRhGMf/s40EsQbR + lwjF0MEkVCYLUgLT9StTtmXVTAlinX13nRxnp5ndLUUihOiYdYwuVkSHiE7hoUOnOkQE + mXWJoKNFEAVeIrb/O5O7Y1S+MDO/eZ7/+3y9wwBVj1KOY0U0YMrOu8nemHZ6dEzb/BpV + qEYUXCnDczoSiQGfqZXP9Wv1LRRpWWqUsdb7NnyrdpkQUDQqd2QDPix5PODjki/knTw1 + ZyQbE6k02SE3uEPJTvIt8tZsiMdDnBaeAVS1U5MzHJdxIjvILUUjK2M+IOt22rTJ76U9 + 7RlT1LDfyDc5C9q48v1A2x5g04uKbcwDHtwDdtdVbPU1wM4RYPFQxfY96c9H2fXKyxxq + 9sMp0Rhr+lAqfa8DNt8Afl4vlX7cLpV+3mEO1vHUMgpu0deyMOUlENQb7Gb85Br9i4Oe + fFULsMA5jmwB+q8ANz8C+x8C2x8DiWpgqBWRy2w3uPLiIucCdOacadfMTuS1Zl0/onXw + aIXWZxtNDVrKsjTf5Wmu8IRbFOkmTFkFztlf23iPCnt4kE/2F7kkvO7frMylU12cJZrY + 1qe06OomN5DvZ8yePnI9r/cZt2c4YOWAme8bCjhyyrbiPBepidTY4/GTZMZXVCcfk/OQ + POcVB2VM334udSJBrqU9OZnrl5pd3Ns+MzHEM5KsWDMTnfHf/MYtJGXefdTcdSz/m2dt + kWcYhQUBEzbvNjQk0YsYGuHARQ4ZekwqTFqlX9BqwsPkX5UWEuVdFhW9WOGeFX/PeRS4 + W8Y/hVgccw3lCJr+Tv+iL+sL+l3983xtob7imXPPmsara18ZV2aW1ci4QY0yvqwpiG+w + 2g56LWRpneIV9OSV9Y3h6jL2fG3Zo8kc4mp8NdSlCGVqxDjjya5l90WyxTfh51vL9q/p + Uft89klNJdeyunhmKfp8NlwNa/+zq2DSsqvw5I2QLjxroe5VD6p9aovaCk09prarbWoX + 346qA+Udw5yViQus22X1KfZgY5reyklXZovg38Ivhv+lXmEL1zQ0+Q9NuLmMaQnfEdw2 + cIeU/8NfswMN3gplbmRzdHJlYW0KZW5kb2JqCjMyIDAgb2JqCjc5MgplbmRvYmoKNyAw + IG9iagpbIC9JQ0NCYXNlZCAzMSAwIFIgXQplbmRvYmoKMTkgMCBvYmoKPDwgL0xlbmd0 + aCAzMyAwIFIgL0Z1bmN0aW9uVHlwZSAwIC9CaXRzUGVyU2FtcGxlIDggL1NpemUgWyAx + MzY1IF0gL0RvbWFpbgpbIDAgMSBdIC9SYW5nZSBbIDAgMSAwIDEgMCAxIF0gL0ZpbHRl + ciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBzcLXcQJAEAXByT8vjLDCO4HwIoobfRPA + K7arW4f22V1akT1anX3ae/t85hfWOcAih1jnCIscY50TLHKKdX5jkTMsco51LrDIJda5 + wiLXWOcGQ7cYusPEPYYeMPEHQ4+YeMLQXww9Y+IFQ6+YeMPQO4Y+MPGJoX+Y+MLAf+tF + vN4KZW5kc3RyZWFtCmVuZG9iagozMyAwIG9iagoxMzAKZW5kb2JqCjMgMCBvYmoKPDwg + L1R5cGUgL1BhZ2VzIC9NZWRpYUJveCBbMCAwIDUxMiA1MTJdIC9Db3VudCAxIC9LaWRz + IFsgMiAwIFIgXSA+PgplbmRvYmoKMzQgMCBvYmoKPDwgL1R5cGUgL0NhdGFsb2cgL1Bh + Z2VzIDMgMCBSIC9WZXJzaW9uIC8xLjQgPj4KZW5kb2JqCjM1IDAgb2JqCjw8IC9MZW5n + dGggMzYgMCBSIC9MZW5ndGgxIDUwMzIgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3Ry + ZWFtCngB7TiNfxNVtufcOzOZNkDT0pbQAJl0aJWm2QIKlEJpaJKWNkILVDepsCb9kADF + Flo+n9Iqq2L4sK7a1d+u6K6rFaw6bZENRZevxdUVfn6ivLfig4X14z0R9Af7VoHOO5Nq + BZd9f8Gbk3vn3nPOveeej3vu3AACwBBoAw7u2mXhJpBhGmEOU7HWrmpR3tm342NqnwaQ + V9zetGhZ6hcziZ4QABDKFzWsvT0yatrbAEN/RzyvR+rDdWeXqscBhjVQf3KEEMPGCEQf + ZtDHRpa1rJH3Yxf1X6e+3NBYG4Y6SKc+8YC0LLymiT0q3k39Y9RX7ggvqx+59bl11P8H + 9dWmxuYWvQpuB0gaRf3sphX1TWeOuN6jvpfWEyYcEhjPEJCoADgMjNgLILbBGCojeA9k + 6hf0v1L5zCj9s4j2NCT31wDwEmpvi4+/qhJP0OyeAZT+L56rBlyzMzAQDE2Ncu2nHMxQ + AltABAsI8Az1zsJxuA8klGEyJEM+vIAfgQcq4R4shJ8TTwaUwQuwGz7ARfolEPSgfgCW + QidOIv0zoAAWwF44T3x/gp9CGNZAK96j30XWSYQxkAk3kDVXwDo4SRwJkAY2yMQsNo0f + gxE0sgHaYAPsFir0Z/XjNCaBoAKeheegH6sxou/STxEmH6aCD+bDHcT7AIqYJo7R/6Kf + 18/DRFrnQlhE8yyHx2AXmnA4OvEPrIh3wDjSsxGaIAod8Ev4M2bjm7xS3w4uggkwh+a7 + FWpozi3QB2/ANyixGraGvcB28RX8ogDCUfFtyae/ph+hyBXBSmMKaMRtFE+Lad4H4R34 + EP4KX8HXmIaZOAEn4wwsw6WsQTJLP9X30BgzjAaFbJADebTSAigmmEe2uBMehS54BV4l + OABfokJwHcE4vBWfwR48xZKYwg6zd9g3PJ/7+aOCVVgo3Ck+Jp6UcnSPvosslQJ2UGEK + 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 + Q1aLMVmjIaszPu5HsroMcqchq8uQ1WXI6nR3xmWhb/H8YrJVoFuG4qBnwcC7l5kTyT0h + myNYHPfTNId1va2PXcACMNMpMoQ+PoZSMVzomumaSSQP88RJw4zvku9I1vXTHLY+LGAX + 4iQLoZMpaPAHLxmtZmdzc/PKFqpaVkLLSoJm4miOU5zQMsBMwfi/LcjbTgplbmRzdHJl + YW0KZW5kb2JqCjM2IDAgb2JqCjM0NTYKZW5kb2JqCjM3IDAgb2JqCjw8IC9UeXBlIC9G + b250RGVzY3JpcHRvciAvQXNjZW50IDc1MSAvQ2FwSGVpZ2h0IDY5NSAvRGVzY2VudCAt + MzE5IC9GbGFncyAzMgovRm9udEJCb3ggWy0xNzYgLTIyNyAxMDc2IDkxM10gL0ZvbnRO + YW1lIC9EUUJQSVYrQ2FsaXNNVEJvbCAvSXRhbGljQW5nbGUgMAovU3RlbVYgMCAvTGVh + ZGluZyAxMTEgL01heFdpZHRoIDEwOTQgL1hIZWlnaHQgNDcxIC9Gb250RmlsZTIgMzUg + MCBSID4+CmVuZG9iagozOCAwIG9iagpbIDM0NCBdCmVuZG9iagoxMSAwIG9iago8PCAv + VHlwZSAvRm9udCAvU3VidHlwZSAvVHJ1ZVR5cGUgL0Jhc2VGb250IC9EUUJQSVYrQ2Fs + aXNNVEJvbCAvRm9udERlc2NyaXB0b3IKMzcgMCBSIC9XaWR0aHMgMzggMCBSIC9GaXJz + dENoYXIgMzMgL0xhc3RDaGFyIDMzIC9FbmNvZGluZyAvTWFjUm9tYW5FbmNvZGluZwo+ + PgplbmRvYmoKMSAwIG9iago8PCAvVGl0bGUgKFVudGl0bGVkKSAvQXV0aG9yIChQcmVz + dG9uIEphY2tzb24pIC9DcmVhdG9yIChPbW5pR3JhZmZsZSBQcm9mZXNzaW9uYWwpCi9Q + cm9kdWNlciAoTWFjIE9TIFggMTAuNS41IFF1YXJ0eiBQREZDb250ZXh0KSAvQ3JlYXRp + b25EYXRlIChEOjIwMDgxMTE3MTg0NzE0WjAwJzAwJykKL01vZERhdGUgKEQ6MjAwODEx + MTcxODQ3MTRaMDAnMDAnKSA+PgplbmRvYmoKeHJlZgowIDM5CjAwMDAwMDAwMDAgNjU1 + MzUgZiAKMDAwMDA2OTExNCAwMDAwMCBuIAowMDAwMDAxMDk4IDAwMDAwIG4gCjAwMDAw + NjQ5NjMgMDAwMDAgbiAKMDAwMDAwMDAyMiAwMDAwMCBuIAowMDAwMDAxMDc5IDAwMDAw + IG4gCjAwMDAwMDEyMDIgMDAwMDAgbiAKMDAwMDA2NDYxNCAwMDAwMCBuIAowMDAwMDAy + Njk4IDAwMDAwIG4gCjAwMDAwMTM5NDkgMDAwMDAgbiAKMDAwMDAwMTQ1NSAwMDAwMCBu + IAowMDAwMDY4OTM5IDAwMDAwIG4gCjAwMDAwNjI5MzcgMDAwMDAgbiAKMDAwMDAwMTYx + MyAwMDAwMCBuIAowMDAwMDAyNjc4IDAwMDAwIG4gCjAwMDAwMTM5NzAgMDAwMDAgbiAK + MDAwMDAxNTM2NiAwMDAwMCBuIAowMDAwMDYyMDE5IDAwMDAwIG4gCjAwMDAwNjIwNjQg + MDAwMDAgbiAKMDAwMDA2NDY1MCAwMDAwMCBuIAowMDAwMDYwNDQ4IDAwMDAwIG4gCjAw + MDAwNjA5ODggMDAwMDAgbiAKMDAwMDA2MzY2MiAwMDAwMCBuIAowMDAwMDE1Mzg3IDAw + MDAwIG4gCjAwMDAwNjA0MjYgMDAwMDAgbiAKMDAwMDA2MTAwOCAwMDAwMCBuIAowMDAw + MDYxOTk5IDAwMDAwIG4gCjAwMDAwNjIxMDkgMDAwMDAgbiAKMDAwMDA2MjkxNyAwMDAw + MCBuIAowMDAwMDYyOTc0IDAwMDAwIG4gCjAwMDAwNjM2NDIgMDAwMDAgbiAKMDAwMDA2 + MzY5OSAwMDAwMCBuIAowMDAwMDY0NTk0IDAwMDAwIG4gCjAwMDAwNjQ5NDMgMDAwMDAg + biAKMDAwMDA2NTA0NiAwMDAwMCBuIAowMDAwMDY1MTEwIDAwMDAwIG4gCjAwMDAwNjg2 + NTYgMDAwMDAgbiAKMDAwMDA2ODY3NyAwMDAwMCBuIAowMDAwMDY4OTE1IDAwMDAwIG4g + CnRyYWlsZXIKPDwgL1NpemUgMzkgL1Jvb3QgMzQgMCBSIC9JbmZvIDEgMCBSIC9JRCBb + IDw0OWU2MjQzZGUwYzBiMTQ0NmRmMDQzNjRjNzc1ZGNlZj4KPDQ5ZTYyNDNkZTBjMGIx + NDQ2ZGYwNDM2NGM3NzVkY2VmPiBdID4+CnN0YXJ0eHJlZgo2OTMzNgolJUVPRgoxIDAg + b2JqCjw8L0F1dGhvciAoUHJlc3RvbiBKYWNrc29uKS9DcmVhdGlvbkRhdGUgKEQ6MjAw + ODExMTQyMzU4MDBaKS9DcmVhdG9yIChPbW5pR3JhZmZsZSBQcm9mZXNzaW9uYWwgNS4x + IHJjIDEpL01vZERhdGUgKEQ6MjAwODExMTcxODQxMDBaKS9Qcm9kdWNlciAoTWFjIE9T + IFggMTAuNS41IFF1YXJ0eiBQREZDb250ZXh0KS9UaXRsZSAoUmVwb3J0ZXJJY29uLmdy + YWZmbGUpPj4KZW5kb2JqCnhyZWYKMSAxCjAwMDAwNzAyNzQgMDAwMDAgbiAKdHJhaWxl + cgo8PC9JRCBbPDQ5ZTYyNDNkZTBjMGIxNDQ2ZGYwNDM2NGM3NzVkY2VmPiA8NDllNjI0 + M2RlMGMwYjE0NDZkZjA0MzY0Yzc3NWRjZWY+XSAvSW5mbyAxIDAgUiAvUHJldiA2OTMz + NiAvUm9vdCAzNCAwIFIgL1NpemUgMzk+PgpzdGFydHhyZWYKNzA0OTgKJSVFT0YK + + QuickLookThumbnail + + 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 + + ReadOnly + NO + RowAlign + 1 + RowSpacing + 36 + SheetTitle + Canvas 1 + SmartAlignmentGuidesActive + NO + SmartDistanceGuidesActive + NO + UniqueID + 1 + UseEntirePage + + VPages + 1 + WindowInfo + + CurrentSheet + 0 + ExpandedCanvases + + + name + Canvas 1 + + + Frame + {{2002, 20}, {1215, 1180}} + ListView + + OutlineWidth + 142 + RightSidebar + + Sidebar + + SidebarWidth + 157 + VisibleRegion + {{-4.5, 0.5}, {522, 535.5}} + Zoom + 2 + ZoomValues + + + Canvas 1 + 2 + 4 + + + + saveQuickLookFiles + YES + + diff --git a/src/client/mac/sender/ReporterIcon.icns b/src/client/mac/sender/ReporterIcon.icns new file mode 100644 index 0000000000000000000000000000000000000000..e8c21242bf84a70a1efb7e005f47684a882eabe1 GIT binary patch literal 170816 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

TxWm44|qnrr$y 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 + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.Breakpad.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/src/client/mac/sender/crash_report_sender.h b/src/client/mac/sender/crash_report_sender.h new file mode 100644 index 00000000..76e6d3ea --- /dev/null +++ b/src/client/mac/sender/crash_report_sender.h @@ -0,0 +1,89 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This component uses the HTTPMultipartUpload of the breakpad project to send +// the minidump and associated data to the crash reporting servers. +// It will perform throttling based on the parameters passed to it and will +// prompt the user to send the minidump. + +#include + +#include "client/mac/Framework/Breakpad.h" + +#define kClientIdPreferenceKey @"clientid" + +@interface Reporter : NSObject { + @public + IBOutlet NSWindow *alertWindow; // The alert window + + // Values bound in the XIB + NSString *headerMessage_; // Message notifying of the crash + NSString *reportMessage_; // Message explaining the crash report + NSString *commentsValue_; // Comments from the user + NSString *emailMessage_; // Message requesting user email + NSString *emailValue_; // Email from the user + + @private + int configFile_; // File descriptor for config file + NSMutableDictionary *parameters_; // Key value pairs of data (STRONG) + NSData *minidumpContents_; // The data in the minidump (STRONG) + NSData *logFileData_; // An NSdata for the tar, bz2'd log file +} + +// Stops the modal panel with an NSAlertDefaultReturn value. This is the action +// invoked by the "Send Report" button. +- (IBAction)sendReport:(id)sender; +// Stops the modal panel with an NSAlertAlternateReturn value. This is the +// action invoked by the "Cancel" button. +- (IBAction)cancel:(id)sender; +// Opens the Google Privacy Policy in the default web browser. +- (IBAction)showPrivacyPolicy:(id)sender; + +// Delegate methods for the NSTextField for comments. We want to capture the +// Return key and use it to send the message when no text has been entered. +// Otherwise, we want Return to add a carriage return to the comments field. +- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView + doCommandBySelector:(SEL)commandSelector; + +// Accessors to make bindings work +- (NSString *)headerMessage; +- (void)setHeaderMessage:(NSString *)value; + +- (NSString *)reportMessage; +- (void)setReportMessage:(NSString *)value; + +- (NSString *)commentsValue; +- (void)setCommentsValue:(NSString *)value; + +- (NSString *)emailMessage; +- (void)setEmailMessage:(NSString *)value; + +- (NSString *)emailValue; +- (void)setEmailValue:(NSString *)value; +@end diff --git a/src/client/mac/sender/crash_report_sender.m b/src/client/mac/sender/crash_report_sender.m new file mode 100644 index 00000000..5d1b1e51 --- /dev/null +++ b/src/client/mac/sender/crash_report_sender.m @@ -0,0 +1,762 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import +#import +#import + +#import +#import + +#import "common/mac/HTTPMultipartUpload.h" + +#import "crash_report_sender.h" +#import "common/mac/GTMLogger.h" + +#define kLastSubmission @"LastSubmission" +const int kMinidumpFileLengthLimit = 800000; + +#define kApplePrefsSyncExcludeAllKey @"com.apple.PreferenceSync.ExcludeAllSyncKeys" + +@interface Reporter(PrivateMethods) ++ (uid_t)consoleUID; + +- (id)initWithConfigurationFD:(int)fd; + +- (NSString *)readString; +- (NSData *)readData:(ssize_t)length; + +- (BOOL)readConfigurationData; +- (BOOL)readMinidumpData; +- (BOOL)readLogFileData; + +- (BOOL)askUserPermissionToSend:(BOOL)shouldSubmitReport; +- (BOOL)shouldSubmitReport; + +// Run an alert window with the given timeout. Returns NSAlertButtonDefault if +// the timeout is exceeded. A timeout of 0 queues the message immediately in the +// modal run loop. +- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout; + +- (NSString*)clientID; +@end + +@implementation Reporter +//============================================================================= ++ (uid_t)consoleUID { + SCDynamicStoreRef store = + SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Reporter"), NULL, NULL); + uid_t uid = -2; // Default to "nobody" + if (store) { + CFStringRef user = SCDynamicStoreCopyConsoleUser(store, &uid, NULL); + + if (user) + CFRelease(user); + else + uid = -2; + + CFRelease(store); + } + + return uid; +} + +//============================================================================= +- (id)initWithConfigurationFD:(int)fd { + if ((self = [super init])) { + configFile_ = fd; + } + + // Because the reporter is embedded in the framework (and many copies + // of the framework may exist) its not completely certain that the OS + // will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our + // Info.plist. To make sure, also set the key directly if needed. + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) { + [ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey]; + } + + return self; +} + +//============================================================================= +- (NSString *)readString { + NSMutableString *str = [NSMutableString stringWithCapacity:32]; + char ch[2] = { 0 }; + + while (read(configFile_, &ch[0], 1) == 1) { + if (ch[0] == '\n') { + // Break if this is the first newline after reading some other string + // data. + if ([str length]) + break; + } else { + [str appendString:[NSString stringWithUTF8String:ch]]; + } + } + + return str; +} + +//============================================================================= +- (NSData *)readData:(ssize_t)length { + NSMutableData *data = [NSMutableData dataWithLength:length]; + char *bytes = (char *)[data bytes]; + + if (read(configFile_, bytes, length) != length) + return nil; + + return data; +} + +//============================================================================= +- (BOOL)readConfigurationData { + parameters_ = [[NSMutableDictionary alloc] init]; + + while (1) { + NSString *key = [self readString]; + + if (![key length]) + break; + + // Read the data. Try to convert to a UTF-8 string, or just save + // the data + NSString *lenStr = [self readString]; + ssize_t len = [lenStr intValue]; + NSData *data = [self readData:len]; + id value = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + + [parameters_ setObject:value ? value : data forKey:key]; + [value release]; + } + + // generate a unique client ID based on this host's MAC address + // then add a key/value pair for it + NSString *clientID = [self clientID]; + [parameters_ setObject:clientID forKey:@"guid"]; + + close(configFile_); + configFile_ = -1; + + return YES; +} + +// Per user per machine +- (NSString *)clientID { + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + NSString *crashClientID = [ud stringForKey:kClientIdPreferenceKey]; + if (crashClientID) { + return crashClientID; + } + + // Otherwise, if we have no client id, generate one! + srandom([[NSDate date] timeIntervalSince1970]); + long clientId1 = random(); + long clientId2 = random(); + long clientId3 = random(); + crashClientID = [NSString stringWithFormat:@"%x%x%x", + clientId1, clientId2, clientId3]; + + [ud setObject:crashClientID forKey:kClientIdPreferenceKey]; + [ud synchronize]; + return crashClientID; +} + +//============================================================================= +- (BOOL)readLogFileData { + unsigned int logFileCounter = 0; + + NSString *logPath; + int logFileTailSize = [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE] + intValue]; + + NSMutableArray *logFilenames; // An array of NSString, one per log file + logFilenames = [[NSMutableArray alloc] init]; + + char tmpDirTemplate[80] = "/tmp/CrashUpload-XXXXX"; + char *tmpDir = mkdtemp(tmpDirTemplate); + + // Construct key names for the keys we expect to contain log file paths + for(logFileCounter = 0;; logFileCounter++) { + NSString *logFileKey = [NSString stringWithFormat:@"%@%d", + @BREAKPAD_LOGFILE_KEY_PREFIX, + logFileCounter]; + + logPath = [parameters_ objectForKey:logFileKey]; + + // They should all be consecutive, so if we don't find one, assume + // we're done + + if (!logPath) { + break; + } + + NSData *entireLogFile = [[NSData alloc] initWithContentsOfFile:logPath]; + + if (entireLogFile == nil) { + continue; + } + + NSRange fileRange; + + // Truncate the log file, only if necessary + + if ([entireLogFile length] <= logFileTailSize) { + fileRange = NSMakeRange(0, [entireLogFile length]); + } else { + fileRange = NSMakeRange([entireLogFile length] - logFileTailSize, + logFileTailSize); + } + + char tmpFilenameTemplate[100]; + + // Generate a template based on the log filename + sprintf(tmpFilenameTemplate,"%s/%s-XXXX", tmpDir, + [[logPath lastPathComponent] fileSystemRepresentation]); + + char *tmpFile = mktemp(tmpFilenameTemplate); + + NSData *logSubdata = [entireLogFile subdataWithRange:fileRange]; + NSString *tmpFileString = [NSString stringWithUTF8String:tmpFile]; + [logSubdata writeToFile:tmpFileString atomically:NO]; + + [logFilenames addObject:[tmpFileString lastPathComponent]]; + [entireLogFile release]; + } + + if ([logFilenames count] == 0) { + [logFilenames release]; + logFileData_ = nil; + return NO; + } + + // now, bzip all files into one + NSTask *tarTask = [[NSTask alloc] init]; + + [tarTask setCurrentDirectoryPath:[NSString stringWithUTF8String:tmpDir]]; + [tarTask setLaunchPath:@"/usr/bin/tar"]; + + NSMutableArray *bzipArgs = [NSMutableArray arrayWithObjects:@"-cjvf", + @"log.tar.bz2",nil]; + [bzipArgs addObjectsFromArray:logFilenames]; + + [logFilenames release]; + + [tarTask setArguments:bzipArgs]; + [tarTask launch]; + [tarTask waitUntilExit]; + [tarTask release]; + + NSString *logTarFile = [NSString stringWithFormat:@"%s/log.tar.bz2",tmpDir]; + logFileData_ = [[NSData alloc] initWithContentsOfFile:logTarFile]; + if (logFileData_ == nil) { + GTMLoggerDebug(@"Cannot find temp tar log file: %@", logTarFile); + return NO; + } + return YES; + +} + +//============================================================================= +- (BOOL)readMinidumpData { + NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; + NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; + + if (![minidumpID length]) + return NO; + + NSString *path = [minidumpDir stringByAppendingPathComponent:minidumpID]; + path = [path stringByAppendingPathExtension:@"dmp"]; + + // check the size of the minidump and limit it to a reasonable size + // before attempting to load into memory and upload + const char *fileName = [path fileSystemRepresentation]; + struct stat fileStatus; + + BOOL success = YES; + + if (!stat(fileName, &fileStatus)) { + if (fileStatus.st_size > kMinidumpFileLengthLimit) { + fprintf(stderr, "Breakpad Reporter: minidump file too large " \ + "to upload : %d\n", (int)fileStatus.st_size); + success = NO; + } + } else { + fprintf(stderr, "Breakpad Reporter: unable to determine minidump " \ + "file length\n"); + success = NO; + } + + if (success) { + minidumpContents_ = [[NSData alloc] initWithContentsOfFile:path]; + success = ([minidumpContents_ length] ? YES : NO); + } + + if (!success) { + // something wrong with the minidump file -- delete it + unlink(fileName); + } + + return success; +} + +//============================================================================= +- (BOOL)askUserPermissionToSend:(BOOL)shouldSubmitReport { + // Send without confirmation + if ([[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM] isEqualToString:@"YES"]) { + GTMLoggerDebug(@"Skipping confirmation and sending report"); + return YES; + } + + // Determine if we should create a text box for user feedback + BOOL shouldRequestComments = + [[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS] + isEqual:@"YES"]; + + NSString *display = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + + if (![display length]) + display = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; + + NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR]; + + if (![vendor length]) + vendor = @"Vendor not specified"; + + NSBundle *bundle = [NSBundle mainBundle]; + [self setHeaderMessage:[NSString stringWithFormat: + NSLocalizedStringFromTableInBundle(@"headerFmt", nil, + bundle, + @""), vendor, display]]; + NSString *defaultButtonTitle = nil; + NSString *otherButtonTitle = nil; + NSTimeInterval timeout = 60.0; // timeout value for the user notification + + // Get the localized alert strings + // If we're going to submit a report, prompt the user if this is okay. + // Otherwise, just let them know that the app crashed. + + if (shouldSubmitReport) { + NSString *msgFormat = NSLocalizedStringFromTableInBundle(@"msg", + nil, + bundle, @""); + + [self setReportMessage:[NSString stringWithFormat:msgFormat, vendor]]; + + defaultButtonTitle = NSLocalizedStringFromTableInBundle(@"sendReportButton", + nil, bundle, @""); + otherButtonTitle = NSLocalizedStringFromTableInBundle(@"cancelButton", nil, + bundle, @""); + + // Nominally use the report interval + timeout = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL] + floatValue]; + } else { + [self setReportMessage:NSLocalizedStringFromTableInBundle(@"noSendMsg", nil, + bundle, @"")]; + defaultButtonTitle = NSLocalizedStringFromTableInBundle(@"noSendButton", + nil, bundle, @""); + timeout = 60.0; + } + // show the notification for at least one minute + if (timeout < 60.0) { + timeout = 60.0; + } + + // Initialize Cocoa, needed to display the alert + NSApplicationLoad(); + + int buttonPressed = NSAlertAlternateReturn; + + if (shouldRequestComments) { + BOOL didLoadNib = [NSBundle loadNibNamed:@"Breakpad" owner:self]; + if (didLoadNib) { + // Append the request for comments to the |reportMessage| string. + NSString *commentsMessage = + NSLocalizedStringFromTableInBundle(@"commentsMsg", nil, bundle, @""); + [self setReportMessage:[NSString stringWithFormat:@"%@\n\n%@", + [self reportMessage], + commentsMessage]]; + + // Add the request for email address. + [self setEmailMessage: + NSLocalizedStringFromTableInBundle(@"emailMsg", nil, bundle, @"")]; + + // Run the alert + buttonPressed = [self runModalWindow:alertWindow withTimeout:timeout]; + + // Extract info from the user into the parameters_ dictionary + if ([self commentsValue]) { + [parameters_ setObject:[self commentsValue] + forKey:@BREAKPAD_COMMENTS]; + } + if ([self emailValue]) { + [parameters_ setObject:[self emailValue] + forKey:@BREAKPAD_EMAIL]; + } + } + } else { + // Create an alert panel to tell the user something happened + NSPanel* alert = NSGetAlertPanel([self headerMessage], + [self reportMessage], + defaultButtonTitle, + otherButtonTitle, nil); + + // Pop the alert with an automatic timeout, and wait for the response + buttonPressed = [self runModalWindow:alert withTimeout:timeout]; + + // Release the panel memory + NSReleaseAlertPanel(alert); + } + return buttonPressed == NSAlertDefaultReturn; +} + +- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout { + // Queue a |stopModal| message to be performed in |timeout| seconds. + [NSApp performSelector:@selector(stopModal) + withObject:nil + afterDelay:timeout]; + + // Run the window modally and wait for either a |stopModal| message or a + // button click. + [NSApp activateIgnoringOtherApps:YES]; + int returnMethod = [NSApp runModalForWindow:window]; + + // Cancel the pending |stopModal| message. + if (returnMethod != NSRunStoppedResponse) { + [NSObject cancelPreviousPerformRequestsWithTarget:NSApp + selector:@selector(stopModal) + object:nil]; + } + return returnMethod; +} + +- (IBAction)sendReport:(id)sender { + [alertWindow orderOut:self]; + // Use NSAlertDefaultReturn so that the return value of |runModalWithWindow| + // matches the AppKit function NSRunAlertPanel() + [NSApp stopModalWithCode:NSAlertDefaultReturn]; +} + +// UI Button Actions +//============================================================================= +- (IBAction)cancel:(id)sender { + [alertWindow orderOut:self]; + // Use NSAlertDefaultReturn so that the return value of |runModalWithWindow| + // matches the AppKit function NSRunAlertPanel() + [NSApp stopModalWithCode:NSAlertAlternateReturn]; +} + +- (IBAction)showPrivacyPolicy:(id)sender { + // Get the localized privacy policy URL and open it in the default browser. + NSURL* privacyPolicyURL = + [NSURL URLWithString:NSLocalizedStringFromTableInBundle( + @"privacyPolicyURL", nil, [NSBundle mainBundle], @"")]; + [[NSWorkspace sharedWorkspace] openURL:privacyPolicyURL]; +} + +// Text Field Delegate Methods +//============================================================================= +- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView + doCommandBySelector:(SEL)commandSelector { + BOOL result = NO; + // If the user has entered text, don't end editing on "return" + if (commandSelector == @selector(insertNewline:) + && [[textView string] length] > 0) { + [textView insertNewlineIgnoringFieldEditor:self]; + result = YES; + } + return result; +} + +// Accessors +//============================================================================= +- (NSString *)headerMessage { + return [[headerMessage_ retain] autorelease]; +} + +- (void)setHeaderMessage:(NSString *)value { + if (headerMessage_ != value) { + [headerMessage_ autorelease]; + headerMessage_ = [value copy]; + } +} + +- (NSString *)reportMessage { + return [[reportMessage_ retain] autorelease]; +} + +- (void)setReportMessage:(NSString *)value { + if (reportMessage_ != value) { + [reportMessage_ autorelease]; + reportMessage_ = [value copy]; + } +} + +- (NSString *)commentsValue { + return [[commentsValue_ retain] autorelease]; +} + +- (void)setCommentsValue:(NSString *)value { + if (commentsValue_ != value) { + [commentsValue_ autorelease]; + commentsValue_ = [value copy]; + } +} + +- (NSString *)emailMessage { + return [[emailMessage_ retain] autorelease]; +} + +- (void)setEmailMessage:(NSString *)value { + if (emailMessage_ != value) { + [emailMessage_ autorelease]; + emailMessage_ = [value copy]; + } +} + +- (NSString *)emailValue { + return [[emailValue_ retain] autorelease]; +} + +- (void)setEmailValue:(NSString *)value { + if (emailValue_ != value) { + [emailValue_ autorelease]; + emailValue_ = [value copy]; + } +} + +//============================================================================= +- (BOOL)shouldSubmitReport { + float interval = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL] + floatValue]; + NSString *program = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + NSMutableDictionary *programDict = + [NSMutableDictionary dictionaryWithDictionary:[ud dictionaryForKey:program]]; + NSNumber *lastTimeNum = [programDict objectForKey:kLastSubmission]; + NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0; + NSTimeInterval now = CFAbsoluteTimeGetCurrent(); + NSTimeInterval spanSeconds = (now - lastTime); + + [programDict setObject:[NSNumber numberWithFloat:now] forKey:kLastSubmission]; + [ud setObject:programDict forKey:program]; + [ud synchronize]; + + // If we've specified an interval and we're within that time, don't ask the + // user if we should report + GTMLoggerDebug(@"Reporter Interval: %f", interval); + if (interval > spanSeconds) { + GTMLoggerDebug(@"Within throttling interval, not sending report"); + return NO; + } + return YES; +} + +//============================================================================= +- (void)report { + NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]]; + HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url]; + NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary]; + + // Set the known parameters. This should be kept up to date with the + // parameters defined in the Breakpad.h list of parameters. The intent + // is so that if there's a parameter that's not in this list, we consider + // it to be a "user-defined" parameter and we'll upload it to the server. + NSSet *knownParameters = + [NSSet setWithObjects:@kReporterMinidumpDirectoryKey, + @kReporterMinidumpIDKey, @BREAKPAD_PRODUCT_DISPLAY, + @BREAKPAD_PRODUCT, @BREAKPAD_VERSION, @BREAKPAD_URL, + @BREAKPAD_REPORT_INTERVAL, @BREAKPAD_SKIP_CONFIRM, + @BREAKPAD_SEND_AND_EXIT, @BREAKPAD_REPORTER_EXE_LOCATION, + @BREAKPAD_INSPECTOR_LOCATION, @BREAKPAD_LOGFILES, + @BREAKPAD_LOGFILE_UPLOAD_SIZE, @BREAKPAD_EMAIL, + @BREAKPAD_REQUEST_COMMENTS, @BREAKPAD_COMMENTS, + @BREAKPAD_VENDOR, nil]; + + // Add parameters + [uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_PRODUCT] + forKey:@"prod"]; + [uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_VERSION] + forKey:@"ver"]; + + if ([parameters_ objectForKey:@BREAKPAD_EMAIL]) { + [uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_EMAIL] forKey:@"email"]; + } + + NSString* comments = [parameters_ objectForKey:@BREAKPAD_COMMENTS]; + if (comments != nil) { + [uploadParameters setObject:comments forKey:@"comments"]; + } + // Add any user parameters + NSArray *parameterKeys = [parameters_ allKeys]; + int keyCount = [parameterKeys count]; + for (int i = 0; i < keyCount; ++i) { + NSString *key = [parameterKeys objectAtIndex:i]; + if (![knownParameters containsObject:key] && + ![key hasPrefix:@BREAKPAD_LOGFILE_KEY_PREFIX]) + [uploadParameters setObject:[parameters_ objectForKey:key] forKey:key]; + } + [upload setParameters:uploadParameters]; + // Add minidump file + if (minidumpContents_) { + [upload addFileContents:minidumpContents_ name:@"upload_file_minidump"]; + + // Send it + NSError *error = nil; + NSData *data = [upload send:&error]; + NSString *result = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + const char *reportID = "ERR"; + + if (error) + fprintf(stderr, "Breakpad Reporter: Send Error: %s\n", + [[error description] UTF8String]); + else + reportID = [result UTF8String]; + + // rename the minidump file according to the id returned from the server + NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; + NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; + + NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp", + minidumpDir, minidumpID]; + NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp", + minidumpDir, reportID]; + + const char *src = [srcString fileSystemRepresentation]; + const char *dest = [destString fileSystemRepresentation]; + + if (rename(src, dest) == 0) { + fprintf(stderr, "Breakpad Reporter: Renamed %s to %s after successful " \ + "upload\n",src, dest); + } + else { + // can't rename - don't worry - it's not important for users + fprintf(stderr, "Breakpad Reporter: successful upload report ID = %s\n", + reportID ); + } + [result release]; + } + + if (logFileData_) { + HTTPMultipartUpload *logUpload = [[HTTPMultipartUpload alloc] initWithURL:url]; + + [uploadParameters setObject:@"log" forKey:@"type"]; + [logUpload setParameters:uploadParameters]; + [logUpload addFileContents:logFileData_ name:@"log"]; + + NSError *error = nil; + NSData *data = [logUpload send:&error]; + NSString *result = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + [result release]; + [logUpload release]; + } + + [upload release]; +} + +//============================================================================= +- (void)dealloc { + [parameters_ release]; + [minidumpContents_ release]; + [logFileData_ release]; + [super dealloc]; +} +@end + +//============================================================================= +int main(int argc, const char *argv[]) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + // The expectation is that there will be one argument which is the path + // to the configuration file + if (argc != 2) { + exit(1); + } + + // Open the file before (potentially) switching to console user + int configFile = open(argv[1], O_RDONLY, 0600); + + // we want to avoid a build-up of old config files even if they + // have been incorrectly written by the framework + unlink(argv[1]); + + if (configFile == -1) { + exit(1); + } + + Reporter *reporter = [[Reporter alloc] initWithConfigurationFD:configFile]; + + // Gather the configuration data + if (![reporter readConfigurationData]) { + exit(1); + } + + // Read the minidump into memory before we (potentially) switch from the + // root user + [reporter readMinidumpData]; + + [reporter readLogFileData]; + + // only submit a report if we have not recently crashed in the past + BOOL shouldSubmitReport = [reporter shouldSubmitReport]; + BOOL okayToSend = NO; + + // ask user if we should send + if (shouldSubmitReport) { + okayToSend = [reporter askUserPermissionToSend:shouldSubmitReport]; + } + + // If we're running as root, switch over to nobody + if (getuid() == 0 || geteuid() == 0) { + struct passwd *pw = getpwnam("nobody"); + + // If we can't get a non-root uid, don't send the report + if (!pw) + exit(0); + + if (setgid(pw->pw_gid) == -1) + exit(0); + + if (setuid(pw->pw_uid) == -1) + exit(0); + } + + if (okayToSend && shouldSubmitReport) { + [reporter report]; + } + + // Cleanup + [reporter release]; + [pool release]; + + return 0; +} diff --git a/src/client/mac/testapp/Controller.h b/src/client/mac/testapp/Controller.h new file mode 100644 index 00000000..7b3be2d6 --- /dev/null +++ b/src/client/mac/testapp/Controller.h @@ -0,0 +1,65 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import + +#import + +enum BreakpadForkBehavior { + DONOTHING = 0, + UNINSTALL, + RESETEXCEPTIONPORT +}; + +enum BreakpadForkTestCrashPoint { + DURINGLAUNCH = 5, + AFTERLAUNCH = 6, + BETWEENFORKEXEC = 7 +}; + +@interface Controller : NSObject { + IBOutlet NSWindow *window_; + IBOutlet NSWindow *forkTestOptions_; + + BreakpadRef breakpad_; + + enum BreakpadForkBehavior bpForkOption; + + BOOL useVFork; + enum BreakpadForkTestCrashPoint progCrashPoint; +} + +- (IBAction)crash:(id)sender; +- (IBAction)forkTestOptions:(id)sender; +- (IBAction)forkTestGo:(id)sender; +- (IBAction)showForkTestWindow:(id) sender; +- (void)generateReportWithoutCrash:(id)sender; +- (void)awakeFromNib; + +@end diff --git a/src/client/mac/testapp/Controller.m b/src/client/mac/testapp/Controller.m new file mode 100644 index 00000000..e548c917 --- /dev/null +++ b/src/client/mac/testapp/Controller.m @@ -0,0 +1,257 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import + +#import "Controller.h" +#import "TestClass.h" +#include +#include + +@implementation Controller + +- (void)causeCrash { + float *aPtr = nil; + NSLog(@"Crash!"); + NSLog(@"Bad programmer: %f", *aPtr); +} + +- (void)generateReportWithoutCrash:(id)sender { + BreakpadGenerateAndSendReport(breakpad_); +} + +- (IBAction)showForkTestWindow:(id) sender { + [forkTestOptions_ setIsVisible:YES]; +} + +- (IBAction)forkTestOptions:(id)sender { + int tag = [[sender selectedCell] tag]; + NSLog(@"sender tag: %d", tag); + if (tag <= 2) { + bpForkOption = tag; + } + + if (tag == 3) { + useVFork = NO; + } + + if (tag == 4) { + useVFork = YES; + } + + if (tag >= 5 && tag <= 7) { + progCrashPoint = tag; + } + +} + +- (IBAction)forkTestGo:(id)sender { + + NSString *resourcePath = [[NSBundle bundleForClass: + [self class]] resourcePath]; + NSString *execProgname; + if (progCrashPoint == DURINGLAUNCH) { + execProgname = [resourcePath stringByAppendingString:@"/crashduringload"]; + } else if (progCrashPoint == AFTERLAUNCH) { + execProgname = [resourcePath stringByAppendingString:@"/crashInMain"]; + } + + const char *progName = NULL; + if (progCrashPoint != BETWEENFORKEXEC) { + progName = [execProgname UTF8String]; + } + + int pid; + + if (bpForkOption == UNINSTALL) { + BreakpadRelease(breakpad_); + } + + if (useVFork) { + pid = vfork(); + } else { + pid = fork(); + } + + if (pid == 0) { + sleep(3); + NSLog(@"Child continuing"); + FILE *fd = fopen("/tmp/childlog.txt","wt"); + kern_return_t kr; + if (bpForkOption == RESETEXCEPTIONPORT) { + kr = task_set_exception_ports(mach_task_self(), + EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | + EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT, + MACH_PORT_NULL, + EXCEPTION_DEFAULT, + THREAD_STATE_NONE); + fprintf(fd,"task_set_exception_ports returned %d\n", kr); + } + + if (progCrashPoint == BETWEENFORKEXEC) { + fprintf(fd,"crashing post-fork\n"); + int *a = NULL; + printf("%d\n",*a++); + } + + fprintf(fd,"about to call exec with %s\n", progName); + fclose(fd); + int i = execl(progName, progName, NULL); + fprintf(fd, "exec returned! %d\n", i); + fclose(fd); + } +} + +- (IBAction)crash:(id)sender { + int tag = [sender tag]; + + if (tag == 1) { + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + [self performSelector:@selector(causeCrash) withObject:nil afterDelay:10]; + [sender setState:NSOnState]; + return; + } + + if (tag == 2 && breakpad_) { + BreakpadRelease(breakpad_); + breakpad_ = NULL; + return; + } + + [self causeCrash]; +} + +- (void)anotherThread { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + TestClass *tc = [[TestClass alloc] init]; + + [tc wait]; + + [pool release]; +} + +- (void)awakeFromNib { + NSBundle *bundle = [NSBundle mainBundle]; + NSDictionary *info = [bundle infoDictionary]; + + + breakpad_ = BreakpadCreate(info); + + // Do some unit tests with keys + // first a series of bogus values + BreakpadSetKeyValue(breakpad_, nil, @"bad2"); + BreakpadSetKeyValue(nil, @"bad3", @"bad3"); + + // Now some good ones + BreakpadSetKeyValue(breakpad_,@"key1", @"value1"); + BreakpadSetKeyValue(breakpad_,@"key2", @"value2"); + BreakpadSetKeyValue(breakpad_,@"key3", @"value3"); + + // Look for a bogus one that we didn't try to set + NSString *test = BreakpadKeyValue(breakpad_, @"bad4"); + if (test) { + NSLog(@"Bad BreakpadKeyValue (bad4)"); + } + + // Look for a bogus one we did try to set + test = BreakpadKeyValue(breakpad_, @"bad1"); + if (test) { + NSLog(@"Bad BreakpadKeyValue (bad1)"); + } + + // Test some bad args for BreakpadKeyValue + test = BreakpadKeyValue(nil, @"bad5"); + if (test) { + NSLog(@"Bad BreakpadKeyValue (bad5)"); + } + + test = BreakpadKeyValue(breakpad_, nil); + if (test) { + NSLog(@"Bad BreakpadKeyValue (nil)"); + } + + // Find some we did set + test = BreakpadKeyValue(breakpad_, @"key1"); + if (![test isEqualToString:@"value1"]) { + NSLog(@"Can't find BreakpadKeyValue (key1)"); + } + test = BreakpadKeyValue(breakpad_, @"key2"); + if (![test isEqualToString:@"value2"]) { + NSLog(@"Can't find BreakpadKeyValue (key2)"); + } + test = BreakpadKeyValue(breakpad_, @"key3"); + if (![test isEqualToString:@"value3"]) { + NSLog(@"Can't find BreakpadKeyValue (key3)"); + } + + // Bad args for BreakpadRemoveKeyValue + BreakpadRemoveKeyValue(nil, @"bad6"); + BreakpadRemoveKeyValue(breakpad_, nil); + + // Remove one that is valid + BreakpadRemoveKeyValue(breakpad_, @"key3"); + + // Try and find it + test = BreakpadKeyValue(breakpad_, @"key3"); + if (test) { + NSLog(@"Shouldn't find BreakpadKeyValue (key3)"); + } + + // Try and remove it again + BreakpadRemoveKeyValue(breakpad_, @"key3"); + + // Try removal by setting to nil + BreakpadSetKeyValue(breakpad_,@"key2", nil); + // Try and find it + test = BreakpadKeyValue(breakpad_, @"key2"); + if (test) { + NSLog(@"Shouldn't find BreakpadKeyValue (key2)"); + } + + [NSThread detachNewThreadSelector:@selector(anotherThread) + toTarget:self withObject:nil]; + + NSUserDefaults *args = [NSUserDefaults standardUserDefaults]; + + // If the user specified autocrash on the command line, toggle + // Breakpad to not confirm and crash immediately. This is for + // automated testing. + if ([args boolForKey:@"autocrash"]) { + BreakpadSetKeyValue(breakpad_, + @BREAKPAD_SKIP_CONFIRM, + @"YES"); + [self causeCrash]; + } + + progCrashPoint = DURINGLAUNCH; + [window_ center]; + [window_ makeKeyAndOrderFront:self]; +} + +@end diff --git a/src/client/mac/testapp/English.lproj/InfoPlist.strings b/src/client/mac/testapp/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..b8c6c6bf0ba1a55cea7e4656822a4ee5d65546b8 GIT binary patch literal 192 zcmXYq!4APt5Jk_rU(r}Lg4kGyl?4*9;0vl+1Z~qI;g9?zju)9^?z}s9&U=68lMu6{ zA|vI@ktaDjy}R1b>Qt0!tQPi#ufn-U9r7(#-IM7@NF_2OYsH2+H!gBZ*6vfeDO;~{ krV?r|8u@z;fiaQO;bot>t@MA%?*Gp$OhX3N$lRFd3!KFv6#xJL literal 0 HcmV?d00001 diff --git a/src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib b/src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib new file mode 100644 index 00000000..ad86b716 --- /dev/null +++ b/src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib @@ -0,0 +1,47 @@ + + + + + IBClasses + + + ACTIONS + + crash + id + forkTestGo + id + forkTestOptions + id + generateReportWithoutCrash + id + showForkTestWindow + id + + CLASS + Controller + LANGUAGE + ObjC + OUTLETS + + forkTestOptions_ + NSWindow + window_ + NSWindow + + SUPERCLASS + NSObject + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib b/src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib new file mode 100644 index 00000000..839043e0 --- /dev/null +++ b/src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib @@ -0,0 +1,22 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + ../GoogleBreakpadTest.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 221 + 29 + 2 + + IBSystem Version + 9F33 + targetFramework + IBCocoaFramework + + diff --git a/src/client/mac/testapp/English.lproj/MainMenu.nib/keyedobjects.nib b/src/client/mac/testapp/English.lproj/MainMenu.nib/keyedobjects.nib new file mode 100644 index 0000000000000000000000000000000000000000..1c6baaf7b83cf03445585669d076ca65adf51031 GIT binary patch literal 34374 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 KzW)DzRsNq#itxMu literal 0 HcmV?d00001 diff --git a/src/client/mac/testapp/Info.plist b/src/client/mac/testapp/Info.plist new file mode 100644 index 00000000..76d70f21 --- /dev/null +++ b/src/client/mac/testapp/Info.plist @@ -0,0 +1,46 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + bomb + CFBundleIdentifier + com.Google.BreakpadTest + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + BreakpadProductDisplay + Google Breakpad Tester + BreakpadProduct + Breakpad_Tester + BreakpadVersion + 1.2.3.4 + BreakpadReportInterval + 10 + BreakpadSkipConfirm + NO + BreakpadSendAndExit + YES + BreakpadRequestComments + YES + BreakpadVendor + Foo Bar Corp, Incorporated, LTD, LLC + LSUIElement + 1 + + diff --git a/src/client/mac/testapp/TestClass.h b/src/client/mac/testapp/TestClass.h new file mode 100644 index 00000000..0a6d736d --- /dev/null +++ b/src/client/mac/testapp/TestClass.h @@ -0,0 +1,37 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import + +@interface TestClass : NSObject { +} + +- (void)wait; + +@end diff --git a/src/client/mac/testapp/TestClass.mm b/src/client/mac/testapp/TestClass.mm new file mode 100644 index 00000000..cade633e --- /dev/null +++ b/src/client/mac/testapp/TestClass.mm @@ -0,0 +1,95 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#import "TestClass.h" + +struct AStruct { + int x; + float y; + double z; +}; + +class InternalTestClass { + public: + InternalTestClass(int a) : a_(a) {} + ~InternalTestClass() {} + + void snooze(float a); + void snooze(int a); + int snooze(int a, float b); + + protected: + int a_; + AStruct s_; + + static void InternalFunction(AStruct &s); + static float kStaticFloatValue; +}; + +void InternalTestClass::snooze(float a) { + InternalFunction(s_); + sleep(a_ * a); +} + +void InternalTestClass::snooze(int a) { + InternalFunction(s_); + sleep(a_ * a); +} + +int InternalTestClass::snooze(int a, float b) { + InternalFunction(s_); + sleep(a_ * a * b); + + return 33; +} + +void InternalTestClass::InternalFunction(AStruct &s) { + s.x = InternalTestClass::kStaticFloatValue; +} + +float InternalTestClass::kStaticFloatValue = 42; + +static float PlainOldFunction() { + return 3.14145; +} + +@implementation TestClass + +- (void)wait { + InternalTestClass t(10); + float z = PlainOldFunction(); + + while (1) { + t.snooze(z); + } +} + +@end diff --git a/src/client/mac/testapp/bomb.icns b/src/client/mac/testapp/bomb.icns new file mode 100644 index 0000000000000000000000000000000000000000..c360dbf618946920498958680eddb253a1ff677f GIT binary patch literal 23659 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 literal 0 HcmV?d00001 diff --git a/src/client/mac/testapp/crashInMain b/src/client/mac/testapp/crashInMain new file mode 100755 index 0000000000000000000000000000000000000000..03bb3172769307065261123d239ca253e4dbf1cf GIT binary patch literal 12588 zcmeI3&ubG=5XaxPtA<+JAU%kR28kz2qTs=UhejH-*dL*dP;BLOlWfzp`9-oREeHmd z9>P-e;6ZQV#j6Joo}^kqMD%YcXd#CRJp`mZSiiHoY0{=jPeNgaPTsuv@!s^4J#=CE zZTsi%6GDh~XdAQ>x`NFTRt0(kDrF>eJA6MpI)Y7Iymg~>rMeZUl+o~bv?)$C^*XF{ z?=dLdOCqJAFBp#P?Dy7BT@>Ou0#n_M8arDRWqZYkl<}fbNM)0*V}B1@I73Bj3uiP- zy$A9&`tJ`#Lrqye*!ADR?Z3{AvZPFChDMenQnuFjPzbfn_qplzf-@G=iH2wY89hEB z&LiCOk29$Mhfpa8h9V=Qjc?#8oTw8%XC?lmlush%Y$=nMh89cf&c3z2g91Jwj??Oj z$3bd;mvol%%nS|;-wpQ<)Nk)YfbK>Qy+~@l7hapk88Q&3XYd);r&v!yBTy1zO-i-Y z$KMztB%YxDpm-Nz#EmWqX7Iu#4L$j`w?J#4=#e>Gpdy?OSO-0XN6yhP6^^+3Oy z7kXHs=k*|=yRSp(E~iI2S!wixo|5snEC#Lx&{`^n=>8f-$+gssEfx(u6S%cogbWrY zzyz286JP>NfC(@GCcp&#AAwm>SvP-dnj2p0fm)gLi{&9tWzeshtIGTDZORw8JXX88 zsxGaReqqyD?L~8ye!r+{c-N>^snzkF*K=5`GX2fJv*!4$vb1;iD(hqBhTnEhdc}wUtKn@z*$lK)zdiB{OF(fym~odzsgbe9-$W;@7iMzlwNi^eL{Gk zDpZiNFaajO1egF5U;<2l2`~XBzyz286JP>IjzDc!j$G^9FcV+`On?b60Vco%m;e)C z0!)AjFaaiTNCGr}d;#OfFEFc2^Uq|zfgOO|g<)&2Pl!I)-LPpE;Erq4OhCl7X)a(A zb~_CW;3x0;X=Y%NY@GQ4XFhUmnu}h7tq8>)f3})z8cLvNcQKPDZCEU2&>u)M2G_ zFF@&B5-Ck}(X89``{VWc4IwN9^v-6Do$VDX_KJ&?$)ag!xwPXr*~32e=s;{Ad$^_1 z1NoZ$4@M>dQR>U2nx}S zaNr+%Q2$F%DThX4W8=*?a2rn4iC}#t{-l&oBISH3Taacvp{w=#*80m72nunLR!4k7 zc*&%9Nqg0QGs8op52J%ajnfAZptI3KFOuBn!fW%mP6p!bSk|$=!g>W7gOU(yQmUms z{>Bg?@dEXy#p&G>KRTnYR5bc@Ez##7?}Cqd@GkefUwZTP!|P|a3NP3DJL$Qo2YNO? z^sGX^*VBZ~4ngTGyGIY5+$9ovJTtGQ)0$bf`@8GTu}@c;J)`e*GAWDUJ7Ki1C6G(| zx@#0A56|(MiE`0Yv*CM3MaW=b0!)AjFaajO1egF5U;<3w{}Y%Om0fG+hqdRgKK52- zLt<^jR~Zg@txe_Qw>ISqT)wKu+Vrk&lz!r-z1E8+DuW?Wo5FX;L5*6PK71#!R9BL? z9&Xot)s+eRZmP07VeJKM=ggXQ4bF-;RO?2y^(ip(c{8KxdcHUKG;ipsn~1gwec<@F zpZlQnvC|$D!Uy$21t|*?U;<2l2`~XBzyz286JP>NfC(@GCUEWu9308HYn>Zr0!)Aj zFaajO1egF5U;<2l2`~XBzy!`nfaZ^TFxdPSv&uAuO!fxsFzgNtTl<4T48ZP$O|t+G z9GhkWVvbF70kg0@G%$d_g5#%|fil_Ha|?SeJ2uTlufkS@LgU<`>auCBn`XX)f`+nX z(JUooLd&Hv(JwK)P*4pTv!~EpDVLObKL4atsOQRTJefgKJVi;P(-9+`bk+qF$%2tj t=i*rvb~Yo`MKxJ6)uVhhx1bsMoWL$xP87|!VG1N^xEApL1m>I&e*k;r-N*m{ literal 0 HcmV?d00001 diff --git a/src/client/mac/testapp/main.m b/src/client/mac/testapp/main.m new file mode 100644 index 00000000..1ed19bf9 --- /dev/null +++ b/src/client/mac/testapp/main.m @@ -0,0 +1,34 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import + +int main(int argc, char *argv[]) { + return NSApplicationMain(argc, (const char **) argv); +} diff --git a/src/client/mac/tests/SimpleStringDictionaryTest.h b/src/client/mac/tests/SimpleStringDictionaryTest.h new file mode 100644 index 00000000..1f48517f --- /dev/null +++ b/src/client/mac/tests/SimpleStringDictionaryTest.h @@ -0,0 +1,40 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import +#import "SimpleStringDictionary.h" + +@interface SimpleStringDictionaryTest : GTMTestCase { + +} + +- (void)testKeyValueEntry; +- (void)testSimpleStringDictionary; +- (void)testSimpleStringDictionaryIterator; +@end diff --git a/src/client/mac/tests/SimpleStringDictionaryTest.mm b/src/client/mac/tests/SimpleStringDictionaryTest.mm new file mode 100644 index 00000000..94179cd2 --- /dev/null +++ b/src/client/mac/tests/SimpleStringDictionaryTest.mm @@ -0,0 +1,243 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import "SimpleStringDictionaryTest.h" +#import "SimpleStringDictionary.h" + +using google_breakpad::KeyValueEntry; +using google_breakpad::SimpleStringDictionary; +using google_breakpad::SimpleStringDictionaryIterator; + +@implementation SimpleStringDictionaryTest + +//============================================================================== +- (void)testKeyValueEntry { + KeyValueEntry entry; + + // Verify that initial state is correct + STAssertFalse(entry.IsActive(), @"Initial key value entry is active!"); + STAssertEquals(strlen(entry.GetKey()), (size_t)0, @"Empty key value did not " + @"have length 0"); + STAssertEquals(strlen(entry.GetValue()), (size_t)0, @"Empty key value did not " + @"have length 0"); + + // Try setting a key/value and then verify + entry.SetKeyValue("key1", "value1"); + STAssertEqualCStrings(entry.GetKey(), "key1", @"key was not equal to key1"); + STAssertEqualCStrings(entry.GetValue(), "value1", @"value was not equal"); + + // Try setting a new value + entry.SetValue("value3"); + + // Make sure the new value took + STAssertEqualCStrings(entry.GetValue(), "value3", @"value was not equal"); + + // Make sure the key didn't change + STAssertEqualCStrings(entry.GetKey(), "key1", @"key changed after setting " + @"value!"); + + // Try setting a new key/value and then verify + entry.SetKeyValue("key2", "value2"); + STAssertEqualCStrings(entry.GetKey(), "key2", @"New key was not equal to " + @"key2"); + STAssertEqualCStrings(entry.GetValue(), "value2", @"New value was not equal " + @"to value2"); + + // Clear the entry and verify the key and value are empty strings + entry.Clear(); + STAssertFalse(entry.IsActive(), @"Key value clear did not clear object"); + STAssertEquals(strlen(entry.GetKey()), (size_t)0, @"Length of cleared key " + @"was not 0"); + STAssertEquals(strlen(entry.GetValue()), (size_t)0, @"Length of cleared " + @"value was not 0!"); +} + +- (void)testEmptyKeyValueCombos { + KeyValueEntry entry; + entry.SetKeyValue(NULL, NULL); + STAssertEqualCStrings(entry.GetKey(), "", @"Setting NULL key did not return " + @"empty key!"); + STAssertEqualCStrings(entry.GetValue(), "", @"Setting NULL value did not " + @"set empty string value!"); +} + + +//============================================================================== +- (void)testSimpleStringDictionary { + // Make a new dictionary + SimpleStringDictionary *dict = new SimpleStringDictionary(); + STAssertTrue(dict != NULL, nil); + + // try passing in NULL for key + //dict->SetKeyValue(NULL, "bad"); // causes assert() to fire + + // Set three distinct values on three keys + dict->SetKeyValue("key1", "value1"); + dict->SetKeyValue("key2", "value2"); + dict->SetKeyValue("key3", "value3"); + + STAssertTrue(!strcmp(dict->GetValueForKey("key1"), "value1"), nil); + STAssertTrue(!strcmp(dict->GetValueForKey("key2"), "value2"), nil); + STAssertTrue(!strcmp(dict->GetValueForKey("key3"), "value3"), nil); + STAssertEquals(dict->GetCount(), 3, @"GetCount did not return 3"); + // try an unknown key + STAssertTrue(dict->GetValueForKey("key4") == NULL, nil); + + // try a NULL key + //STAssertTrue(dict->GetValueForKey(NULL) == NULL, nil); // asserts + + // Remove a key + dict->RemoveKey("key3"); + + // Now make sure it's not there anymore + STAssertTrue(dict->GetValueForKey("key3") == NULL, nil); + + // Remove a NULL key + //dict->RemoveKey(NULL); // will cause assert() to fire + + // Remove by setting value to NULL + dict->SetKeyValue("key2", NULL); + + // Now make sure it's not there anymore + STAssertTrue(dict->GetValueForKey("key2") == NULL, nil); +} + +//============================================================================== +// The idea behind this test is to add a bunch of values to the dictionary, +// remove some in the middle, then add a few more in. We then create a +// SimpleStringDictionaryIterator and iterate through the dictionary, taking +// note of the key/value pairs we see. We then verify that it iterates +// through exactly the number of key/value pairs we expect, and that they +// match one-for-one with what we would expect. In all cases we're setting +// key value pairs of the form: +// +// key/value (like key0/value0, key17,value17, etc.) +// +- (void)testSimpleStringDictionaryIterator { + SimpleStringDictionary *dict = new SimpleStringDictionary(); + STAssertTrue(dict != NULL, nil); + + char key[KeyValueEntry::MAX_STRING_STORAGE_SIZE]; + char value[KeyValueEntry::MAX_STRING_STORAGE_SIZE]; + + const int kDictionaryCapacity = SimpleStringDictionary::MAX_NUM_ENTRIES; + const int kPartitionIndex = kDictionaryCapacity - 5; + + // We assume at least this size in the tests below + STAssertTrue(kDictionaryCapacity >= 64, nil); + + // We'll keep track of the number of key/value pairs we think should + // be in the dictionary + int expectedDictionarySize = 0; + + // Set a bunch of key/value pairs like key0/value0, key1/value1, ... + for (int i = 0; i < kPartitionIndex; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize = kPartitionIndex; + + // set a couple of the keys twice (with the same value) - should be nop + dict->SetKeyValue("key2", "value2"); + dict->SetKeyValue("key4", "value4"); + dict->SetKeyValue("key15", "value15"); + + // Remove some random elements in the middle + dict->RemoveKey("key7"); + dict->RemoveKey("key18"); + dict->RemoveKey("key23"); + dict->RemoveKey("key31"); + expectedDictionarySize -= 4; // we just removed four key/value pairs + + // Set some more key/value pairs like key59/value59, key60/value60, ... + for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize += kDictionaryCapacity - kPartitionIndex; + + // Now create an iterator on the dictionary + SimpleStringDictionaryIterator iter(*dict); + + // We then verify that it iterates through exactly the number of + // key/value pairs we expect, and that they match one-for-one with what we + // would expect. The ordering of the iteration does not matter... + + // used to keep track of number of occurrences found for key/value pairs + int count[kDictionaryCapacity]; + memset(count, 0, sizeof(count)); + + int totalCount = 0; + + const KeyValueEntry *entry; + + while ((entry = iter.Next())) { + totalCount++; + + // Extract keyNumber from a string of the form key + int keyNumber; + sscanf(entry->GetKey(), "key%d", &keyNumber); + + // Extract valueNumber from a string of the form value + int valueNumber; + sscanf(entry->GetValue(), "value%d", &valueNumber); + + // The value number should equal the key number since that's how we set them + STAssertTrue(keyNumber == valueNumber, nil); + + // Key and value numbers should be in proper range: + // 0 <= keyNumber < kDictionaryCapacity + bool isKeyInGoodRange = + (keyNumber >= 0 && keyNumber < kDictionaryCapacity); + bool isValueInGoodRange = + (valueNumber >= 0 && valueNumber < kDictionaryCapacity); + STAssertTrue(isKeyInGoodRange, nil); + STAssertTrue(isValueInGoodRange, nil); + + if (isKeyInGoodRange && isValueInGoodRange) { + ++count[keyNumber]; + } + } + + // Make sure each of the key/value pairs showed up exactly one time, except + // for the ones which we removed. + for (int i = 0; i < kDictionaryCapacity; ++i) { + // Skip over key7, key18, key23, and key31, since we removed them + if (!(i == 7 || i == 18 || i == 23 || i == 31)) { + STAssertTrue(count[i] == 1, nil); + } + } + + // Make sure the number of iterations matches the expected dictionary size. + STAssertTrue(totalCount == expectedDictionarySize, nil); +} + +@end diff --git a/src/client/minidump_file_writer_unittest.cc b/src/client/minidump_file_writer_unittest.cc index 8eadff46..a83307d9 100644 --- a/src/client/minidump_file_writer_unittest.cc +++ b/src/client/minidump_file_writer_unittest.cc @@ -126,17 +126,22 @@ static bool CompareFile(const char *path) { 0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009, 0x0000000a, 0x0900000a, 0x0000000b, 0x00000000 #else - 0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000, - 0x00000018, 0x00690046, 0x00730072, 0x00200074, 0x00740053, 0x00690072, - 0x0067006e, 0x00000000, 0x0000001a, 0x00650053, 0x006f0063, 0x0064006e, - 0x00530020, 0x00720074, 0x006e0069, 0x00000067, 0x0001da00, 0x00000002, - 0x0002da01, 0x00000003, 0x0003da02, 0x00000004, 0x0004da03, 0x00000005, - 0x0005da04, 0x00000006, 0x0006da05, 0x00000007, 0x0007da06, 0x00000008, - 0x0008da07, 0x00000009, 0x0009da08, 0x0000000a, 0x000ada09, 0x0000000b, - 0x0000000a, 0x00018700, 0x00000002, 0x00028701, 0x00000003, 0x00038702, - 0x00000004, 0x00048703, 0x00000005, 0x00058704, 0x00000006, 0x00068705, - 0x00000007, 0x00078706, 0x00000008, 0x00088707, 0x00000009, 0x00098708, - 0x0000000a, 0x000a8709, 0x0000000b, 0x00000000, + 0x0000beef, 0x0000001e, 0x00000018, 0x00000020, + 0x00000038, 0x00000000, 0x00000018, 0x00690046, + 0x00730072, 0x00200074, 0x00740053, 0x00690072, + 0x0067006e, 0x00000000, 0x0000001a, 0x00650053, + 0x006f0063, 0x0064006e, 0x00530020, 0x00720074, + 0x006e0069, 0x00000067, 0x00011e00, 0x00000002, + 0x00021e01, 0x00000003, 0x00031e02, 0x00000004, + 0x00041e03, 0x00000005, 0x00051e04, 0x00000006, + 0x00061e05, 0x00000007, 0x00071e06, 0x00000008, + 0x00081e07, 0x00000009, 0x00091e08, 0x0000000a, + 0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00, + 0x00000002, 0x00021c01, 0x00000003, 0x00031c02, + 0x00000004, 0x00041c03, 0x00000005, 0x00051c04, + 0x00000006, 0x00061c05, 0x00000007, 0x00071c06, + 0x00000008, 0x00081c07, 0x00000009, 0x00091c08, + 0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000, #endif }; unsigned int expected_byte_count = sizeof(expected); @@ -156,7 +161,6 @@ static bool CompareFile(const char *path) { printf("%d\n",b1 - (char*)buffer); - ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0); return true; } diff --git a/src/common/mac/GTMDefines.h b/src/common/mac/GTMDefines.h new file mode 100644 index 00000000..b88193cd --- /dev/null +++ b/src/common/mac/GTMDefines.h @@ -0,0 +1,241 @@ +// +// GTMDefines.h +// +// Copyright 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// ============================================================================ + +#include +#include + +// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs +#ifndef MAC_OS_X_VERSION_10_5 + #define MAC_OS_X_VERSION_10_5 1050 +#endif +#ifndef MAC_OS_X_VERSION_10_6 + #define MAC_OS_X_VERSION_10_6 1060 +#endif + +// ---------------------------------------------------------------------------- +// CPP symbols that can be overridden in a prefix to control how the toolbox +// is compiled. +// ---------------------------------------------------------------------------- + + +// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and +// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens +// when a validation fails. If you implement your own validators, you may want +// to control their internals using the same macros for consistency. +#ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT + #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0 +#endif + +// Give ourselves a consistent way to do inlines. Apple's macros even use +// a few different actual definitions, so we're based off of the foundation +// one. +#if !defined(GTM_INLINE) + #if defined (__GNUC__) && (__GNUC__ == 4) + #define GTM_INLINE static __inline__ __attribute__((always_inline)) + #else + #define GTM_INLINE static __inline__ + #endif +#endif + +// Give ourselves a consistent way of doing externs that links up nicely +// when mixing objc and objc++ +#if !defined (GTM_EXTERN) + #if defined __cplusplus + #define GTM_EXTERN extern "C" + #else + #define GTM_EXTERN extern + #endif +#endif + +// Give ourselves a consistent way of exporting things if we have visibility +// set to hidden. +#if !defined (GTM_EXPORT) + #define GTM_EXPORT __attribute__((visibility("default"))) +#endif + +// _GTMDevLog & _GTMDevAssert +// +// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for +// developer level errors. This implementation simply macros to NSLog/NSAssert. +// It is not intended to be a general logging/reporting system. +// +// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert +// for a little more background on the usage of these macros. +// +// _GTMDevLog log some error/problem in debug builds +// _GTMDevAssert assert if conditon isn't met w/in a method/function +// in all builds. +// +// To replace this system, just provide different macro definitions in your +// prefix header. Remember, any implementation you provide *must* be thread +// safe since this could be called by anything in what ever situtation it has +// been placed in. +// + +// We only define the simple macros if nothing else has defined this. +#ifndef _GTMDevLog + +#ifdef DEBUG + #define _GTMDevLog(...) NSLog(__VA_ARGS__) +#else + #define _GTMDevLog(...) do { } while (0) +#endif + +#endif // _GTMDevLog + +// Declared here so that it can easily be used for logging tracking if +// necessary. See GTMUnitTestDevLog.h for details. +@class NSString; +GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...); + +#ifndef _GTMDevAssert +// we directly invoke the NSAssert handler so we can pass on the varargs +// (NSAssert doesn't have a macro we can use that takes varargs) +#if !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) \ + do { \ + if (!(condition)) { \ + [[NSAssertionHandler currentHandler] \ + handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ + file:[NSString stringWithUTF8String:__FILE__] \ + lineNumber:__LINE__ \ + description:__VA_ARGS__]; \ + } \ + } while(0) +#else // !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) do { } while (0) +#endif // !defined(NS_BLOCK_ASSERTIONS) + +#endif // _GTMDevAssert + +// _GTMCompileAssert +// _GTMCompileAssert is an assert that is meant to fire at compile time if you +// want to check things at compile instead of runtime. For example if you +// want to check that a wchar is 4 bytes instead of 2 you would use +// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X) +// Note that the second "arg" is not in quotes, and must be a valid processor +// symbol in it's own right (no spaces, punctuation etc). + +// Wrapping this in an #ifndef allows external groups to define their own +// compile time assert scheme. +#ifndef _GTMCompileAssert + // We got this technique from here: + // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html + + #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg + #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg) + #define _GTMCompileAssert(test, msg) \ + typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] +#endif // _GTMCompileAssert + +// Macro to allow fast enumeration when building for 10.5 or later, and +// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration +// does keys, so pick the right thing, nothing is done on the FastEnumeration +// side to be sure you're getting what you wanted. +#ifndef GTM_FOREACH_OBJECT + #if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + #define GTM_FOREACH_OBJECT(element, collection) \ + for (element in collection) + #define GTM_FOREACH_KEY(element, collection) \ + for (element in collection) + #else + #define GTM_FOREACH_OBJECT(element, collection) \ + for (NSEnumerator * _ ## element ## _enum = [collection objectEnumerator]; \ + (element = [_ ## element ## _enum nextObject]) != nil; ) + #define GTM_FOREACH_KEY(element, collection) \ + for (NSEnumerator * _ ## element ## _enum = [collection keyEnumerator]; \ + (element = [_ ## element ## _enum nextObject]) != nil; ) + #endif +#endif + +// ============================================================================ + +// ---------------------------------------------------------------------------- +// CPP symbols defined based on the project settings so the GTM code has +// simple things to test against w/o scattering the knowledge of project +// setting through all the code. +// ---------------------------------------------------------------------------- + +// Provide a single constant CPP symbol that all of GTM uses for ifdefing +// iPhone code. +#if TARGET_OS_IPHONE // iPhone SDK + // For iPhone specific stuff + #define GTM_IPHONE_SDK 1 + #if TARGET_IPHONE_SIMULATOR + #define GTM_IPHONE_SIMULATOR 1 + #else + #define GTM_IPHONE_DEVICE 1 + #endif // TARGET_IPHONE_SIMULATOR +#else + // For MacOS specific stuff + #define GTM_MACOS_SDK 1 +#endif + +// Provide a symbol to include/exclude extra code for GC support. (This mainly +// just controls the inclusion of finalize methods). +#ifndef GTM_SUPPORT_GC + #if GTM_IPHONE_SDK + // iPhone never needs GC + #define GTM_SUPPORT_GC 0 + #else + // We can't find a symbol to tell if GC is supported/required, so best we + // do on Mac targets is include it if we're on 10.5 or later. + #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + #define GTM_SUPPORT_GC 0 + #else + #define GTM_SUPPORT_GC 1 + #endif + #endif +#endif + +// To simplify support for 64bit (and Leopard in general), we provide the type +// defines for non Leopard SDKs +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + // NSInteger/NSUInteger and Max/Mins + #ifndef NSINTEGER_DEFINED + #if __LP64__ || NS_BUILD_32_LIKE_64 + typedef long NSInteger; + typedef unsigned long NSUInteger; + #else + typedef int NSInteger; + typedef unsigned int NSUInteger; + #endif + #define NSIntegerMax LONG_MAX + #define NSIntegerMin LONG_MIN + #define NSUIntegerMax ULONG_MAX + #define NSINTEGER_DEFINED 1 + #endif // NSINTEGER_DEFINED + // CGFloat + #ifndef CGFLOAT_DEFINED + #if defined(__LP64__) && __LP64__ + // This really is an untested path (64bit on Tiger?) + typedef double CGFloat; + #define CGFLOAT_MIN DBL_MIN + #define CGFLOAT_MAX DBL_MAX + #define CGFLOAT_IS_DOUBLE 1 + #else /* !defined(__LP64__) || !__LP64__ */ + typedef float CGFloat; + #define CGFLOAT_MIN FLT_MIN + #define CGFLOAT_MAX FLT_MAX + #define CGFLOAT_IS_DOUBLE 0 + #endif /* !defined(__LP64__) || !__LP64__ */ + #define CGFLOAT_DEFINED 1 + #endif // CGFLOAT_DEFINED +#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 diff --git a/src/common/mac/GTMGarbageCollection.h b/src/common/mac/GTMGarbageCollection.h new file mode 100644 index 00000000..93d4efab --- /dev/null +++ b/src/common/mac/GTMGarbageCollection.h @@ -0,0 +1,72 @@ +// +// GTMGarbageCollection.h +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import + +#import "GTMDefines.h" + +// This allows us to easily move our code from GC to non GC. +// They are no-ops unless we are require Leopard or above. +// See +// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/index.html +// and +// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/Articles/gcCoreFoundation.html#//apple_ref/doc/uid/TP40006687-SW1 +// for details. + +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) && !GTM_IPHONE_SDK +// General use would be to call this through GTMCFAutorelease +// but there may be a reason the you want to make something collectable +// but not autoreleased, especially in pure GC code where you don't +// want to bother with the nop autorelease. Done as a define instead of an +// inline so that tools like Clang's scan-build don't report code as leaking. +#define GTMNSMakeCollectable(cf) ((id)NSMakeCollectable(cf)) + +// GTMNSMakeUncollectable is for global maps, etc. that we don't +// want released ever. You should still retain these in non-gc code. +GTM_INLINE void GTMNSMakeUncollectable(id object) { + [[NSGarbageCollector defaultCollector] disableCollectorForPointer:object]; +} + +// Hopefully no code really needs this, but GTMIsGarbageCollectionEnabled is +// a common way to check at runtime if GC is on. +// There are some places where GC doesn't work w/ things w/in Apple's +// frameworks, so this is here so GTM unittests and detect it, and not run +// individual tests to work around bugs in Apple's frameworks. +GTM_INLINE BOOL GTMIsGarbageCollectionEnabled(void) { + return ([NSGarbageCollector defaultCollector] != nil); +} + +#else + +#define GTMNSMakeCollectable(cf) ((id)(cf)) + +GTM_INLINE void GTMNSMakeUncollectable(id object) { +} + +GTM_INLINE BOOL GTMIsGarbageCollectionEnabled(void) { + return NO; +} + +#endif + +// GTMCFAutorelease makes a CF object collectable in GC mode, or adds it +// to the autorelease pool in non-GC mode. Either way it is taken care +// of. Done as a define instead of an inline so that tools like Clang's +// scan-build don't report code as leaking. +#define GTMCFAutorelease(cf) ([GTMNSMakeCollectable(cf) autorelease]) + diff --git a/src/common/mac/GTMLogger.h b/src/common/mac/GTMLogger.h new file mode 100644 index 00000000..1626b1b6 --- /dev/null +++ b/src/common/mac/GTMLogger.h @@ -0,0 +1,458 @@ +// +// GTMLogger.h +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// Key Abstractions +// ---------------- +// +// This file declares multiple classes and protocols that are used by the +// GTMLogger logging system. The 4 main abstractions used in this file are the +// following: +// +// * logger (GTMLogger) - The main logging class that users interact with. It +// has methods for logging at different levels and uses a log writer, a log +// formatter, and a log filter to get the job done. +// +// * log writer (GTMLogWriter) - Writes a given string to some log file, where +// a "log file" can be a physical file on disk, a POST over HTTP to some URL, +// or even some in-memory structure (e.g., a ring buffer). +// +// * log formatter (GTMLogFormatter) - Given a format string and arguments as +// a va_list, returns a single formatted NSString. A "formatted string" could +// be a string with the date prepended, a string with values in a CSV format, +// or even a string of XML. +// +// * log filter (GTMLogFilter) - Given a formatted log message as an NSString +// and the level at which the message is to be logged, this class will decide +// whether the given message should be logged or not. This is a flexible way +// to filter out messages logged at a certain level, messages that contain +// certain text, or filter nothing out at all. This gives the caller the +// flexibility to dynamically enable debug logging in Release builds. +// +// This file also declares some classes to handle the common log writer, log +// formatter, and log filter cases. Callers can also create their own writers, +// formatters, and filters and they can even build them on top of the ones +// declared here. Keep in mind that your custom writer/formatter/filter may be +// called from multiple threads, so it must be thread-safe. + +#import + +// Predeclaration of used protocols that are declared later in this file. +@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter; + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 +#define CHECK_FORMAT_NSSTRING(a, b) __attribute__((format(__NSString__, a, b))) +#else +#define CHECK_FORMAT_NSSTRING(a, b) +#endif + +// GTMLogger +// +// GTMLogger is the primary user-facing class for an object-oriented logging +// system. It is built on the concept of log formatters (GTMLogFormatter), log +// writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is +// sent to a GTMLogger to log a message, the message is formatted using the log +// formatter, then the log filter is consulted to see if the message should be +// logged, and if so, the message is sent to the log writer to be written out. +// +// GTMLogger is intended to be a flexible and thread-safe logging solution. Its +// flexibility comes from the fact that GTMLogger instances can be customized +// with user defined formatters, filters, and writers. And these writers, +// filters, and formatters can be combined, stacked, and customized in arbitrary +// ways to suit the needs at hand. For example, multiple writers can be used at +// the same time, and a GTMLogger instance can even be used as another +// GTMLogger's writer. This allows for arbitrarily deep logging trees. +// +// A standard GTMLogger uses a writer that sends messages to standard out, a +// formatter that smacks a timestamp and a few other bits of interesting +// information on the message, and a filter that filters out debug messages from +// release builds. Using the standard log settings, a log message will look like +// the following: +// +// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo= +// +// The output contains the date and time of the log message, the name of the +// process followed by its process ID/thread ID, the log level at which the +// message was logged (in the previous example the level was 1: +// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in +// this case, the log message was @"foo=%@", foo). +// +// Multiple instances of GTMLogger can be created, each configured their own +// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide +// access to a shared (i.e., globally accessible) GTMLogger instance. This makes +// it convenient for all code in a process to use the same GTMLogger instance. +// The shared GTMLogger instance can also be configured in an arbitrary, and +// these configuration changes will affect all code that logs through the shared +// instance. + +// +// Log Levels +// ---------- +// GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger +// doesn't take any special action based on the log level; it simply forwards +// this information on to formatters, filters, and writers, each of which may +// optionally take action based on the level. Since log level filtering is +// performed at runtime, log messages are typically not filtered out at compile +// time. The exception to this rule is that calls to the GTMLoggerDebug() macro +// *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible +// with behavior that many developers are currently used to. Note that this +// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but +// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out. +// +// Standard loggers are created with the GTMLogLevelFilter log filter, which +// filters out certain log messages based on log level, and some other settings. +// +// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on +// GTMLogger itself, there are also C macros that make usage of the shared +// GTMLogger instance very convenient. These macros are: +// +// GTMLoggerDebug(...) +// GTMLoggerInfo(...) +// GTMLoggerError(...) +// +// Again, a notable feature of these macros is that GTMLogDebug() calls *will be +// compiled out of non-DEBUG builds*. +// +// Standard Loggers +// ---------------- +// GTMLogger has the concept of "standard loggers". A standard logger is simply +// a logger that is pre-configured with some standard/common writer, formatter, +// and filter combination. Standard loggers are created using the creation +// methods beginning with "standard". The alternative to a standard logger is a +// regular logger, which will send messages to stdout, with no special +// formatting, and no filtering. +// +// How do I use GTMLogger? +// ---------------------- +// The typical way you will want to use GTMLogger is to simply use the +// GTMLogger*() macros for logging from code. That way we can easily make +// changes to the GTMLogger class and simply update the macros accordingly. Only +// your application startup code (perhaps, somewhere in main()) should use the +// GTMLogger class directly in order to configure the shared logger, which all +// of the code using the macros will be using. Again, this is just the typical +// situation. +// +// To be complete, there are cases where you may want to use GTMLogger directly, +// or even create separate GTMLogger instances for some reason. That's fine, +// too. +// +// Examples +// -------- +// The following show some common GTMLogger use cases. +// +// 1. You want to log something as simply as possible. Also, this call will only +// appear in debug builds. In non-DEBUG builds it will be completely removed. +// +// GTMLoggerDebug(@"foo = %@", foo); +// +// 2. The previous example is similar to the following. The major difference is +// that the previous call (example 1) will be compiled out of Release builds +// but this statement will not be compiled out. +// +// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo]; +// +// 3. Send all logging output from the shared logger to a file. We do this by +// creating an NSFileHandle for writing associated with a file, and setting +// that file handle as the logger's writer. +// +// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" +// create:YES]; +// [[GTMLogger sharedLogger] setWriter:f]; +// GTMLoggerError(@"hi"); // This will be sent to /tmp/f.log +// +// 4. Create a new GTMLogger that will log to a file. This example differs from +// the previous one because here we create a new GTMLogger that is different +// from the shared logger. +// +// GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"]; +// [logger logInfo:@"hi temp log file"]; +// +// 5. Create a logger that writes to stdout and does NOT do any formatting to +// the log message. This might be useful, for example, when writing a help +// screen for a command-line tool to standard output. +// +// GTMLogger *logger = [GTMLogger logger]; +// [logger logInfo:@"%@ version 0.1 usage", progName]; +// +// 6. Send log output to stdout AND to a log file. The trick here is that +// NSArrays function as composite log writers, which means when an array is +// set as the log writer, it forwards all logging messages to all of its +// contained GTMLogWriters. +// +// // Create array of GTMLogWriters +// NSArray *writers = [NSArray arrayWithObjects: +// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES], +// [NSFileHandle fileHandleWithStandardOutput], nil]; +// +// GTMLogger *logger = [GTMLogger standardLogger]; +// [logger setWriter:writers]; +// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log +// +// For futher details on log writers, formatters, and filters, see the +// documentation below. +// +// NOTE: GTMLogger is application level logging. By default it does nothing +// with _GTMDevLog/_GTMDevAssert (see GTMDefines.h). An application can choose +// to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro +// definitions in its prefix header (see GTMDefines.h for how one would do +// that). +// +@interface GTMLogger : NSObject { + @private + id writer_; + id formatter_; + id filter_; +} + +// +// Accessors for the shared logger instance +// + +// Returns a shared/global standard GTMLogger instance. Callers should typically +// use this method to get a GTMLogger instance, unless they explicitly want +// their own instance to configure for their own needs. This is the only method +// that returns a shared instance; all the rest return new GTMLogger instances. ++ (id)sharedLogger; + +// Sets the shared logger instance to |logger|. Future calls to +sharedLogger +// will return |logger| instead. ++ (void)setSharedLogger:(GTMLogger *)logger; + +// +// Creation methods +// + +// Returns a new autoreleased GTMLogger instance that will log to stdout, using +// the GTMLogStandardFormatter, and the GTMLogLevelFilter filter. ++ (id)standardLogger; + +// Same as +standardLogger, but logs to stderr. ++ (id)standardLoggerWithStderr; + +// Returns a new standard GTMLogger instance with a log writer that will +// write to the file at |path|, and will use the GTMLogStandardFormatter and +// GTMLogLevelFilter classes. If |path| does not exist, it will be created. ++ (id)standardLoggerWithPath:(NSString *)path; + +// Returns an autoreleased GTMLogger instance that will use the specified +// |writer|, |formatter|, and |filter|. ++ (id)loggerWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter; + +// Returns an autoreleased GTMLogger instance that logs to stdout, with the +// basic formatter, and no filter. The returned logger differs from the logger +// returned by +standardLogger because this one does not do any filtering and +// does not do any special log formatting; this is the difference between a +// "regular" logger and a "standard" logger. ++ (id)logger; + +// Designated initializer. This method returns a GTMLogger initialized with the +// specified |writer|, |formatter|, and |filter|. See the setter methods below +// for what values will be used if nil is passed for a parameter. +- (id)initWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter; + +// +// Logging methods +// + +// Logs a message at the debug level (kGTMLoggerLevelDebug). +- (void)logDebug:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); +// Logs a message at the info level (kGTMLoggerLevelInfo). +- (void)logInfo:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); +// Logs a message at the error level (kGTMLoggerLevelError). +- (void)logError:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); +// Logs a message at the assert level (kGTMLoggerLevelAssert). +- (void)logAssert:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); + + +// +// Accessors +// + +// Accessor methods for the log writer. If the log writer is set to nil, +// [NSFileHandle fileHandleWithStandardOutput] is used. +- (id)writer; +- (void)setWriter:(id)writer; + +// Accessor methods for the log formatter. If the log formatter is set to nil, +// GTMLogBasicFormatter is used. This formatter will format log messages in a +// plain printf style. +- (id)formatter; +- (void)setFormatter:(id)formatter; + +// Accessor methods for the log filter. If the log filter is set to nil, +// GTMLogNoFilter is used, which allows all log messages through. +- (id)filter; +- (void)setFilter:(id)filter; + +@end // GTMLogger + + +// Helper functions that are used by the convenience GTMLogger*() macros that +// enable the logging of function names. +@interface GTMLogger (GTMLoggerMacroHelpers) +- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +@end // GTMLoggerMacroHelpers + + +// Convenience macros that log to the shared GTMLogger instance. These macros +// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug() +// calls will be compiled out of non-Debug builds. +#define GTMLoggerDebug(...) \ + [[GTMLogger sharedLogger] logFuncDebug:__func__ msg:__VA_ARGS__] +#define GTMLoggerInfo(...) \ + [[GTMLogger sharedLogger] logFuncInfo:__func__ msg:__VA_ARGS__] +#define GTMLoggerError(...) \ + [[GTMLogger sharedLogger] logFuncError:__func__ msg:__VA_ARGS__] +#define GTMLoggerAssert(...) \ + [[GTMLogger sharedLogger] logFuncAssert:__func__ msg:__VA_ARGS__] + +// If we're not in a debug build, remove the GTMLoggerDebug statements. This +// makes calls to GTMLoggerDebug "compile out" of Release builds +#ifndef DEBUG +#undef GTMLoggerDebug +#define GTMLoggerDebug(...) do {} while(0) +#endif + +// Log levels. +typedef enum { + kGTMLoggerLevelUnknown, + kGTMLoggerLevelDebug, + kGTMLoggerLevelInfo, + kGTMLoggerLevelError, + kGTMLoggerLevelAssert, +} GTMLoggerLevel; + + +// +// Log Writers +// + +// Protocol to be implemented by a GTMLogWriter instance. +@protocol GTMLogWriter +// Writes the given log message to where the log writer is configured to write. +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level; +@end // GTMLogWriter + + +// Simple category on NSFileHandle that makes NSFileHandles valid log writers. +// This is convenient because something like, say, +fileHandleWithStandardError +// now becomes a valid log writer. Log messages are written to the file handle +// with a newline appended. +@interface NSFileHandle (GTMFileHandleLogWriter) +// Opens the file at |path| in append mode, and creates the file with |mode| +// if it didn't previously exist. ++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode; +@end // NSFileHandle + + +// This category makes NSArray a GTMLogWriter that can be composed of other +// GTMLogWriters. This is the classic Composite GoF design pattern. When the +// GTMLogWriter -logMessage:level: message is sent to the array, the array +// forwards the message to all of its elements that implement the GTMLogWriter +// protocol. +// +// This is useful in situations where you would like to send log output to +// multiple log writers at the same time. Simply create an NSArray of the log +// writers you wish to use, then set the array as the "writer" for your +// GTMLogger instance. +@interface NSArray (GTMArrayCompositeLogWriter) +@end // GTMArrayCompositeLogWriter + + +// This category adapts the GTMLogger interface so that it can be used as a log +// writer; it's an "adapter" in the GoF Adapter pattern sense. +// +// This is useful when you want to configure a logger to log to a specific +// writer with a specific formatter and/or filter. But you want to also compose +// that with a different log writer that may have its own formatter and/or +// filter. +@interface GTMLogger (GTMLoggerLogWriter) +@end // GTMLoggerLogWriter + + +// +// Log Formatters +// + +// Protocol to be implemented by a GTMLogFormatter instance. +@protocol GTMLogFormatter +// Returns a formatted string using the format specified in |fmt| and the va +// args specified in |args|. +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level; +@end // GTMLogFormatter + + +// A basic log formatter that formats a string the same way that NSLog (or +// printf) would. It does not do anything fancy, nor does it add any data of its +// own. +@interface GTMLogBasicFormatter : NSObject +@end // GTMLogBasicFormatter + + +// A log formatter that formats the log string like the basic formatter, but +// also prepends a timestamp and some basic process info to the message, as +// shown in the following sample output. +// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] log mesage here +@interface GTMLogStandardFormatter : GTMLogBasicFormatter { + @private + NSDateFormatter *dateFormatter_; // yyyy-MM-dd HH:mm:ss.SSS + NSString *pname_; + pid_t pid_; +} +@end // GTMLogStandardFormatter + + +// +// Log Filters +// + +// Protocol to be imlemented by a GTMLogFilter instance. +@protocol GTMLogFilter +// Returns YES if |msg| at |level| should be filtered out; NO otherwise. +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level; +@end // GTMLogFilter + + +// A log filter that filters messages at the kGTMLoggerLevelDebug level out of +// non-debug builds. Messages at the kGTMLoggerLevelInfo level are also filtered +// out of non-debug builds unless GTMVerboseLogging is set in the environment or +// the processes's defaults. Messages at the kGTMLoggerLevelError level are +// never filtered. +@interface GTMLogLevelFilter : NSObject +@end // GTMLogLevelFilter + + +// A simple log filter that does NOT filter anything out; +// -filterAllowsMessage:level will always return YES. This can be a convenient +// way to enable debug-level logging in release builds (if you so desire). +@interface GTMLogNoFilter : NSObject +@end // GTMLogNoFilter + diff --git a/src/common/mac/GTMLogger.m b/src/common/mac/GTMLogger.m new file mode 100644 index 00000000..de941d2e --- /dev/null +++ b/src/common/mac/GTMLogger.m @@ -0,0 +1,445 @@ +// +// GTMLogger.m +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMLogger.h" +#import "GTMGarbageCollection.h" +#import +#import +#import +#import + + +// Define a trivial assertion macro to avoid dependencies +#ifdef DEBUG + #define GTMLOGGER_ASSERT(expr) assert(expr) +#else + #define GTMLOGGER_ASSERT(expr) +#endif + + +@interface GTMLogger (PrivateMethods) + +- (void)logInternalFunc:(const char *)func + format:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level; + +@end + + +// Reference to the shared GTMLogger instance. This is not a singleton, it's +// just an easy reference to one shared instance. +static GTMLogger *gSharedLogger = nil; + + +@implementation GTMLogger + +// Returns a pointer to the shared logger instance. If none exists, a standard +// logger is created and returned. ++ (id)sharedLogger { + @synchronized(self) { + if (gSharedLogger == nil) { + gSharedLogger = [[self standardLogger] retain]; + } + GTMLOGGER_ASSERT(gSharedLogger != nil); + } + return [[gSharedLogger retain] autorelease]; +} + ++ (void)setSharedLogger:(GTMLogger *)logger { + @synchronized(self) { + [gSharedLogger autorelease]; + gSharedLogger = [logger retain]; + } +} + ++ (id)standardLogger { + id writer = [NSFileHandle fileHandleWithStandardOutput]; + id fr = [[[GTMLogStandardFormatter alloc] init] autorelease]; + id filter = [[[GTMLogLevelFilter alloc] init] autorelease]; + return [self loggerWithWriter:writer formatter:fr filter:filter]; +} + ++ (id)standardLoggerWithStderr { + id me = [self standardLogger]; + [me setWriter:[NSFileHandle fileHandleWithStandardError]]; + return me; +} + ++ (id)standardLoggerWithPath:(NSString *)path { + NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644]; + if (fh == nil) return nil; + id me = [self standardLogger]; + [me setWriter:fh]; + return me; +} + ++ (id)loggerWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter { + return [[[self alloc] initWithWriter:writer + formatter:formatter + filter:filter] autorelease]; +} + ++ (id)logger { + return [[[self alloc] init] autorelease]; +} + +- (id)init { + return [self initWithWriter:nil formatter:nil filter:nil]; +} + +- (id)initWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter { + if ((self = [super init])) { + [self setWriter:writer]; + [self setFormatter:formatter]; + [self setFilter:filter]; + GTMLOGGER_ASSERT(formatter_ != nil); + GTMLOGGER_ASSERT(filter_ != nil); + GTMLOGGER_ASSERT(writer_ != nil); + } + return self; +} + +- (void)dealloc { + GTMLOGGER_ASSERT(writer_ != nil); + GTMLOGGER_ASSERT(formatter_ != nil); + GTMLOGGER_ASSERT(filter_ != nil); + [writer_ release]; + [formatter_ release]; + [filter_ release]; + [super dealloc]; +} + +- (id)writer { + GTMLOGGER_ASSERT(writer_ != nil); + return [[writer_ retain] autorelease]; +} + +- (void)setWriter:(id)writer { + @synchronized(self) { + [writer_ autorelease]; + if (writer == nil) + writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain]; + else + writer_ = [writer retain]; + } + GTMLOGGER_ASSERT(writer_ != nil); +} + +- (id)formatter { + GTMLOGGER_ASSERT(formatter_ != nil); + return [[formatter_ retain] autorelease]; +} + +- (void)setFormatter:(id)formatter { + @synchronized(self) { + [formatter_ autorelease]; + if (formatter == nil) + formatter_ = [[GTMLogBasicFormatter alloc] init]; + else + formatter_ = [formatter retain]; + } + GTMLOGGER_ASSERT(formatter_ != nil); +} + +- (id)filter { + GTMLOGGER_ASSERT(filter_ != nil); + return [[filter_ retain] autorelease]; +} + +- (void)setFilter:(id)filter { + @synchronized(self) { + [filter_ autorelease]; + if (filter == nil) + filter_ = [[GTMLogNoFilter alloc] init]; + else + filter_ = [filter retain]; + } + GTMLOGGER_ASSERT(filter_ != nil); +} + +- (void)logDebug:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug]; + va_end(args); +} + +- (void)logInfo:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo]; + va_end(args); +} + +- (void)logError:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError]; + va_end(args); +} + +- (void)logAssert:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert]; + va_end(args); +} + +@end // GTMLogger + + +@implementation GTMLogger (GTMLoggerMacroHelpers) + +- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug]; + va_end(args); +} + +- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo]; + va_end(args); +} + +- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError]; + va_end(args); +} + +- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert]; + va_end(args); +} + +@end // GTMLoggerMacroHelpers + + +@implementation GTMLogger (PrivateMethods) + +- (void)logInternalFunc:(const char *)func + format:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + GTMLOGGER_ASSERT(formatter_ != nil); + GTMLOGGER_ASSERT(filter_ != nil); + GTMLOGGER_ASSERT(writer_ != nil); + + NSString *fname = func ? [NSString stringWithUTF8String:func] : nil; + NSString *msg = [formatter_ stringForFunc:fname + withFormat:fmt + valist:args + level:level]; + if (msg && [filter_ filterAllowsMessage:msg level:level]) + [writer_ logMessage:msg level:level]; +} + +@end // PrivateMethods + + +@implementation NSFileHandle (GTMFileHandleLogWriter) + ++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode { + int fd = -1; + if (path) { + int flags = O_WRONLY | O_APPEND | O_CREAT; + fd = open([path fileSystemRepresentation], flags, mode); + } + if (fd == -1) return nil; + return [[[self alloc] initWithFileDescriptor:fd + closeOnDealloc:YES] autorelease]; +} + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + @synchronized(self) { + NSString *line = [NSString stringWithFormat:@"%@\n", msg]; + [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; + } +} + +@end // GTMFileHandleLogWriter + + +@implementation NSArray (GTMArrayCompositeLogWriter) + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + @synchronized(self) { + id child = nil; + GTM_FOREACH_OBJECT(child, self) { + if ([child conformsToProtocol:@protocol(GTMLogWriter)]) + [child logMessage:msg level:level]; + } + } +} + +@end // GTMArrayCompositeLogWriter + + +@implementation GTMLogger (GTMLoggerLogWriter) + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + switch (level) { + case kGTMLoggerLevelDebug: + [self logDebug:@"%@", msg]; + break; + case kGTMLoggerLevelInfo: + [self logInfo:@"%@", msg]; + break; + case kGTMLoggerLevelError: + [self logError:@"%@", msg]; + break; + case kGTMLoggerLevelAssert: + [self logAssert:@"%@", msg]; + break; + default: + // Ignore the message. + break; + } +} + +@end // GTMLoggerLogWriter + + +@implementation GTMLogBasicFormatter + +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + // Performance note: since we always have to create a new NSString from the + // returned CFStringRef, we may want to do a quick check here to see if |fmt| + // contains a '%', and if not, simply return 'fmt'. + CFStringRef cfmsg = NULL; + cfmsg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, + NULL, // format options + (CFStringRef)fmt, + args); + return GTMCFAutorelease(cfmsg); +} + +@end // GTMLogBasicFormatter + + +@implementation GTMLogStandardFormatter + +- (id)init { + if ((self = [super init])) { + dateFormatter_ = [[NSDateFormatter alloc] init]; + [dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4]; + [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"]; + pname_ = [[[NSProcessInfo processInfo] processName] copy]; + pid_ = [[NSProcessInfo processInfo] processIdentifier]; + } + return self; +} + +- (void)dealloc { + [dateFormatter_ release]; + [pname_ release]; + [super dealloc]; +} + +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + GTMLOGGER_ASSERT(dateFormatter_ != nil); + NSString *tstamp = nil; + @synchronized (dateFormatter_) { + tstamp = [dateFormatter_ stringFromDate:[NSDate date]]; + } + return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@", + tstamp, pname_, pid_, pthread_self(), + level, (func ? func : @"(no func)"), + [super stringForFunc:func withFormat:fmt valist:args level:level]]; +} + +@end // GTMLogStandardFormatter + + +@implementation GTMLogLevelFilter + +// Check the environment and the user preferences for the GTMVerboseLogging key +// to see if verbose logging has been enabled. The environment variable will +// override the defaults setting, so check the environment first. +// COV_NF_START +static BOOL IsVerboseLoggingEnabled(void) { + static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging"; + static char *env = NULL; + if (env == NULL) + env = getenv([kVerboseLoggingKey UTF8String]); + + if (env && env[0]) { + return (strtol(env, NULL, 10) != 0); + } + + return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey]; +} +// COV_NF_END + +// In DEBUG builds, log everything. If we're not in a debug build we'll assume +// that we're in a Release build. +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { +#if DEBUG + return YES; +#endif + + BOOL allow = YES; + + switch (level) { + case kGTMLoggerLevelDebug: + allow = NO; + break; + case kGTMLoggerLevelInfo: + allow = (IsVerboseLoggingEnabled() == YES); + break; + case kGTMLoggerLevelError: + allow = YES; + break; + case kGTMLoggerLevelAssert: + allow = YES; + break; + default: + allow = YES; + break; + } + + return allow; +} + +@end // GTMLogLevelFilter + + +@implementation GTMLogNoFilter + +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { + return YES; // Allow everything through +} + +@end // GTMLogNoFilter diff --git a/src/common/mac/MachIPC.h b/src/common/mac/MachIPC.h new file mode 100644 index 00000000..89924123 --- /dev/null +++ b/src/common/mac/MachIPC.h @@ -0,0 +1,304 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// MachIPC.h +// +// Some helpful wrappers for using Mach IPC calls + +#ifndef MACH_IPC_H__ +#define MACH_IPC_H__ + +#import +#import +#import +#import + +#import + +//============================================================================== +// DISCUSSION: +// +// The three main classes of interest are +// +// MachMessage: a wrapper for a mach message of the following form +// mach_msg_header_t +// mach_msg_body_t +// optional descriptors +// optional extra message data +// +// MachReceiveMessage and MachSendMessage subclass MachMessage +// and are used instead of MachMessage which is an abstract base class +// +// ReceivePort: +// Represents a mach port for which we have receive rights +// +// MachPortSender: +// Represents a mach port for which we have send rights +// +// Here's an example to receive a message on a server port: +// +// // This creates our named server port +// ReceivePort receivePort("com.Google.MyService"); +// +// MachReceiveMessage message; +// kern_return_t result = receivePort.WaitForMessage(&message, 0); +// +// if (result == KERN_SUCCESS && message.GetMessageID() == 57) { +// mach_port_t task = message.GetTranslatedPort(0); +// mach_port_t thread = message.GetTranslatedPort(1); +// +// char *messageString = message.GetData(); +// +// printf("message string = %s\n", messageString); +// } +// +// Here is an example of using these classes to send a message to this port: +// +// // send to already named port +// MachPortSender sender("com.Google.MyService"); +// MachSendMessage message(57); // our message ID is 57 +// +// // add some ports to be translated for us +// message.AddDescriptor(mach_task_self()); // our task +// message.AddDescriptor(mach_thread_self()); // this thread +// +// char messageString[] = "Hello server!\n"; +// message.SetData(messageString, strlen(messageString)+1); +// +// kern_return_t result = sender.SendMessage(message, 1000); // timeout 1000ms +// + +#define PRINT_MACH_RESULT(result_, message_) \ + printf(message_" %s (%d)\n", mach_error_string(result_), result_ ); + +//============================================================================== +// A wrapper class for mach_msg_port_descriptor_t (with same memory layout) +// with convenient constructors and accessors +class MachMsgPortDescriptor : public mach_msg_port_descriptor_t { + public: + // General-purpose constructor + MachMsgPortDescriptor(mach_port_t in_name, + mach_msg_type_name_t in_disposition) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = in_disposition; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // For passing send rights to a port + MachMsgPortDescriptor(mach_port_t in_name) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = MACH_MSG_TYPE_COPY_SEND; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // Copy constructor + MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) { + name = desc.name; + pad1 = desc.pad1; + pad2 = desc.pad2; + disposition = desc.disposition; + type = desc.type; + } + + mach_port_t GetMachPort() const { + return name; + } + + mach_msg_type_name_t GetDisposition() const { + return disposition; + } + + // We're just a simple wrapper for mach_msg_port_descriptor_t + // and have the same memory layout + operator mach_msg_port_descriptor_t&() { + return *this; + } + + // For convenience + operator mach_port_t() const { + return GetMachPort(); + } +}; + +//============================================================================== +// MachMessage: a wrapper for a mach message +// (mach_msg_header_t, mach_msg_body_t, extra data) +// +// This considerably simplifies the construction of a message for sending +// and the getting at relevant data and descriptors for the receiver. +// +// Currently the combined size of the descriptors plus data must be +// less than 1024. But as a benefit no memory allocation is necessary. +// +// TODO: could consider adding malloc() support for very large messages +// +// A MachMessage object is used by ReceivePort::WaitForMessage +// and MachPortSender::SendMessage +// +class MachMessage { + public: + + // The receiver of the message can retrieve the raw data this way + u_int8_t *GetData() { + return GetDataLength() > 0 ? GetDataPacket()->data : NULL; + } + + u_int32_t GetDataLength() { + return EndianU32_LtoN(GetDataPacket()->data_length); + } + + // The message ID may be used as a code identifying the type of message + void SetMessageID(int32_t message_id) { + GetDataPacket()->id = EndianU32_NtoL(message_id); + } + + int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); } + + // Adds a descriptor (typically a mach port) to be translated + // returns true if successful, otherwise not enough space + bool AddDescriptor(const MachMsgPortDescriptor &desc); + + int GetDescriptorCount() const { return body.msgh_descriptor_count; } + MachMsgPortDescriptor *GetDescriptor(int n); + + // Convenience method which gets the mach port described by the descriptor + mach_port_t GetTranslatedPort(int n); + + // A simple message is one with no descriptors + bool IsSimpleMessage() const { return GetDescriptorCount() == 0; } + + // Sets raw data for the message (returns false if not enough space) + bool SetData(void *data, int32_t data_length); + + protected: + // Consider this an abstract base class - must create an actual instance + // of MachReceiveMessage or MachSendMessage + + MachMessage() { + memset(this, 0, sizeof(MachMessage)); + } + + friend class ReceivePort; + friend class MachPortSender; + + // Represents raw data in our message + struct MessageDataPacket { + int32_t id; // little-endian + int32_t data_length; // little-endian + u_int8_t data[1]; // actual size limited by sizeof(MachMessage) + }; + + MessageDataPacket* GetDataPacket(); + + void SetDescriptorCount(int n); + void SetDescriptor(int n, const MachMsgPortDescriptor &desc); + + // Returns total message size setting msgh_size in the header to this value + int CalculateSize(); + + mach_msg_header_t head; + mach_msg_body_t body; + u_int8_t padding[1024]; // descriptors and data may be embedded here +}; + +//============================================================================== +// MachReceiveMessage and MachSendMessage are useful to separate the idea +// of a mach message being sent and being received, and adds increased type +// safety: +// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage +// MachPortSender::SendMessage() only accepts a MachSendMessage + +//============================================================================== +class MachReceiveMessage : public MachMessage { + public: + MachReceiveMessage() : MachMessage() {}; +}; + +//============================================================================== +class MachSendMessage : public MachMessage { + public: + MachSendMessage(int32_t message_id); +}; + +//============================================================================== +// Represents a mach port for which we have receive rights +class ReceivePort { + public: + // Creates a new mach port for receiving messages and registers a name for it + ReceivePort(const char *receive_port_name); + + // Given an already existing mach port, use it. We take ownership of the + // port and deallocate it in our destructor. + ReceivePort(mach_port_t receive_port); + + // Create a new mach port for receiving messages + ReceivePort(); + + ~ReceivePort(); + + // Waits on the mach port until message received or timeout + kern_return_t WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout); + + // The underlying mach port that we wrap + mach_port_t GetPort() const { return port_; } + + private: + ReceivePort(const ReceivePort&); // disable copy c-tor + + mach_port_t port_; + kern_return_t init_result_; +}; + +//============================================================================== +// Represents a mach port for which we have send rights +class MachPortSender { + public: + // get a port with send rights corresponding to a named registered service + MachPortSender(const char *receive_port_name); + + + // Given an already existing mach port, use it. + MachPortSender(mach_port_t send_port); + + kern_return_t SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout); + + private: + MachPortSender(const MachPortSender&); // disable copy c-tor + + mach_port_t send_port_; + kern_return_t init_result_; +}; + +#endif // MACH_IPC_H__ diff --git a/src/common/mac/MachIPC.mm b/src/common/mac/MachIPC.mm new file mode 100644 index 00000000..9e521e48 --- /dev/null +++ b/src/common/mac/MachIPC.mm @@ -0,0 +1,297 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// MachIPC.mm +// Wrapper for mach IPC calls + +#import +#import "MachIPC.h" + +//============================================================================== +MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { + head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); + + // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage() + head.msgh_local_port = MACH_PORT_NULL; + head.msgh_reserved = 0; + head.msgh_id = 0; + + SetDescriptorCount(0); // start out with no descriptors + + SetMessageID(message_id); + SetData(NULL, 0); // client may add data later +} + +//============================================================================== +// returns true if successful +bool MachMessage::SetData(void *data, + int32_t data_length) { + // first check to make sure we have enough space + int size = CalculateSize(); + int new_size = size + data_length; + + if ((unsigned)new_size > sizeof(MachMessage)) { + return false; // not enough space + } + + GetDataPacket()->data_length = EndianU32_NtoL(data_length); + if (data) memcpy(GetDataPacket()->data, data, data_length); + + CalculateSize(); + + return true; +} + +//============================================================================== +// calculates and returns the total size of the message +// Currently, the entire message MUST fit inside of the MachMessage +// messsage size <= sizeof(MachMessage) +int MachMessage::CalculateSize() { + int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t); + + // add space for MessageDataPacket + int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3; + size += 2*sizeof(int32_t) + alignedDataLength; + + // add space for descriptors + size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor); + + head.msgh_size = size; + + return size; +} + +//============================================================================== +MachMessage::MessageDataPacket *MachMessage::GetDataPacket() { + int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); + MessageDataPacket *packet = + reinterpret_cast(padding + desc_size); + + return packet; +} + +//============================================================================== +void MachMessage::SetDescriptor(int n, + const MachMsgPortDescriptor &desc) { + MachMsgPortDescriptor *desc_array = + reinterpret_cast(padding); + desc_array[n] = desc; +} + +//============================================================================== +// returns true if successful otherwise there was not enough space +bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) { + // first check to make sure we have enough space + int size = CalculateSize(); + int new_size = size + sizeof(MachMsgPortDescriptor); + + if ((unsigned)new_size > sizeof(MachMessage)) { + return false; // not enough space + } + + // unfortunately, we need to move the data to allow space for the + // new descriptor + u_int8_t *p = reinterpret_cast(GetDataPacket()); + bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t)); + + SetDescriptor(GetDescriptorCount(), desc); + SetDescriptorCount(GetDescriptorCount() + 1); + + CalculateSize(); + + return true; +} + +//============================================================================== +void MachMessage::SetDescriptorCount(int n) { + body.msgh_descriptor_count = n; + + if (n > 0) { + head.msgh_bits |= MACH_MSGH_BITS_COMPLEX; + } else { + head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; + } +} + +//============================================================================== +MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) { + if (n < GetDescriptorCount()) { + MachMsgPortDescriptor *desc = + reinterpret_cast(padding); + return desc + n; + } + + return nil; +} + +//============================================================================== +mach_port_t MachMessage::GetTranslatedPort(int n) { + if (n < GetDescriptorCount()) { + return GetDescriptor(n)->GetMachPort(); + } + return MACH_PORT_NULL; +} + +#pragma mark - + +//============================================================================== +// create a new mach port for receiving messages and register a name for it +ReceivePort::ReceivePort(const char *receive_port_name) { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); + + if (init_result_ != KERN_SUCCESS) + return; + + mach_port_t bootstrap_port = 0; + init_result_ = task_get_bootstrap_port(current_task, &bootstrap_port); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = bootstrap_register(bootstrap_port, + const_cast(receive_port_name), + port_); +} + +//============================================================================== +// create a new mach port for receiving messages +ReceivePort::ReceivePort() { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); +} + +//============================================================================== +// Given an already existing mach port, use it. We take ownership of the +// port and deallocate it in our destructor. +ReceivePort::ReceivePort(mach_port_t receive_port) + : port_(receive_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +ReceivePort::~ReceivePort() { + if (init_result_ == KERN_SUCCESS) + mach_port_deallocate(mach_task_self(), port_); +} + +//============================================================================== +kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout) { + if (!out_message) { + return KERN_INVALID_ARGUMENT; + } + + // return any error condition encountered in constructor + if (init_result_ != KERN_SUCCESS) + return init_result_; + + out_message->head.msgh_bits = 0; + out_message->head.msgh_local_port = port_; + out_message->head.msgh_remote_port = MACH_PORT_NULL; + out_message->head.msgh_reserved = 0; + out_message->head.msgh_id = 0; + + kern_return_t result = mach_msg(&out_message->head, + MACH_RCV_MSG | MACH_RCV_TIMEOUT, + 0, + sizeof(MachMessage), + port_, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} + +#pragma mark - + +//============================================================================== +// get a port with send rights corresponding to a named registered service +MachPortSender::MachPortSender(const char *receive_port_name) { + mach_port_t bootstrap_port = 0; + init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = bootstrap_look_up(bootstrap_port, + const_cast(receive_port_name), + &send_port_); +} + +//============================================================================== +MachPortSender::MachPortSender(mach_port_t send_port) + : send_port_(send_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +kern_return_t MachPortSender::SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout) { + if (message.head.msgh_size == 0) { + return KERN_INVALID_VALUE; // just for safety -- never should occur + }; + + if (init_result_ != KERN_SUCCESS) + return init_result_; + + message.head.msgh_remote_port = send_port_; + + kern_return_t result = mach_msg(&message.head, + MACH_SEND_MSG | MACH_SEND_TIMEOUT, + message.head.msgh_size, + 0, + MACH_PORT_NULL, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} diff --git a/src/common/mac/SimpleStringDictionary.h b/src/common/mac/SimpleStringDictionary.h new file mode 100644 index 00000000..3d6c27bd --- /dev/null +++ b/src/common/mac/SimpleStringDictionary.h @@ -0,0 +1,195 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// SimpleStringDictionary.h +// + +#ifndef SimpleStringDictionary_H__ +#define SimpleStringDictionary_H__ + +#import +#import + +namespace google_breakpad { + +//============================================================================== +// SimpleStringDictionary (and associated class KeyValueEntry) implement a very +// basic dictionary container class. It has the property of not making any +// memory allocations when getting and setting values. But it is not very +// efficient, with calls to get and set values operating in linear time. +// It has the additional limitation of having a fairly small fixed capacity of +// SimpleStringDictionary::MAX_NUM_ENTRIES entries. An assert() will fire if +// the client attempts to set more than this number of key/value pairs. +// Ordinarilly a C++ programmer would use something like the std::map template +// class, or on the Macintosh would often choose CFDictionary or NSDictionary. +// But these dictionary classes may call malloc() during get and set operations. +// Google Breakpad requires that no memory allocations be made in code running +// in its exception handling thread, so it uses SimpleStringDictionary as the +// underlying implementation for the GoogleBreakpad.framework APIs: +// GoogleBreakpadSetKeyValue(), GoogleBreakpadKeyValue(), and +// GoogleBreakpadRemoveKeyValue() +// + +//============================================================================== +// KeyValueEntry +// +// A helper class used by SimpleStringDictionary representing a single +// storage cell for a key/value pair. Each key and value string are +// limited to MAX_STRING_STORAGE_SIZE-1 bytes (not glyphs). This class +// performs no memory allocations. It has methods for setting and getting +// key and value strings. +// +class KeyValueEntry { + public: + KeyValueEntry() { + Clear(); + } + + KeyValueEntry(const char *key, const char *value) { + SetKeyValue(key, value); + } + + void SetKeyValue(const char *key, const char *value) { + if (!key) { + key = ""; + } + if (!value) { + value = ""; + } + + strlcpy(key_, key, sizeof(key_)); + strlcpy(value_, value, sizeof(value_)); + } + + void SetValue(const char *value) { + if (!value) { + value = ""; + } + strlcpy(value_, value, sizeof(value_)); + }; + + // Removes the key/value + void Clear() { + memset(key_, 0, sizeof(key_)); + memset(value_, 0, sizeof(value_)); + } + + bool IsActive() const { return key_[0] != '\0'; } + const char *GetKey() const { return key_; } + const char *GetValue() const { return value_; } + + // Don't change this without considering the fixed size + // of MachMessage (in MachIPC.h) + // (see also struct KeyValueMessageData in Inspector.h) + enum {MAX_STRING_STORAGE_SIZE = 256}; + + private: + char key_[MAX_STRING_STORAGE_SIZE]; + char value_[MAX_STRING_STORAGE_SIZE]; +}; + +//============================================================================== +// This class is not an efficient dictionary, but for the purposes of breakpad +// will be just fine. We're just dealing with ten or so distinct +// key/value pairs. The idea is to avoid any malloc() or free() calls +// in certain important methods to be called when a process is in a +// crashed state. Each key and value string are limited to +// KeyValueEntry::MAX_STRING_STORAGE_SIZE-1 bytes (not glyphs). Strings passed +// in exceeding this length will be truncated. +// +class SimpleStringDictionary { + public: + SimpleStringDictionary() {}; // entries will all be cleared + + // Returns the number of active key/value pairs. The upper limit for this + // is MAX_NUM_ENTRIES. + int GetCount() const; + + // Given |key|, returns its corresponding |value|. + // If |key| is NULL, an assert will fire or NULL will be returned. If |key| + // is not found or is an empty string, NULL is returned. + const char *GetValueForKey(const char *key); + + // Stores a string |value| represented by |key|. If |key| is NULL or an empty + // string, this will assert (or do nothing). If |value| is NULL then + // the |key| will be removed. An empty string is OK for |value|. + void SetKeyValue(const char *key, const char *value); + + // Given |key|, removes any associated value. It will assert (or do nothing) + // if NULL is passed in. It will do nothing if |key| is not found. + void RemoveKey(const char *key); + + // This is the maximum number of key/value pairs which may be set in the + // dictionary. An assert may fire if more values than this are set. + // Don't change this without also changing comment in GoogleBreakpad.h + enum {MAX_NUM_ENTRIES = 64}; + + private: + friend class SimpleStringDictionaryIterator; + + const KeyValueEntry *GetEntry(int i) const; + + KeyValueEntry entries_[MAX_NUM_ENTRIES]; +}; + +//============================================================================== +class SimpleStringDictionaryIterator { + public: + SimpleStringDictionaryIterator(const SimpleStringDictionary &dict) + : dict_(dict), i_(0) { + } + + // Initializes iterator to the beginning (may later call Next() ) + void Start() { + i_ = 0; + } + + // like the nextObject method of NSEnumerator (in Cocoa) + // returns NULL when there are no more entries + // + const KeyValueEntry* Next() { + for (; i_ < SimpleStringDictionary::MAX_NUM_ENTRIES; ++i_) { + const KeyValueEntry *entry = dict_.GetEntry(i_); + if (entry->IsActive()) { + i_++; // move to next entry for next time + return entry; + } + } + + return NULL; // reached end of array + } + + private: + const SimpleStringDictionary& dict_; + int i_; +}; + +} // namespace google_breakpad + +#endif // SimpleStringDictionary_H__ diff --git a/src/common/mac/SimpleStringDictionary.mm b/src/common/mac/SimpleStringDictionary.mm new file mode 100644 index 00000000..567855e0 --- /dev/null +++ b/src/common/mac/SimpleStringDictionary.mm @@ -0,0 +1,131 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// SimpleStringDictionary.mm +// Simple string dictionary that does not allocate memory +// + +#import "SimpleStringDictionary.h" + +namespace google_breakpad { + +//============================================================================== +const KeyValueEntry *SimpleStringDictionary::GetEntry(int i) const { + return (i >= 0 && i < MAX_NUM_ENTRIES) ? &entries_[i] : NULL; +} + +//============================================================================== +int SimpleStringDictionary::GetCount() const { + int count = 0; + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + if (entries_[i].IsActive() ) { + ++count; + } + } + + return count; +} + +//============================================================================== +const char *SimpleStringDictionary::GetValueForKey(const char *key) { + assert(key); + if (!key) + return NULL; + + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + KeyValueEntry &entry = entries_[i]; + if (entry.IsActive() && !strcmp(entry.GetKey(), key)) { + return entry.GetValue(); + } + } + + return NULL; +} + +//============================================================================== +void SimpleStringDictionary::SetKeyValue(const char *key, + const char *value) { + if (!value) { + RemoveKey(key); + return; + } + + // key must not be NULL + assert(key); + if (!key) + return; + + // key must not be empty string + assert(key[0] != '\0'); + if (key[0] == '\0') + return; + + int free_index = -1; + + // check if key already exists + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + KeyValueEntry &entry = entries_[i]; + + if (entry.IsActive()) { + if (!strcmp(entry.GetKey(), key)) { + entry.SetValue(value); + return; + } + } else { + // Make a note of an empty slot + if (free_index == -1) { + free_index = i; + } + } + } + + // check if we've run out of space + assert(free_index != -1); + + // Put new key into an empty slot (if found) + if (free_index != -1) { + entries_[free_index].SetKeyValue(key, value); + } +} + +//============================================================================== +void SimpleStringDictionary::RemoveKey(const char *key) { + assert(key); + if (!key) + return; + + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + if (!strcmp(entries_[i].GetKey(), key)) { + entries_[i].Clear(); + return; + } + } +} + +} // namespace google_breakpad diff --git a/src/common/mac/testing/GTMSenTestCase.h b/src/common/mac/testing/GTMSenTestCase.h new file mode 100644 index 00000000..d425f597 --- /dev/null +++ b/src/common/mac/testing/GTMSenTestCase.h @@ -0,0 +1,1004 @@ +// +// GTMSenTestCase.h +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// Portions of this file fall under the following license, marked with +// SENTE_BEGIN - SENTE_END +// +// Copyright (c) 1997-2005, Sen:te (Sente SA). All rights reserved. +// +// Use of this source code is governed by the following license: +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL Sente SA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Note: this license is equivalent to the FreeBSD license. +// +// This notice may not be removed from this file. + +// Some extra test case macros that would have been convenient for SenTestingKit +// to provide. I didn't stick GTM in front of the Macro names, so that they would +// be easy to remember. + +#import "GTMDefines.h" + +#if (!GTM_IPHONE_SDK) +#import +#else +#import +NSString *STComposeString(NSString *, ...); +#endif + +// Generates a failure when a1 != noErr +// Args: +// a1: should be either an OSErr or an OSStatus +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNoErr(a1, description, ...) \ +do { \ + @try {\ + OSStatus a1value = (a1); \ + if (a1value != noErr) { \ + NSString *_expression = [NSString stringWithFormat:@"Expected noErr, got %ld for (%s)", a1value, #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == noErr fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 != a2 +// Args: +// a1: received value. Should be either an OSErr or an OSStatus +// a2: expected value. Should be either an OSErr or an OSStatus +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertErr(a1, a2, description, ...) \ +do { \ + @try {\ + OSStatus a1value = (a1); \ + OSStatus a2value = (a2); \ + if (a1value != a2value) { \ + NSString *_expression = [NSString stringWithFormat:@"Expected %s(%ld) but got %ld for (%s)", #a2, a2value, a1value, #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s) fails", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +// Generates a failure when a1 is NULL +// Args: +// a1: should be a pointer (use STAssertNotNil for an object) +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotNULL(a1, description, ...) \ +do { \ + @try {\ + const void* a1value = (a1); \ + if (a1value == NULL) { \ + NSString *_expression = [NSString stringWithFormat:@"(%s) != NULL", #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != NULL fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is not NULL +// Args: +// a1: should be a pointer (use STAssertNil for an object) +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNULL(a1, description, ...) \ +do { \ + @try {\ + const void* a1value = (a1); \ + if (a1value != NULL) { \ + NSString *_expression = [NSString stringWithFormat:@"(%s) == NULL", #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == NULL fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is equal to a2. This test is for C scalars, +// structs and unions. +// Args: +// a1: argument 1 +// a2: argument 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEquals(a1, a2, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ + if ([a1encoded isEqualToValue:a2encoded]) { \ + NSString *_expression = [NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is equal to a2. This test is for objects. +// Args: +// a1: argument 1. object. +// a2: argument 2. object. +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualObjects(a1, a2, desc, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if ( (@encode(__typeof__(a1value)) == @encode(id)) && \ + (@encode(__typeof__(a2value)) == @encode(id)) && \ + ![(id)a1value isEqual:(id)a2value] ) continue; \ + NSString *_expression = [NSString stringWithFormat:@"%s('%@') != %s('%@')", #a1, [a1 description], #a2, [a2 description]]; \ + if (desc) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(desc, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(desc, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is not 'op' to a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertOperation(a1, a2, op, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + if (!(a1value op a2value)) { \ + double a1DoubleValue = a1value; \ + double a2DoubleValue = a2value; \ + NSString *_expression = [NSString stringWithFormat:@"%s (%lg) %s %s (%lg)", #a1, a1DoubleValue, #op, #a2, a2DoubleValue]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException \ + failureInRaise:[NSString stringWithFormat:@"(%s) %s (%s)", #a1, #op, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is not > a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertGreaterThan(a1, a2, description, ...) \ + STAssertOperation(a1, a2, >, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not >= a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertGreaterThanOrEqual(a1, a2, description, ...) \ + STAssertOperation(a1, a2, >=, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not < a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertLessThan(a1, a2, description, ...) \ + STAssertOperation(a1, a2, <, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not <= a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertLessThanOrEqual(a1, a2, description, ...) \ + STAssertOperation(a1, a2, <=, description, ##__VA_ARGS__) + +// Generates a failure when string a1 is not equal to string a2. This call +// differs from STAssertEqualObjects in that strings that are different in +// composition (precomposed vs decomposed) will compare equal if their final +// representation is equal. +// ex O + umlaut decomposed is the same as O + umlaut composed. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertEqualStrings(a1, a2, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if (a1value == a2value) continue; \ + if ([a1value isKindOfClass:[NSString class]] && \ + [a2value isKindOfClass:[NSString class]] && \ + [a1value compare:a2value options:0] == NSOrderedSame) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: a1value \ + andObject: a2value \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when string a1 is equal to string a2. This call +// differs from STAssertEqualObjects in that strings that are different in +// composition (precomposed vs decomposed) will compare equal if their final +// representation is equal. +// ex O + umlaut decomposed is the same as O + umlaut composed. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualStrings(a1, a2, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if ([a1value isKindOfClass:[NSString class]] && \ + [a2value isKindOfClass:[NSString class]] && \ + [a1value compare:a2value options:0] != NSOrderedSame) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: a1value \ + andObject: a2value \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when c-string a1 is not equal to c-string a2. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertEqualCStrings(a1, a2, description, ...) \ +do { \ + @try {\ + const char* a1value = (a1); \ + const char* a2value = (a2); \ + if (a1value == a2value) continue; \ + if (strcmp(a1value, a2value) == 0) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \ + andObject: [NSString stringWithUTF8String:a2value] \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when c-string a1 is equal to c-string a2. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualCStrings(a1, a2, description, ...) \ +do { \ + @try {\ + const char* a1value = (a1); \ + const char* a2value = (a2); \ + if (strcmp(a1value, a2value) != 0) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \ + andObject: [NSString stringWithUTF8String:a2value] \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +#if GTM_IPHONE_SDK + +// SENTE_BEGIN +/*" Generates a failure when !{ [a1 isEqualTo:a2] } is false + (or one is nil and the other is not). + _{a1 The object on the left.} + _{a2 The object on the right.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertEqualObjects(a1, a2, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if (a1value == a2value) continue; \ + if ( (@encode(__typeof__(a1value)) == @encode(id)) && \ + (@encode(__typeof__(a2value)) == @encode(id)) && \ + [(id)a1value isEqual: (id)a2value] ) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: a1value \ + andObject: a2value \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +/*" Generates a failure when a1 is not equal to a2. This test is for + C scalars, structs and unions. + _{a1 The argument on the left.} + _{a2 The argument on the right.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertEquals(a1, a2, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType: @encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType: @encode(__typeof__(a2))]; \ + if (![a1encoded isEqualToValue:a2encoded]) { \ + [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \ + andValue: a2encoded \ + withAccuracy: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +#define STAbsoluteDifference(left,right) (MAX(left,right)-MIN(left,right)) + + +/*" Generates a failure when a1 is not equal to a2 within + or - accuracy is false. + This test is for scalars such as floats and doubles where small differences + could make these items not exactly equal, but also works for all scalars. + _{a1 The scalar on the left.} + _{a2 The scalar on the right.} + _{accuracy The maximum difference between a1 and a2 for these values to be + considered equal.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ + +#define STAssertEqualsWithAccuracy(a1, a2, accuracy, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + __typeof__(accuracy) accuracyvalue = (accuracy); \ + if (STAbsoluteDifference(a1value, a2value) > accuracyvalue) { \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ + NSValue *accuracyencoded = [NSValue value:&accuracyvalue withObjCType:@encode(__typeof__(accuracy))]; \ + [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \ + andValue: a2encoded \ + withAccuracy: accuracyencoded \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + + +/*" Generates a failure unconditionally. + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STFail(description, ...) \ +[self failWithException:[NSException failureInFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]] + + + +/*" Generates a failure when a1 is not nil. + _{a1 An object.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNil(a1, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + if (a1value != nil) { \ + NSString *_a1 = [NSString stringWithUTF8String: #a1]; \ + NSString *_expression = [NSString stringWithFormat:@"((%@) == nil)", _a1]; \ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == nil fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +/*" Generates a failure when a1 is nil. + _{a1 An object.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNotNil(a1, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + if (a1value == nil) { \ + NSString *_a1 = [NSString stringWithUTF8String: #a1]; \ + NSString *_expression = [NSString stringWithFormat:@"((%@) != nil)", _a1]; \ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != nil fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +/*" Generates a failure when expression evaluates to false. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertTrue(expr, description, ...) \ +do { \ + BOOL _evaluatedExpression = (expr);\ + if (!_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while (0) + + +/*" Generates a failure when expression evaluates to false and in addition will + generate error messages if an exception is encountered. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertTrueNoThrow(expr, description, ...) \ +do { \ + @try {\ + BOOL _evaluatedExpression = (expr);\ + if (!_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) ", #expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while (0) + + +/*" Generates a failure when the expression evaluates to true. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertFalse(expr, description, ...) \ +do { \ + BOOL _evaluatedExpression = (expr);\ + if (_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: YES \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while (0) + + +/*" Generates a failure when the expression evaluates to true and in addition + will generate error messages if an exception is encountered. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertFalseNoThrow(expr, description, ...) \ +do { \ + @try {\ + BOOL _evaluatedExpression = (expr);\ + if (_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: YES \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"!(%s) ", #expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while (0) + + +/*" Generates a failure when expression does not throw an exception. + _{expression The expression that is evaluated.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent. +"*/ +#define STAssertThrows(expr, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (id anException) { \ + continue; \ + }\ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does not throw an exception of a + specific class. + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertThrowsSpecific(expr, specificException, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + continue; \ + }\ + @catch (id anException) {\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + }\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does not throw an exception of a + specific class with a specific name. Useful for those frameworks like + AppKit or Foundation that throw generic NSException w/specific names + (NSInvalidArgumentException, etc). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{aName The name of the specified exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} + +"*/ +#define STAssertThrowsSpecificNamed(expr, specificException, aName, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + if ([aName isEqualToString: [anException name]]) continue; \ + NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + }\ + @catch (id anException) {\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + }\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does throw an exception. + _{expression The expression that is evaluated.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNoThrow(expr, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while (0) + + +/*" Generates a failure when expression does throw an exception of the specitied + class. Any other exception is okay (i.e. does not generate a failure). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNoThrowSpecific(expr, specificException, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anythingElse) {\ + ; \ + }\ +} while (0) + + +/*" Generates a failure when expression does throw an exception of a + specific class with a specific name. Useful for those frameworks like + AppKit or Foundation that throw generic NSException w/specific names + (NSInvalidArgumentException, etc). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{aName The name of the specified exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} + +"*/ +#define STAssertNoThrowSpecificNamed(expr, specificException, aName, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + if ([aName isEqualToString: [anException name]]) { \ + NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + } \ + continue; \ + }\ + @catch (id anythingElse) {\ + ; \ + }\ +} while (0) + + + +@interface NSException (GTMSenTestAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInCondition:(NSString *)condition + isTrue:(BOOL)isTrue + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInEqualityBetweenObject:(id)left + andObject:(id)right + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left + andValue:(NSValue *)right + withAccuracy:(NSValue *)accuracy + inFile:(NSString *)filename + atLine:(int) ineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInRaise:(NSString *)expression + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInRaise:(NSString *)expression + exception:(NSException *)exception + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; +@end + +// SENTE_END + +@interface SenTestCase : NSObject { + SEL currentSelector_; +} + +- (void)setUp; +- (void)invokeTest; +- (void)tearDown; +- (void)performTest:(SEL)sel; +- (void)failWithException:(NSException*)exception; +@end + +GTM_EXTERN NSString *const SenTestFailureException; + +GTM_EXTERN NSString *const SenTestFilenameKey; +GTM_EXTERN NSString *const SenTestLineNumberKey; + +#endif // GTM_IPHONE_SDK + +// All unittest cases in GTM should inherit from GTMTestCase. It makes sure +// to set up our logging system correctly to verify logging calls. +// See GTMUnitTestDevLog.h for details +@interface GTMTestCase : SenTestCase +@end diff --git a/src/common/mac/testing/GTMSenTestCase.m b/src/common/mac/testing/GTMSenTestCase.m new file mode 100644 index 00000000..99b9db07 --- /dev/null +++ b/src/common/mac/testing/GTMSenTestCase.m @@ -0,0 +1,366 @@ +// +// GTMSenTestCase.m +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMSenTestCase.h" +#import + +#if !GTM_IPHONE_SDK +#import "GTMGarbageCollection.h" +#endif // !GTM_IPHONE_SDK + +#if GTM_IPHONE_SDK +#import + +@interface NSException (GTMSenTestPrivateAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + reason:(NSString *)reason; +@end + +@implementation NSException (GTMSenTestPrivateAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + reason:(NSString *)reason { + NSDictionary *userInfo = + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger:lineNumber], SenTestLineNumberKey, + filename, SenTestFilenameKey, + nil]; + + return [self exceptionWithName:SenTestFailureException + reason:reason + userInfo:userInfo]; +} +@end + +@implementation NSException (GTMSenTestAdditions) + ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = testDescription; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInCondition:(NSString *)condition + isTrue:(BOOL)isTrue + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@", + condition, isTrue ? "TRUE" : "FALSE", testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInEqualityBetweenObject:(id)left + andObject:(id)right + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", + [left description], [right description], testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left + andValue:(NSValue *)right + withAccuracy:(NSValue *)accuracy + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason; + if (accuracy) { + reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", + left, right, testDescription]; + } else { + reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@' +/-'%@'. %@", + left, right, accuracy, testDescription]; + } + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInRaise:(NSString *)expression + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = [NSString stringWithFormat:@"'%@' should raise. %@", + expression, testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInRaise:(NSString *)expression + exception:(NSException *)exception + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason; + if ([[exception name] isEqualToString:SenTestFailureException]) { + // it's our exception, assume it has the right description on it. + reason = [exception reason]; + } else { + // not one of our exception, use the exceptions reason and our description + reason = [NSString stringWithFormat:@"'%@' raised '%@'. %@", + expression, [exception reason], testDescription]; + } + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + +@end + +NSString *STComposeString(NSString *formatString, ...) { + NSString *reason = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + reason = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + return reason; +} + +NSString *const SenTestFailureException = @"SenTestFailureException"; +NSString *const SenTestFilenameKey = @"SenTestFilenameKey"; +NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey"; + +@interface SenTestCase (SenTestCasePrivate) +// our method of logging errors ++ (void)printException:(NSException *)exception fromTestName:(NSString *)name; +@end + +@implementation SenTestCase +- (void)failWithException:(NSException*)exception { + [exception raise]; +} + +- (void)setUp { +} + +- (void)performTest:(SEL)sel { + currentSelector_ = sel; + @try { + [self invokeTest]; + } @catch (NSException *exception) { + [[self class] printException:exception + fromTestName:NSStringFromSelector(sel)]; + [exception raise]; + } +} + ++ (void)printException:(NSException *)exception fromTestName:(NSString *)name { + NSDictionary *userInfo = [exception userInfo]; + NSString *filename = [userInfo objectForKey:SenTestFilenameKey]; + NSNumber *lineNumber = [userInfo objectForKey:SenTestLineNumberKey]; + NSString *className = NSStringFromClass([self class]); + if ([filename length] == 0) { + filename = @"Unknown.m"; + } + fprintf(stderr, "%s:%ld: error: -[%s %s] : %s\n", + [filename UTF8String], + (long)[lineNumber integerValue], + [className UTF8String], + [name UTF8String], + [[exception reason] UTF8String]); + fflush(stderr); +} + +- (void)invokeTest { + NSException *e = nil; + @try { + // Wrap things in autorelease pools because they may + // have an STMacro in their dealloc which may get called + // when the pool is cleaned up + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + // We don't log exceptions here, instead we let the person that called + // this log the exception. This ensures they are only logged once but the + // outer layers get the exceptions to report counts, etc. + @try { + [self setUp]; + @try { + [self performSelector:currentSelector_]; + } @catch (NSException *exception) { + e = [exception retain]; + } + [self tearDown]; + } @catch (NSException *exception) { + e = [exception retain]; + } + [pool release]; + } @catch (NSException *exception) { + e = [exception retain]; + } + if (e) { + [e autorelease]; + [e raise]; + } +} + +- (void)tearDown { +} + +- (NSString *)description { + // This matches the description OCUnit would return to you + return [NSString stringWithFormat:@"-[%@ %@]", [self class], + NSStringFromSelector(currentSelector_)]; +} +@end + +#endif // GTM_IPHONE_SDK + +@implementation GTMTestCase : SenTestCase +- (void)invokeTest { + Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog"); + if (devLogClass) { + [devLogClass performSelector:@selector(enableTracking)]; + [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; + + } + [super invokeTest]; + if (devLogClass) { + [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; + [devLogClass performSelector:@selector(disableTracking)]; + } +} +@end + +// Leak detection +#if !GTM_IPHONE_DEVICE +// Don't want to get leaks on the iPhone Device as the device doesn't +// have 'leaks'. The simulator does though. + +// COV_NF_START +// We don't have leak checking on by default, so this won't be hit. +static void _GTMRunLeaks(void) { + // This is an atexit handler. It runs leaks for us to check if we are + // leaking anything in our tests. + const char* cExclusionsEnv = getenv("GTM_LEAKS_SYMBOLS_TO_IGNORE"); + NSMutableString *exclusions = [NSMutableString string]; + if (cExclusionsEnv) { + NSString *exclusionsEnv = [NSString stringWithUTF8String:cExclusionsEnv]; + NSArray *exclusionsArray = [exclusionsEnv componentsSeparatedByString:@","]; + NSString *exclusion; + NSCharacterSet *wcSet = [NSCharacterSet whitespaceCharacterSet]; + GTM_FOREACH_OBJECT(exclusion, exclusionsArray) { + exclusion = [exclusion stringByTrimmingCharactersInSet:wcSet]; + [exclusions appendFormat:@"-exclude \"%@\" ", exclusion]; + } + } + NSString *string + = [NSString stringWithFormat:@"/usr/bin/leaks %@%d" + @"| /usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'", + exclusions, getpid()]; + int ret = system([string UTF8String]); + if (ret) { + fprintf(stderr, "%s:%d: Error: Unable to run leaks. 'system' returned: %d", + __FILE__, __LINE__, ret); + fflush(stderr); + } +} +// COV_NF_END + +static __attribute__((constructor)) void _GTMInstallLeaks(void) { + BOOL checkLeaks = YES; +#if !GTM_IPHONE_SDK + checkLeaks = GTMIsGarbageCollectionEnabled() ? NO : YES; +#endif // !GTM_IPHONE_SDK + if (checkLeaks) { + checkLeaks = getenv("GTM_ENABLE_LEAKS") ? YES : NO; + if (checkLeaks) { + // COV_NF_START + // We don't have leak checking on by default, so this won't be hit. + fprintf(stderr, "Leak Checking Enabled\n"); + fflush(stderr); + int ret = atexit(&_GTMRunLeaks); + _GTMDevAssert(ret == 0, + @"Unable to install _GTMRunLeaks as an atexit handler (%d)", + errno); + // COV_NF_END + } + } +} + +#endif // !GTM_IPHONE_DEVICE