diff --git a/AboutWindowController.m b/AboutWindowController.m index 4b8ea1e..526bbeb 100644 --- a/AboutWindowController.m +++ b/AboutWindowController.m @@ -42,12 +42,12 @@ } //automatically invoked when user clicks 'more info' -// ->load knockknock's html page +// ->load task explorer's html page - (IBAction)moreInfo:(id)sender { //open URL // ->invokes user's default browser - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://objective-see.com/products/knockknock.html"]]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://objective-see.com/products/taskexplorer.html"]]; return; } diff --git a/AppDelegate.m b/AppDelegate.m index b81c634..c4c43f2 100755 --- a/AppDelegate.m +++ b/AppDelegate.m @@ -15,11 +15,9 @@ #import "Task.h" -//TODO: full VT queue!! - flush - //TODO: add IPV6 :: as 0.0.0.0? (since we do this for IPV4 i think) //TODO: filter out dup'd networks (airportd 0:0..) -not sure want to do this - +//TODO: first time (w/ auth) dylibs don't show up? @implementation AppDelegate @@ -108,11 +106,9 @@ // ->display authentication request if needed if(YES != [self isAuthenticated]) { - //ask - // ->displays popup + //display auth popup + // ->will kick off task emun on successful auth :) [self askForRoot]; - - //TODO: make sure we call 'explore' once successfully auth'd :) } else { diff --git a/Items/Binary.h b/Items/Binary.h index 34cd5cb..6e6fca4 100644 --- a/Items/Binary.h +++ b/Items/Binary.h @@ -41,10 +41,6 @@ /* VIRUS TOTAL INFO */ -//flag saying item is 'last' -// ->thus VT items should all be queried -@property BOOL lastItem; - //dictionary returned by VT @property (nonatomic, retain)NSDictionary* vtInfo; diff --git a/Items/Binary.m b/Items/Binary.m index 3a17d38..1ef30ed 100644 --- a/Items/Binary.m +++ b/Items/Binary.m @@ -23,7 +23,6 @@ @synthesize isTaskBinary; @synthesize vtInfo; -@synthesize lastItem; //init method -(id)initWithParams:(NSDictionary*)params diff --git a/RequestRootWindowController.h b/RequestRootWindowController.h index 70c0637..fcaeaf7 100644 --- a/RequestRootWindowController.h +++ b/RequestRootWindowController.h @@ -15,6 +15,15 @@ /* PROPERTIES */ +//auth button +@property (weak) IBOutlet NSButton *authButton; + +//arrow icon +@property (weak) IBOutlet NSImageView *arrowIcon; + +//help button +@property (weak) IBOutlet NSButton *helpButton; + //status msg @property (weak) IBOutlet NSTextField *statusMsg; @@ -27,6 +36,10 @@ // ->auths user! -(IBAction)authenticate:(id)sender; +//invoked when user clicks 'help' button +// ->open product's page w/ anchor to help +-(IBAction)help:(id)sender; + //invoked when user clicks 'cancel' button // ->exits app -(IBAction)close:(id)sender; diff --git a/RequestRootWindowController.m b/RequestRootWindowController.m index 3e54106..eb0d3ec 100644 --- a/RequestRootWindowController.m +++ b/RequestRootWindowController.m @@ -16,11 +16,9 @@ @implementation RequestRootWindowController @synthesize statusMsg; +@synthesize authButton; @synthesize shouldExit; -//TODO: add 'why' / info button :) - - //automatically called when nib is loaded // ->center window -(void)awakeFromNib @@ -28,6 +26,13 @@ //center [self.window center]; + //make auth button one in focus + [self.window makeFirstResponder:self.authButton]; + + //default to exit app + // ->unset if auth is succesful + self.shouldExit = YES; + return; } @@ -52,9 +57,6 @@ // ->make ourselves unmodal and possibly exit app -(void)windowWillClose:(NSNotification *)notification { - //save prefs - //[self savePrefs]; - //make un-modal [[NSApplication sharedApplication] stopModal]; @@ -88,6 +90,12 @@ //path to XPC service NSString* xpcService = nil; + //hide arrow icon + self.arrowIcon.hidden = YES; + + //hide help button + self.helpButton.hidden = YES; + //get path to XPC service xpcService = getPath2XPC(); if(nil == xpcService) @@ -117,9 +125,6 @@ //err msg NSLog(@"ERROR: AuthorizationCreate() failed with %d", osStatus); - //set exit flag - self.shouldExit = YES; - //bail goto bail; } @@ -137,9 +142,6 @@ //set font to red self.statusMsg.textColor = [NSColor redColor]; - //set exit flag - self.shouldExit = YES; - //bail goto bail; } @@ -151,7 +153,7 @@ //2nd arg: permissions // ->4 at front is setuid - //TODO: make 4755 + //TODO: make 4755 before deploy (for testing, 777 makes XCOde be able to del it during build!) installArgs[1] = "4777"; //3rd arg: XPC service @@ -175,9 +177,6 @@ //set font to red self.statusMsg.textColor = [NSColor redColor]; - //set exit flag - self.shouldExit = YES; - //bail goto bail; } @@ -213,14 +212,22 @@ bail: } //invoked when user clicks 'cancel' button -// ->set exit flag and close window +// ->close window, which will trigger app exit -(IBAction)close:(id)sender { - //set flag to exit - self.shouldExit = YES; - //exit [self.window close]; } +//invoked when user clicks 'help' button +// ->open product's page w/ anchor to help +//TODO: make sure taskexplorer.html#help has help! +-(IBAction)help:(id)sender +{ + //open URL + // ->invokes user's default browser + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://objective-see.com/products/taskexplorer.html#help"]]; + + return; +} @end diff --git a/Task.m b/Task.m index 501332e..785ae49 100644 --- a/Task.m +++ b/Task.m @@ -114,7 +114,7 @@ self.binary.isTaskBinary = YES; //add to queue - // ->this will processing + // ->this will process in background [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.binaryQueue enqueue:self.binary]; //add it to 'global' list @@ -420,7 +420,7 @@ bail: //complete dylib processing for new dylib // ->get signing info, hash, etc, & save into global list - // ...reloads each row (if task is stil current) + // ...reloads each row (if task is still current) for(Binary* newDylib in newDylibs) { //generate signing info diff --git a/TaskExplorer.xcodeproj/project.pbxproj b/TaskExplorer.xcodeproj/project.pbxproj index dfa33a8..bae1d95 100755 --- a/TaskExplorer.xcodeproj/project.pbxproj +++ b/TaskExplorer.xcodeproj/project.pbxproj @@ -38,6 +38,8 @@ CD4D54281B2EAC7800008030 /* NetworkInfoWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CD4D54271B2EAC7800008030 /* NetworkInfoWindow.xib */; }; CD4D543F1B2FF32C00008030 /* signedAppleIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = CD4D543E1B2FF32C00008030 /* signedAppleIcon.png */; }; CD6095731A87067D00E091CD /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD6095721A87067D00E091CD /* Security.framework */; }; + CD6AFB7F1B4736DF00D42C34 /* flatIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = CD6AFB7E1B4736DF00D42C34 /* flatIcon.png */; }; + CD6AFB811B4737AC00D42C34 /* arrow.png in Resources */ = {isa = PBXBuildFile; fileRef = CD6AFB801B4737AC00D42C34 /* arrow.png */; }; CD6E54FF1B1162B5007953AB /* ItemView.m in Sources */ = {isa = PBXBuildFile; fileRef = CD6E54FE1B1162B5007953AB /* ItemView.m */; }; CD7B9F4D1ACB959200DF3C71 /* logoAppleOver.png in Resources */ = {isa = PBXBuildFile; fileRef = CD7B9F4C1ACB959200DF3C71 /* logoAppleOver.png */; }; CD7B9F501ACB9A8400DF3C71 /* Exception.m in Sources */ = {isa = PBXBuildFile; fileRef = CD7B9F4F1ACB9A8400DF3C71 /* Exception.m */; }; @@ -180,6 +182,8 @@ CD4D54271B2EAC7800008030 /* NetworkInfoWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = NetworkInfoWindow.xib; path = UI/NetworkInfoWindow.xib; sourceTree = ""; }; CD4D543E1B2FF32C00008030 /* signedAppleIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = signedAppleIcon.png; path = images/signedAppleIcon.png; sourceTree = SOURCE_ROOT; }; CD6095721A87067D00E091CD /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + CD6AFB7E1B4736DF00D42C34 /* flatIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = flatIcon.png; path = images/flatIcon.png; sourceTree = SOURCE_ROOT; }; + CD6AFB801B4737AC00D42C34 /* arrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = arrow.png; path = images/arrow.png; sourceTree = SOURCE_ROOT; }; CD6E54FD1B1162B5007953AB /* ItemView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ItemView.h; sourceTree = ""; }; CD6E54FE1B1162B5007953AB /* ItemView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ItemView.m; sourceTree = ""; }; CD7B9F4C1ACB959200DF3C71 /* logoAppleOver.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = logoAppleOver.png; path = images/logoAppleOver.png; sourceTree = SOURCE_ROOT; }; @@ -393,6 +397,8 @@ CD6095501A8329FA00E091CD /* images */ = { isa = PBXGroup; children = ( + CD6AFB801B4737AC00D42C34 /* arrow.png */, + CD6AFB7E1B4736DF00D42C34 /* flatIcon.png */, CDEE77031B41220300763826 /* search.png */, CDEE77041B41220300763826 /* searchBG.png */, CDEE77051B41220300763826 /* searchOver.png */, @@ -655,6 +661,8 @@ CD7B9FA71ACCAE5E00DF3C71 /* startScanOver.png in Resources */, CDF08CC11AC3E8B0009B3423 /* showOver.png in Resources */, CD4D54241B2D082300008030 /* DylibInfoWindow.xib in Resources */, + CD6AFB7F1B4736DF00D42C34 /* flatIcon.png in Resources */, + CD6AFB811B4737AC00D42C34 /* arrow.png in Resources */, CDA81DCC1A9960A3009790E2 /* virusTotalBG.png in Resources */, CD001B381AB903040089014A /* logo.png in Resources */, CDA81D6D1A95B4E9009790E2 /* scanIcon.png in Resources */, diff --git a/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 9233061..823cdb1 100644 --- a/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -36,11 +36,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "732" - endingLineNumber = "732" + startingLineNumber = "728" + endingLineNumber = "728" landmarkName = "-reloadTaskTable" landmarkType = "5"> @@ -116,11 +116,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254554.748014" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "264" - endingLineNumber = "264" + startingLineNumber = "260" + endingLineNumber = "260" landmarkName = "-changeViewController" landmarkType = "5"> @@ -142,27 +142,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "VirusTotal.m" - timestampString = "456607881.829588" + timestampString = "457651407.339698" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "35" - endingLineNumber = "35" - landmarkName = "-addItem:" - landmarkType = "5"> - - - - @@ -174,11 +158,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254554.748014" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "417" - endingLineNumber = "417" + startingLineNumber = "413" + endingLineNumber = "413" landmarkName = "-reloadBottomPane:itemView:" landmarkType = "5"> @@ -190,11 +174,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254554.748014" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "435" - endingLineNumber = "435" + startingLineNumber = "431" + endingLineNumber = "431" landmarkName = "-reloadBottomPane:itemView:" landmarkType = "5"> @@ -318,11 +302,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "1493" - endingLineNumber = "1493" + startingLineNumber = "1489" + endingLineNumber = "1489" landmarkName = "-selectBottomPaneContent:" landmarkType = "5"> @@ -334,23 +318,7 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" - startingColumnNumber = "9223372036854775807" - endingColumnNumber = "9223372036854775807" - startingLineNumber = "1485" - endingLineNumber = "1485" - landmarkName = "-switchView:" - landmarkType = "5"> - - - - + + + + @@ -398,11 +382,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "1694" - endingLineNumber = "1694" + startingLineNumber = "1690" + endingLineNumber = "1690" landmarkName = "-controlTextDidChange:" landmarkType = "5"> @@ -430,11 +414,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "Items/Binary.m" - timestampString = "457135238.550795" + timestampString = "457429064.566221" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "189" - endingLineNumber = "189" + startingLineNumber = "188" + endingLineNumber = "188" landmarkName = "-formatSigningInfo" landmarkType = "5"> @@ -478,11 +462,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "530" - endingLineNumber = "530" + startingLineNumber = "526" + endingLineNumber = "526" landmarkName = "-reloadBinary:" landmarkType = "5"> @@ -494,11 +478,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "559" - endingLineNumber = "559" + startingLineNumber = "555" + endingLineNumber = "555" landmarkName = "-reloadBinary:" landmarkType = "5"> @@ -510,11 +494,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "1173" - endingLineNumber = "1173" + startingLineNumber = "1169" + endingLineNumber = "1169" landmarkName = "-buttonAppearance:shouldReset:" landmarkType = "5"> @@ -526,11 +510,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "1185" - endingLineNumber = "1185" + startingLineNumber = "1181" + endingLineNumber = "1181" landmarkName = "-buttonAppearance:shouldReset:" landmarkType = "5"> @@ -542,11 +526,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "1070" - endingLineNumber = "1070" + startingLineNumber = "1066" + endingLineNumber = "1066" landmarkName = "-mouseEntered:" landmarkType = "5"> @@ -558,11 +542,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "1080" - endingLineNumber = "1080" + startingLineNumber = "1076" + endingLineNumber = "1076" landmarkName = "-mouseExited:" landmarkType = "5"> @@ -574,11 +558,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "633" - endingLineNumber = "633" + startingLineNumber = "629" + endingLineNumber = "629" landmarkName = "-initTrackingAreas" landmarkType = "5"> @@ -590,14 +574,46 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "457254594.166396" + timestampString = "457651596.707976" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "1090" - endingLineNumber = "1090" + startingLineNumber = "1086" + endingLineNumber = "1086" landmarkName = "-buttonAppearance:shouldReset:" landmarkType = "5"> + + + + + + + + diff --git a/UI/RequestRootWindow.xib b/UI/RequestRootWindow.xib index 378c679..de97b9c 100644 --- a/UI/RequestRootWindow.xib +++ b/UI/RequestRootWindow.xib @@ -1,5 +1,5 @@ - + @@ -7,6 +7,10 @@ + + + + @@ -22,27 +26,17 @@ - - + + - - + + - - + + + + + + + @@ -66,7 +84,8 @@ - - + + + diff --git a/VirusTotal.h b/VirusTotal.h index 54d03b8..8a3c3cc 100644 --- a/VirusTotal.h +++ b/VirusTotal.h @@ -17,7 +17,7 @@ /* PROPERTIES */ //array for (up to 25) items -@property (nonatomic, retain)NSMutableArray* items; +@property(nonatomic, retain)NSMutableArray* items; /* METHODS */ diff --git a/VirusTotal.m b/VirusTotal.m index 1438c4d..4b14826 100644 --- a/VirusTotal.m +++ b/VirusTotal.m @@ -25,37 +25,103 @@ { //alloc array for items items = [NSMutableArray array]; + + //kick of thread to watch/flush queue + // ->will flush if item not processed in 30 seconds + [NSThread detachNewThreadSelector:@selector(queueFlusher) toTarget:self withObject:nil]; } return self; } -//TODO: move this in Queue? +//watch queue +// ->manaully flush if any item in for more than 3.0 seconds +-(void)queueFlusher +{ + //last item + Binary* lastItem = nil; + + //items to process + NSMutableArray* vtItems = nil; + + //forever + // ->watch/flush if needed + while(YES) + { + //grab last item + lastItem = [self.items lastObject]; + + //sleep + [NSThread sleepForTimeInterval:3.0]; + + //sync + @synchronized(self.items) + { + //no items? + // ->just loop/re-nap + if(0 == self.items.count) + { + //loop + continue; + } + + //check if last item, is still last + // ->if not, loop/re-nap + if(lastItem != [self.items lastObject]) + { + //loop + continue; + } + + //make copy + vtItems = [NSMutableArray arrayWithArray:self.items]; + + //last item is same after timeout + // ->flush the queue + [NSThread detachNewThreadSelector:@selector(queryVT:) toTarget:self withObject:vtItems]; + + //remove all items + [self.items removeAllObjects]; + } + + }//forever + + return; +} + //add item // ->will query VT when 25 items are hit -(void)addItem:(Binary*)binary { + //items to process + NSMutableArray* vtItems = nil; + //sync @synchronized(self.items) { //add item [self.items addObject:binary]; - } + + //query VT once 25 items have been gathered + if(VT_MAX_QUERY_COUNT == self.items.count) + { + //make copy + vtItems = [NSMutableArray arrayWithArray:self.items]; - //query VT once 25 items have been gathered - // ->or this is a 'last' item - if( (VT_MAX_QUERY_COUNT == self.items.count) || - (YES == binary.lastItem) ) - { - //kick of thread to make a query to VT - [NSThread detachNewThreadSelector:@selector(queryVT) toTarget:self withObject:nil]; - } + //kick of thread to make a query to VT + [NSThread detachNewThreadSelector:@selector(queryVT:) toTarget:self withObject:vtItems]; + + //remove all items + [self.items removeAllObjects]; + } + + }//sync return; } //make query to VT --(void)queryVT +-(void)queryVT:(NSMutableArray*)vtItems { //item data NSMutableDictionary* itemData = nil; @@ -82,12 +148,8 @@ //init query URL queryURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", VT_QUERY_URL, VT_API_KEY]]; - //sync - @synchronized(self.items) - { - //add all binaries to VT query - for(Binary* item in self.items) + for(Binary* item in vtItems) { //skip items with blank hashes // ->TODO not sure why this would happen @@ -121,13 +183,6 @@ //save as queried item queriedItems[item.hashes[KEY_HASH_SHA1]] = item; } - - //remove all items - // ->since they've been added to VT request - [self.items removeAllObjects]; - - }//sync - //make query to VT results = [self postRequest:queryURL parameters:parameters]; @@ -136,7 +191,6 @@ //process results [self processResults:queriedItems results:results]; } - return; } diff --git a/en.lproj/MainMenu.xib b/en.lproj/MainMenu.xib index c0bd247..83a0634 100755 --- a/en.lproj/MainMenu.xib +++ b/en.lproj/MainMenu.xib @@ -1,5 +1,5 @@ - + diff --git a/images/Icon Template.psd b/images/Icon Template.psd new file mode 100644 index 0000000..e8c4c0c Binary files /dev/null and b/images/Icon Template.psd differ diff --git a/images/arrow.png b/images/arrow.png new file mode 100644 index 0000000..edea9c6 Binary files /dev/null and b/images/arrow.png differ diff --git a/images/flatIcon.png b/images/flatIcon.png new file mode 100644 index 0000000..64cfb80 Binary files /dev/null and b/images/flatIcon.png differ