def click_handler(self, event): recording = preferences.getValueForPreference('recording') event_screenshots = preferences.getValueForPreference('eventScreenshots') if event_screenshots: self.sniffer.activity_tracker.take_screenshot() if recording: # check if the clipboard has updated self.sniffer.clr.get_clipboard_contents() # get data ready to write loc = NSEvent.mouseLocation() scr = NSScreen.screens() xmin = 0 ymin = 0 for s in scr: if s.frame().origin.x < xmin: xmin = s.frame().origin.x if s.frame().origin.y < ymin: ymin = s.frame().origin.y x = int(loc.x) - xmin y = int(loc.y) - ymin #get click type click_type = "Unknown" if event.type() == NSLeftMouseDown: click_type = "Left" elif event.type() == NSRightMouseDown: click_type = "Right" # write JSON object to clicklog file text = '{"time": '+ str(cfg.NOW()) + ' , "button": "' + click_type + '", "location": [' + str(x) + ',' + str(y) + ']}' utils_cocoa.write_to_file(text, cfg.CLICKLOG)
def appLaunchCallback_(self, notification): recording = preferences.getValueForPreference('recording') # get event info t = cfg.NOW() app = notification.userInfo()["NSWorkspaceApplicationKey"] name = unicode(app.localizedName()) pid = int(app.processIdentifier()) # create app listener for this app's window events if app.activationPolicy() == 0: mess = acc.create_application_ref(pid = pid) mess.set_callback(self.windowCallback) mess.watch("AXMoved", "AXWindowResized", "AXFocusedWindowChanged", "AXWindowCreated","AXWindowMiniaturized", "AXWindowDeminiaturized") self.watched[pid] = mess if recording: # log that the application launched text = '{"time": '+ str(t) + ' , "type": "Open", "app": "' + name + '"}' utils_cocoa.write_to_file(text, cfg.APPLOG) # log that application is active if app.isActive(): text = '{"time": '+ str(t) + ' , "type": "Active", "app": "' + name + '"}' utils_cocoa.write_to_file(text, cfg.APPLOG) # take a screenshot eventScreenshots = preferences.getValueForPreference('eventScreenshots') if eventScreenshots: self.sniffer.activity_tracker.take_screenshot() # check if the screen geometry changed and update active window self.updateWindowList()
def take_screenshot(self): # check screenshot preferences recording = preferences.getValueForPreference('recording') screenshots_active = preferences.getValueForPreference('screenshots') screenshot_time_min = preferences.getValueForPreference('imageTimeMin') / 1000.0 # take a screenshot if preferences allow if (screenshots_active and recording and (cfg.NOW() - self.last_screenshot) > screenshot_time_min) : try: # get filename filename = datetime.datetime.now().strftime("%y%m%d-%H%M%S%f") y = filename[0:2] m = filename[2:4] d = filename[4:6] h = filename[7:9] folder = os.path.join(cfg.CURRENT_DIR,"screenshots",y,m,d,h) if not os.path.exists(folder): os.makedirs(folder) path = os.path.join(folder,""+filename+".jpg") # take screenshot self.sniffer.screenshot(path) self.last_screenshot = cfg.NOW() except: print "There was an error with saving a screenshot"
def move_handler(self, event): recording = preferences.getValueForPreference('recording') event_screenshots = preferences.getValueForPreference( 'eventScreenshots') if event_screenshots: self.sniffer.activity_tracker.take_screenshot() if recording: if event.type() == NSMouseMoved: loc = NSEvent.mouseLocation() # get all the image size information scr = NSScreen.screens() xmin = 0 ymin = 0 for s in scr: if s.frame().origin.x < xmin: xmin = s.frame().origin.x if s.frame().origin.y < ymin: ymin = s.frame().origin.y x = int(loc.x) - xmin y = int(loc.y) - ymin # write JSON object to movelog file text = '{"time": ' + str(cfg.NOW()) + ' , "location": [' + str( x) + ',' + str(y) + ']}' utils_cocoa.write_to_file(text, cfg.MOVELOG)
def windowCallback(self, **kwargs): recording = preferences.getValueForPreference('recording') if recording: # get event info t = cfg.NOW() notification_title = str(kwargs['notification'])[2:-1] # remove 'AX' from front of event names before we save that info # TODO this app title may not match what we get from app.localizedName() # find way to reconcile app_title = unicode(kwargs['element']['AXTitle']) # take screenshot eventScreenshots = preferences.getValueForPreference('eventScreenshots') if eventScreenshots: self.sniffer.activity_tracker.take_screenshot() # when miniaturized, we may not be able to get window title and position data if notification_title == "WindowMiniaturized": # write to window log file about event text = '{"time": '+ str(t) + ' , "type": "' + notification_title + '", "app": "' + app_title + '"}' utils_cocoa.write_to_file(text, cfg.WINDOWLOG) # all other events should let us get title and postiion data else: # get the relevant window data title = unicode(kwargs['element']['AXFocusedWindow']['AXTitle']) position = str(kwargs['element']['AXFocusedWindow']['AXPosition']) size = str(kwargs['element']['AXFocusedWindow']['AXSize']) # write to window log file about event text = '{"time": ' + str(t) + ' , "type": "' + notification_title + '", "app": "' + app_title + '", "window": "' + title + '", "position": ' + position + ' , "size": ' + size +' }' utils_cocoa.write_to_file(text, cfg.WINDOWLOG) # get most recent screen geometry and update active window self.updateWindowList()
def move_handler(self, event): recording = preferences.getValueForPreference('recording') event_screenshots = preferences.getValueForPreference('eventScreenshots') if event_screenshots: self.sniffer.activity_tracker.take_screenshot() if recording: if event.type() == NSMouseMoved: loc = NSEvent.mouseLocation() # get all the image size information scr = NSScreen.screens() xmin = 0 ymin = 0 for s in scr: if s.frame().origin.x < xmin: xmin = s.frame().origin.x if s.frame().origin.y < ymin: ymin = s.frame().origin.y x = int(loc.x) - xmin y = int(loc.y) - ymin # write JSON object to movelog file text = '{"time": '+ str(cfg.NOW()) + ' , "location": [' + str(x) + ',' + str(y) + ']}' utils_cocoa.write_to_file(text, cfg.MOVELOG)
def chromeCallback(self, **kwargs): recording = preferences.getValueForPreference('recording') if recording: # get event info t = cfg.NOW() notification_title = str( kwargs['notification'] )[2:] # remove 'AX' from front of event names before we save that info if notification_title != "MenuItemSelected" and notification_title != "TitleChanged": self.AppRecorder.windowCallback(**kwargs) return Chrome = SBApplication.applicationWithBundleIdentifier_( "com.google.Chrome") windows = Chrome.windows() oldTabList = self.tabList self.tabList = {} #TODO check if we get all tabs from multiple windows. # Is tab.id uniqe across all windows, or duplicated (i.e. window 1 # has tabs 1, 2, 3 and window 2 has tabs 1, 2, 3, 4) for window in windows: tabs = window.tabs() for tab in tabs: self.tabList[tab.id()] = { 'title': str(tab.title()), 'id': str(tab.id()), 'url': str(tab.URL()), 'host': urlparse.urlparse(str(tab.URL())).hostname } closedTabs = set(oldTabList.keys()) - set(self.tabList.keys()) openedTabs = set(self.tabList.keys()) - set(oldTabList.keys()) # take screenshot eventScreenshots = preferences.getValueForPreference( 'eventScreenshots') if eventScreenshots: self.AppRecorder.sniffer.activity_tracker.take_screenshot() # get list of urls from the database db_urls = session.query(URL).all() urls = [u.url for u in db_urls] # check if any recently opened tabs are not yet in the database for t in openedTabs: if tabList[t]['url'] not in urls: url_to_add = URL(t, app_name) session.add(url_to_add) #TODO need to find way to commit! activity_tracker.storage.sqlcommit() # update our local url list db_urls = session.query(URL).all() urls = [u.url for u in db_urls]
def scroll_handler(self, event): recording = preferences.getValueForPreference('recording') event_screenshots = preferences.getValueForPreference('eventScreenshots') if event_screenshots: self.sniffer.activity_tracker.take_screenshot() if recording: if event.type() == NSScrollWheel: # write JSON object to scrolllog file text = '{"time": '+ str(cfg.NOW()) + ' , "distance": [' + str(event.deltaX()) + ',' + str(event.deltaY()) + '], "window_number": ' + str(event.windowNumber()) + '}' utils_cocoa.write_to_file(text, cfg.SCROLLLOG)
def run_screenshot_loop(self): # take a screenshot if computer is idle and appropriate amount of time has passed screenshot_time_max = preferences.getValueForPreference('imageTimeMax') periodic = preferences.getValueForPreference('periodicScreenshots') time_since_last_screenshot = cfg.NOW() - self.last_screenshot if (time_since_last_screenshot > screenshot_time_max): if periodic: self.take_screenshot() time_since_last_screenshot = 0.0 sleep_time = screenshot_time_max - time_since_last_screenshot + 0.001 # add a milisecond for good measure self.screenshotTimer = threading.Timer(sleep_time,self.run_screenshot_loop) self.screenshotTimer.start()
def wakeCallback_(self, notification): recording = preferences.getValueForPreference('recording') if recording: t = cfg.NOW() text = '{"time": '+ str(t) + ' , "type": "Wake"}' utils_cocoa.write_to_file(text, cfg.RECORDERLOG) # take a screenshot eventScreenshots = preferences.getValueForPreference('eventScreenshots') if eventScreenshots: self.sniffer.activity_tracker.take_screenshot() # get updated list of applications and windows self.updateWindowList()
def scroll_handler(self, event): recording = preferences.getValueForPreference('recording') event_screenshots = preferences.getValueForPreference( 'eventScreenshots') if event_screenshots: self.sniffer.activity_tracker.take_screenshot() if recording: if event.type() == NSScrollWheel: # write JSON object to scrolllog file text = '{"time": ' + str(cfg.NOW()) + ' , "distance": [' + str( event.deltaX()) + ',' + str( event.deltaY()) + '], "window_number": ' + str( event.windowNumber()) + '}' utils_cocoa.write_to_file(text, cfg.SCROLLLOG)
def chromeCallback(self, **kwargs): recording = preferences.getValueForPreference('recording') if recording: # get event info t = cfg.NOW() notification_title = str(kwargs['notification'])[2:] # remove 'AX' from front of event names before we save that info if notification_title != "MenuItemSelected" and notification_title != "TitleChanged": self.AppRecorder.windowCallback(**kwargs) return Chrome = SBApplication.applicationWithBundleIdentifier_("com.google.Chrome") windows = Chrome.windows() oldTabList = self.tabList self.tabList = {} #TODO check if we get all tabs from multiple windows. # Is tab.id uniqe across all windows, or duplicated (i.e. window 1 # has tabs 1, 2, 3 and window 2 has tabs 1, 2, 3, 4) for window in windows: tabs = window.tabs() for tab in tabs: self.tabList[tab.id()] = {'title': str(tab.title()), 'id': str(tab.id()), 'url': str(tab.URL()), 'host': urlparse.urlparse(str(tab.URL())).hostname} closedTabs = set(oldTabList.keys()) - set(self.tabList.keys()) openedTabs = set(self.tabList.keys()) - set(oldTabList.keys()) # take screenshot eventScreenshots = preferences.getValueForPreference('eventScreenshots') if eventScreenshots: self.AppRecorder.sniffer.activity_tracker.take_screenshot() # get list of urls from the database db_urls = session.query(URL).all() urls = [u.url for u in db_urls] # check if any recently opened tabs are not yet in the database for t in openedTabs: if tabList[t]['url'] not in urls: url_to_add = URL(t, app_name) session.add(url_to_add) #TODO need to find way to commit! activity_tracker.storage.sqlcommit() # update our local url list db_urls = session.query(URL).all() urls = [u.url for u in db_urls]
def stop_app_observers(self): recording = preferences.getValueForPreference('recording') t = cfg.NOW() # get workspace and list of all applications workspace = NSWorkspace.sharedWorkspace() activeApps = workspace.runningApplications() # let app observers be garabage collected self.watched = {} if recording: # prune list of applications to apps that appear in the dock regularApps = [] for app in activeApps: if app.activationPolicy() == 0: regularApps.append(app) # listen for window events of these applications for app in regularApps: name = unicode(app.localizedName()) # log that the app recording will stop text = '{"time": '+ str(t) + ' , "type": "Close", "app": "' + name + '"}' utils_cocoa.write_to_file(text, cfg.APPLOG) if app.isActive(): text = '{"time": '+ str(t) + ' , "type": "Inactive", "app": "' + name + '"}' utils_cocoa.write_to_file(text, cfg.APPLOG) # write a blank line to the geometry table to close out all windows text = '{"time": ' + str(t) + ', "geometry": {} }' utils_cocoa.write_to_file(text, cfg.GEOLOG)
def appTerminateCallback_(self, notification): recording = preferences.getValueForPreference('recording') # get event info t = cfg.NOW() app = notification.userInfo()["NSWorkspaceApplicationKey"] #TODO find out why we are getting no name from the app terminate name = unicode(app.localizedName()) pid = int(app.processIdentifier()) # let app listener be garbage collected by removing our saved reference to it if pid in self.watched.keys(): del self.watched[pid] # watcher = self.watched[p] if recording: # log the the application has closed text = '{"time": '+ str(t) + ' , "type": "Close", "app": "' + name + '"}' utils_cocoa.write_to_file(text, cfg.APPLOG) #TODO throw app deactivate event when active application terminates? # would have to tell what app this is, and if it was the most # previously active app if unicode(app.localizedName()) == "\"Google Chrome\"": self.wr.closeChrome() # check if the screen geometry changed and update active window self.updateWindowList()
def checkLoops(self): # if we're recording, start the screenshot and parsing loops recording = preferences.getValueForPreference('recording') if(recording): self.startLoops() else: self.stopLoops()
def safariCallback(self, **kwargs): recording = preferences.getValueForPreference('recording') if recording: notification_title = str(kwargs['notification'])[2:] if notification_title != "MenuItemSelected" and notification_title != "TitleChanged": self.AppRecorder.windowCallback(**kwargs) if self.lastSafariTime == None: self.lastSafariTime = int(utils_cocoa.unix_to_safari(cfg.NOW())) return conn = sqlite3.connect(os.path.expanduser('~/Library/Safari/History.db')) c = conn.cursor() c.execute("SELECT visit_time, title, url FROM history_visits LEFT JOIN history_items ON history_visits.history_item == history_items.id where visit_time > " + str(self.lastSafariTime)) # looks like safari writes every ~10 seconds so offset by 10. Add an extra 0.1 sec for roundoff issues safariTimeOffset = int(utils_cocoa.unix_to_safari(cfg.NOW())) - 9.9 if self.lastSafariTime < safariTimeOffset: self.lastSafariTime = safariTimeOffset for entry in c.fetchall(): url = entry[2] title = entry[1] time = entry[0] if self.lastSafariTime < time: self.lastSafariTime = time text = '{"time": ' + str(utils_cocoa.safari_to_unix(time)) + ', "browser": "Safari", "url": "' + str(url) + '", "title": "' + str(title) + '", "event": ' + '"Open"' +' }' utils_cocoa.write_to_file(text, cfg.URLLOG) if not recording: self.lastSafariTime = None
def getChromeURLs(self, active): recording = preferences.getValueForPreference('recording') if recording: Chrome = SBApplication.applicationWithBundleIdentifier_("com.google.Chrome") windows = Chrome.windows() windowList = {} tabList = {} # set first window to active if browser is active firstWindow = active for window in windows: #TODO need to find way to tell if app is onscreen cwid = window.id() name = 'Chrome ' + str(cwid) bounds = {'x':window.bounds().origin.x, 'y':window.bounds().origin.y, 'width':window.bounds().size.width, 'height':window.bounds().size.height} tabs = window.tabs() if firstWindow: firstWindow = False for tab in tabs: activeTab = True if tab.id() == window.activeTab().id() else False tabList[tab.id()] = {'active': activeTab, 'title': unicode(tab.title()), 'url': unicode(tab.URL()), 'host': urlparse.urlparse(unicode(tab.URL())).hostname} windowList[cwid] = {'active':firstWindow, 'name': unicode(name), 'bounds': bounds, 'tabs': tabList} tabList = {} return windowList
def appActivateCallback_(self, notification): recording = preferences.getValueForPreference('recording') # get event info t = cfg.NOW() app = notification.userInfo()["NSWorkspaceApplicationKey"] name = unicode(app.localizedName()) pid = int(app.processIdentifier()) # log that the application has become active if pid in self.watched.keys() and recording: text = '{"time": '+ str(t) + ' , "type": "Active", "app": "' + name + '"}' utils_cocoa.write_to_file(text, cfg.APPLOG) # take screenshot eventScreenshots = preferences.getValueForPreference('eventScreenshots') if eventScreenshots: self.sniffer.activity_tracker.take_screenshot() # check if the screen geometry changed and update active window self.updateWindowList()
def firefoxCallback(self, **kwargs): recording = preferences.getValueForPreference('recording') if recording: notification_title = str(kwargs['notification'])[2:] if notification_title != "MenuItemSelected" and notification_title != "TitleChanged": self.AppRecorder.windowCallback(**kwargs) if self.lastFirefoxTime == None: self.lastFirefoxTime = int( utils_cocoa.unix_to_firefox(cfg.NOW())) return profiles = [] profFile = open( os.path.expanduser( '~/Library/Application Support/Firefox/profiles.ini')) for line in profFile: if line[:5] == 'Path=': profiles.append(line[5:-1]) for profile in profiles: conn = sqlite3.connect( os.path.expanduser( '~/Library/Application Support/Firefox/' + profile + '/places.sqlite')) c = conn.cursor() c.execute( "SELECT visit_date, title, url FROM moz_historyvisits LEFT JOIN moz_places ON moz_historyvisits.place_id == moz_places.id where visit_date > " + str(self.lastFirefoxTime)) # offset by 10. Add an extra 0.1 sec for roundoff issues ffTimeOffset = int(utils_cocoa.unix_to_firefox( cfg.NOW())) - 9.9 if self.lastFirefoxTime < ffTimeOffset: self.lastFirefoxTime = ffTimeOffset for entry in c.fetchall(): url = entry[2] title = entry[1] time = entry[0] if self.lastFirefoxTime < time: self.lastFirefoxTime = time text = '{"time": ' + str(utils_cocoa.firefox_to_unix( time)) + ', "browser": "Firefox", "url": "' + str( url) + '", "title": "' + str( title) + '", "event": ' + '"Open"' + ' }' utils_cocoa.write_to_file(text, cfg.URLLOG) if not recording: self.lastFirefoxTime = None
def get_clipboard_contents(self): # only store data if we have new items recording = preferences.getValueForPreference('recording') if recording: clip = NSPasteboard.generalPasteboard() count = clip.changeCount() if self.last_count == None: self.last_count = count elif count != self.last_count: self.last_count = count text = "" url = "" image = None path = "" items = clip.pasteboardItems() for i in items: t = i.types() # get the textual data if 'public.utf8-plain-text' in t: text = i.stringForType_('public.utf8-plain-text') if 'public.url' in t: url = i.stringForType_('public.url') if 'public.url-name' in t: url = i.stringForType_('public.url-name') if 'public.file-url' in t: url = i.stringForType_('public.file-url') # get image data if 'public.tiff' in t: image = i.dataForType_('public.tiff') if 'public.png' in t: image = i.dataForType_('public.png') if 'public.jpeg' in t: image = i.dataForType_('public.jpeg') # save image file if we have one if image != None: folder = os.path.join(cfg.CURRENT_DIR, "clipboard") filename = datetime.datetime.now().strftime( "%y%m%d-%H%M%S%f") path = os.path.join(folder, "" + filename + ".jpg") image.writeToFile_atomically_(path, False) # clean up text and url text = json.dumps(text) url = json.dumps(url) # save to a clipboard file tex = '{"time": ' + str( cfg.NOW() ) + ' , "text": ' + text + ' , "url": ' + url + ' , "image": "' + path + '"}' utils_cocoa.write_to_file(tex, cfg.CLIPLOG)
def click_handler(self, event): recording = preferences.getValueForPreference('recording') event_screenshots = preferences.getValueForPreference( 'eventScreenshots') if event_screenshots: self.sniffer.activity_tracker.take_screenshot() if recording: # check if the clipboard has updated self.sniffer.clr.get_clipboard_contents() # get data ready to write loc = NSEvent.mouseLocation() scr = NSScreen.screens() xmin = 0 ymin = 0 for s in scr: if s.frame().origin.x < xmin: xmin = s.frame().origin.x if s.frame().origin.y < ymin: ymin = s.frame().origin.y x = int(loc.x) - xmin y = int(loc.y) - ymin #get click type click_type = "Unknown" if event.type() == NSLeftMouseDown: click_type = "Left" elif event.type() == NSRightMouseDown: click_type = "Right" # write JSON object to clicklog file text = '{"time": ' + str(cfg.NOW( )) + ' , "button": "' + click_type + '", "location": [' + str( x) + ',' + str(y) + ']}' utils_cocoa.write_to_file(text, cfg.CLICKLOG)
def get_clipboard_contents(self): # only store data if we have new items recording = preferences.getValueForPreference('recording') if recording: clip = NSPasteboard.generalPasteboard() count = clip.changeCount() if self.last_count == None: self.last_count = count elif count != self.last_count: self.last_count = count text = "" url = "" image = None path = "" items = clip.pasteboardItems() for i in items: t = i.types() # get the textual data if 'public.utf8-plain-text' in t: text = i.stringForType_('public.utf8-plain-text') if 'public.url' in t: url = i.stringForType_('public.url') if 'public.url-name' in t: url = i.stringForType_('public.url-name') if 'public.file-url' in t: url = i.stringForType_('public.file-url') # get image data if 'public.tiff' in t: image = i.dataForType_('public.tiff') if 'public.png' in t: image = i.dataForType_('public.png') if 'public.jpeg' in t: image = i.dataForType_('public.jpeg') # save image file if we have one if image != None: folder = os.path.join(cfg.CURRENT_DIR,"clipboard") filename = datetime.datetime.now().strftime("%y%m%d-%H%M%S%f") path = os.path.join(folder,""+filename+".jpg") image.writeToFile_atomically_(path ,False) # clean up text and url text = json.dumps(text) url = json.dumps(url) # save to a clipboard file tex = '{"time": '+ str(cfg.NOW()) + ' , "text": ' + text + ' , "url": ' + url + ' , "image": "' + path + '"}' utils_cocoa.write_to_file(tex, cfg.CLIPLOG)
def sqlcommit(self): self.last_commit = cfg.NOW() for _ in xrange(1000): try: self.session.commit() break except sqlalchemy.exc.OperationalError: # pause recording if(preferences.getValueForPreference("recording")): self.activity_tracker.sniffer.delegate.toggleLogging_(self) self.session.rollback() # show modal alert print "Database operational error. Your storage device may be full. Turning off Selfspy recording." utils_cocoa.show_alert("Database operational error. Your storage device may be full. Turning off Selfspy recording.") break except: raise print "Rollback database" self.session.rollback()
def getChromeURLs(self, active): recording = preferences.getValueForPreference('recording') if recording: Chrome = SBApplication.applicationWithBundleIdentifier_( "com.google.Chrome") windows = Chrome.windows() windowList = {} tabList = {} # set first window to active if browser is active firstWindow = active for window in windows: #TODO need to find way to tell if app is onscreen cwid = window.id() name = 'Chrome ' + str(cwid) bounds = { 'x': window.bounds().origin.x, 'y': window.bounds().origin.y, 'width': window.bounds().size.width, 'height': window.bounds().size.height } tabs = window.tabs() if firstWindow: firstWindow = False for tab in tabs: activeTab = True if tab.id() == window.activeTab().id( ) else False tabList[tab.id()] = { 'active': activeTab, 'title': unicode(tab.title()), 'url': unicode(tab.URL()), 'host': urlparse.urlparse(unicode(tab.URL())).hostname } windowList[cwid] = { 'active': firstWindow, 'name': unicode(name), 'bounds': bounds, 'tabs': tabList } tabList = {} return windowList
def sqlcommit(self): self.last_commit = cfg.NOW() for _ in xrange(1000): try: self.session.commit() break except sqlalchemy.exc.OperationalError: # pause recording if (preferences.getValueForPreference("recording")): self.activity_tracker.sniffer.delegate.toggleLogging_(self) self.session.rollback() # show modal alert print "Database operational error. Your storage device may be full. Turning off Selfspy recording." utils_cocoa.show_alert( "Database operational error. Your storage device may be full. Turning off Selfspy recording." ) break except: raise print "Rollback database" self.session.rollback()
def appDeactivateCallback_(self, notification): recording = preferences.getValueForPreference('recording') # get event info t = cfg.NOW() app = notification.userInfo()["NSWorkspaceApplicationKey"] # only save the info if we have app information # we don't get app information when this is thrown after an app closes if app.processIdentifier() == -1: return name = unicode(app.localizedName()) pid = int(app.processIdentifier()) # log that the application has become inactive if pid in self.watched.keys() and recording: text = '{"time": '+ str(t) + ' , "type": "Inactive", "app": "' + name + '"}' utils_cocoa.write_to_file(text, cfg.APPLOG) # check if the screen geometry changed and update active window self.updateWindowList()
def safariCallback(self, **kwargs): recording = preferences.getValueForPreference('recording') if recording: notification_title = str(kwargs['notification'])[2:] if notification_title != "MenuItemSelected" and notification_title != "TitleChanged": self.AppRecorder.windowCallback(**kwargs) if self.lastSafariTime == None: self.lastSafariTime = int(utils_cocoa.unix_to_safari( cfg.NOW())) return conn = sqlite3.connect( os.path.expanduser('~/Library/Safari/History.db')) c = conn.cursor() c.execute( "SELECT visit_time, title, url FROM history_visits LEFT JOIN history_items ON history_visits.history_item == history_items.id where visit_time > " + str(self.lastSafariTime)) # looks like safari writes every ~10 seconds so offset by 10. Add an extra 0.1 sec for roundoff issues safariTimeOffset = int(utils_cocoa.unix_to_safari(cfg.NOW())) - 9.9 if self.lastSafariTime < safariTimeOffset: self.lastSafariTime = safariTimeOffset for entry in c.fetchall(): url = entry[2] title = entry[1] time = entry[0] if self.lastSafariTime < time: self.lastSafariTime = time text = '{"time": ' + str(utils_cocoa.safari_to_unix( time)) + ', "browser": "Safari", "url": "' + str( url) + '", "title": "' + str( title) + '", "event": ' + '"Open"' + ' }' utils_cocoa.write_to_file(text, cfg.URLLOG) if not recording: self.lastSafariTime = None
def toggleLogging_(self, notification): print "Toggle Recording" t = cfg.NOW() recording = preferences.getValueForPreference('recording') recording = not recording NSUserDefaultsController.sharedUserDefaultsController().defaults().setBool_forKey_(recording,'recording') self.activity_tracker.checkLoops() #change text and enabled status of screenshot menu item if recording: self.loggingMenuItem.setTitle_("Pause Recording") sc.ar.unpause_app_observers() text = '{"time": '+ str(t) + ' , "type": "Unpause"}' utils_cocoa.write_to_file(text, cfg.RECORDERLOG) else: self.loggingMenuItem.setTitle_("Start Recording") sc.ar.pause_app_observers() text = '{"time": '+ str(t) + ' , "type": "Pause"}' utils_cocoa.write_to_file(text, cfg.RECORDERLOG) self.changeIcon()
def firefoxCallback(self, **kwargs): recording = preferences.getValueForPreference('recording') if recording: notification_title = str(kwargs['notification'])[2:] if notification_title != "MenuItemSelected" and notification_title != "TitleChanged": self.AppRecorder.windowCallback(**kwargs) if self.lastFirefoxTime == None: self.lastFirefoxTime = int(utils_cocoa.unix_to_firefox(cfg.NOW())) return profiles = [] profFile = open(os.path.expanduser('~/Library/Application Support/Firefox/profiles.ini')) for line in profFile: if line[:5] == 'Path=': profiles.append(line[5:-1]) for profile in profiles: conn = sqlite3.connect(os.path.expanduser('~/Library/Application Support/Firefox/' + profile + '/places.sqlite')) c = conn.cursor() c.execute("SELECT visit_date, title, url FROM moz_historyvisits LEFT JOIN moz_places ON moz_historyvisits.place_id == moz_places.id where visit_date > " + str(self.lastFirefoxTime)) # offset by 10. Add an extra 0.1 sec for roundoff issues ffTimeOffset = int(utils_cocoa.unix_to_firefox(cfg.NOW())) - 9.9 if self.lastFirefoxTime < ffTimeOffset: self.lastFirefoxTime = ffTimeOffset for entry in c.fetchall(): url = entry[2] title = entry[1] time = entry[0] if self.lastFirefoxTime < time: self.lastFirefoxTime = time text = '{"time": ' + str(utils_cocoa.firefox_to_unix(time)) + ', "browser": "Firefox", "url": "' + str(url) + '", "title": "' + str(title) + '", "event": ' + '"Open"' +' }' utils_cocoa.write_to_file(text, cfg.URLLOG) if not recording: self.lastFirefoxTime = None
def updateWindowList(self): # get an early timestamp recording = preferences.getValueForPreference('recording') t = cfg.NOW() if recording: # clear our past geometry and active window self.apps_and_windows = {} active_window = None # get list of applications that show up in the dock workspace = NSWorkspace.sharedWorkspace() activeApps = workspace.runningApplications() regularApps = [] for app in activeApps: if app.activationPolicy() == 0: regularApps.append(app) # save app info to dictionary for app in regularApps: name = unicode(app.localizedName()) active = app.isActive() pid = app.processIdentifier() d = {'name': name, 'active': active, 'windows':{}} # get window and tab info for browsers if name == 'Google Chrome': d['windows'] = self.wr.getChromeURLs(active) if pid not in self.browser_pids: self.browser_pids.append(pid) elif name == 'Safari': d['windows'] = self.wr.getSafariURLs(active) if pid not in self.browser_pids: self.browser_pids.append(pid) self.apps_and_windows[int(pid)] = d # store app data by pid # get title of the active window if possible if active: try: mess = self.watched[pid] active_window = unicode(mess['AXFocusedWindow']['AXTitle']) except: pass # add list of current windows options = kCGWindowListOptionAll + kCGWindowListExcludeDesktopElements windows = CGWindowListCopyWindowInfo(options, kCGNullWindowID) for window in windows: try: # get window data owning_app_pid = window['kCGWindowOwnerPID'] # don't record window data if for a browser if owning_app_pid in self.browser_pids: continue window_layer = window['kCGWindowLayer'] name = unicode(window['kCGWindowName']) # window_id = window['kCGWindowNumber'] window_id = str(window['kCGWindowNumber']) if window_id[-1] == "L": window_id = window_id[0:-1] window_id = int(window_id) bounds = window['kCGWindowBounds'] win_bounds = {'width':bounds['Width'], 'height':bounds['Height'], 'x':bounds['X'], 'y':bounds['Y']} active = False if unicode(window['kCGWindowName']) == active_window: active = True on_screen = False if 'kCGWindowIsOnscreen' in window.keys(): on_screen = window['kCGWindowIsOnscreen'] # unless it has a name and is on the top layer, we don't count it if owning_app_pid in self.apps_and_windows and window_layer == 0 and name: # add window data to the app_window dictionary window_dict = {'name': name, 'bounds': win_bounds, 'active': active, 'onscreen': on_screen} self.apps_and_windows[owning_app_pid]['windows'][window_id] = window_dict except: pass # write self.apps_and_windows to a geometry file text = '{"time": ' + str(t) + ', "geometry": ' + str(self.apps_and_windows) + "}" utils_cocoa.write_to_file(text, cfg.GEOLOG)
def sleepCallback_(self, notification): recording = preferences.getValueForPreference('recording') if recording: t = cfg.NOW() text = '{"time": '+ str(t) + ' , "type": "Sleep"}' utils_cocoa.write_to_file(text, cfg.RECORDERLOG)
def start_app_observers(self): recording = preferences.getValueForPreference('recording') # prompt user to grant accessibility access to Traces, if not already granted acc.is_enabled() # get an early timestamp t = cfg.NOW() # create listeners for application events workspace = NSWorkspace.sharedWorkspace() nc = workspace.notificationCenter() s = objc.selector(self.appLaunchCallback_,signature='v@:@') nc.addObserver_selector_name_object_(self, s, 'NSWorkspaceDidLaunchApplicationNotification', None) s = objc.selector(self.appTerminateCallback_,signature='v@:@') nc.addObserver_selector_name_object_(self, s, 'NSWorkspaceDidTerminateApplicationNotification', None) s = objc.selector(self.appActivateCallback_,signature='v@:@') nc.addObserver_selector_name_object_(self, s, 'NSWorkspaceDidActivateApplicationNotification', None) s = objc.selector(self.appDeactivateCallback_,signature='v@:@') nc.addObserver_selector_name_object_(self, s, 'NSWorkspaceDidDeactivateApplicationNotification', None) # create listeners for system events s = objc.selector(self.wakeCallback_,signature='v@:@') nc.addObserver_selector_name_object_(self, s, 'NSWorkspaceDidWakeNotification', None) s = objc.selector(self.sleepCallback_,signature='v@:@') nc.addObserver_selector_name_object_(self, s, 'NSWorkspaceWillSleepNotification', None) # other events that may be useful to track in the future # https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSWorkspace_Class/ # NSWorkspaceDidHideApplicationNotification # NSWorkspaceDidUnhideApplicationNotification # NSWorkspaceActiveSpaceDidChangeNotification # NSWorkspaceWillPowerOffNotification # NSWorkspaceDidPerformFileOperationNotification # get list of active applications activeApps = workspace.runningApplications() regularApps = [] for app in activeApps: if app.activationPolicy() == 0: # those that show up in the Dock regularApps.append(app) # listen for window events of these applications for app in regularApps: try: p = int(app.processIdentifier()) name = unicode(app.localizedName()) mess = acc.create_application_ref(pid=p) mess.set_callback(self.windowCallback) mess.watch("AXMoved", "AXWindowResized", "AXFocusedWindowChanged", "AXWindowCreated","AXWindowMiniaturized", "AXWindowDeminiaturized") # AXMainWindowChanged self.watched[p] = mess # we need to maintain the listener or it will be deleted on cleanup if recording: # log that the app is open text = '{"time": '+ str(t) + ' , "type": "Open", "app": "' + name + '"}' utils_cocoa.write_to_file(text, cfg.APPLOG) if app.isActive(): text = '{"time": '+ str(t) + ' , "type": "Active", "app": "' + name + '"}' utils_cocoa.write_to_file(text, cfg.APPLOG) except: raise print "Could not create event listener for application: " + str(name) # get inital list of windows and add window listeners self.updateWindowList() # start event loop to track events from other applications CFRunLoopRun()
def key_handler(self, event): # neither of these is returning a valid value, just None and 0 # print event.window() # print event.windowNumber() recording = preferences.getValueForPreference('recording') record_keystrokes = preferences.getValueForPreference('keystrokes') if recording and record_keystrokes: event_screenshots = preferences.getValueForPreference('eventScreenshots') if event_screenshots: self.sniffer.activity_tracker.take_screenshot() if event.type() == NSKeyDown: # get list of applications that show up in the dock # workspace = NSWorkspace.sharedWorkspace() # activeApps = workspace.runningApplications() # app_name = "" # window_name = "" # for app in activeApps: # if app.isActive(): # app_name = app.localizedName() # # print type(app) # # window_name = app.windows() # # print app_name # # print window_name # # options = kCGWindowListOptionAll + kCGWindowListExcludeDesktopElements # windows = CGWindowListCopyWindowInfo(options, kCGNullWindowID) # print windows[0].keys() # for window in windows: # activeApp = utils_cocoa.ascii_encode(NSWorkspace.sharedWorkspace().activeApplication()['NSApplicationName']) # not working because I'm not calling it on the app itself # print NSApp.keyWindow() # print utils_cocoa.ascii_encode(NSWorkspace.sharedWorkspace().activeApplication().keys()) # keyWindow = utils_cocoa.ascii_encode(NSWorkspace.sharedWorkspace().activeApplication().keyWindow()) # get the modifier keys that were depressed when this key was pressed flags = event.modifierFlags() modifiers = [] # OS X api doesn't care it if is left or right if flags & NSControlKeyMask: modifiers.append('Ctrl') if flags & NSAlternateKeyMask: modifiers.append('Alt') if flags & NSCommandKeyMask: modifiers.append('Cmd') if flags & (NSShiftKeyMask | NSAlphaShiftKeyMask): modifiers.append('Shift') # TODO determine why we have to use keycodes here for enter and # backspace but can rely on the charachter list for the others # get the charachter of the pressed key character = event.charactersIgnoringModifiers() if event.keyCode() is 51: character = "Backspace" elif event.keyCode() is 8 and modifiers == ["Cmd"]: # trigger clipboard check self.sniffer.clr.get_clipboard_contents() elif event.keyCode() is 36: character = "Enter" elif event.keyCode() is 39 and modifiers == ["Shift"]: character = "\\\"" elif event.keyCode() is 42 and modifiers != ["Shift"]: character = "\\\\" elif event.keyCode() is 46 and modifiers == ["Cmd", "Shift"]: self.sniffer.delegate.showExperience_("Fake Notification") string = KEYCODES.get(character, character) if string in SKIP_MODIFIERS: return # write JSON object to keylog file text = '{"time": '+ str(cfg.NOW()) + ' , "key": "' + string + '" , "modifiers": ' + str(modifiers) + '}' utils_cocoa.write_to_file(text, cfg.KEYLOG)
def changeIcon(self): record = preferences.getValueForPreference('recording') if(record): self.statusitem.setImage_(self.icon) else: self.statusitem.setImage_(self.iconGray)
def key_handler(self, event): # neither of these is returning a valid value, just None and 0 # print event.window() # print event.windowNumber() recording = preferences.getValueForPreference('recording') record_keystrokes = preferences.getValueForPreference('keystrokes') if recording and record_keystrokes: event_screenshots = preferences.getValueForPreference( 'eventScreenshots') if event_screenshots: self.sniffer.activity_tracker.take_screenshot() if event.type() == NSKeyDown: # get list of applications that show up in the dock # workspace = NSWorkspace.sharedWorkspace() # activeApps = workspace.runningApplications() # app_name = "" # window_name = "" # for app in activeApps: # if app.isActive(): # app_name = app.localizedName() # # print type(app) # # window_name = app.windows() # # print app_name # # print window_name # # options = kCGWindowListOptionAll + kCGWindowListExcludeDesktopElements # windows = CGWindowListCopyWindowInfo(options, kCGNullWindowID) # print windows[0].keys() # for window in windows: # activeApp = utils_cocoa.ascii_encode(NSWorkspace.sharedWorkspace().activeApplication()['NSApplicationName']) # not working because I'm not calling it on the app itself # print NSApp.keyWindow() # print utils_cocoa.ascii_encode(NSWorkspace.sharedWorkspace().activeApplication().keys()) # keyWindow = utils_cocoa.ascii_encode(NSWorkspace.sharedWorkspace().activeApplication().keyWindow()) # get the modifier keys that were depressed when this key was pressed flags = event.modifierFlags() modifiers = [] # OS X api doesn't care it if is left or right if flags & NSControlKeyMask: modifiers.append('Ctrl') if flags & NSAlternateKeyMask: modifiers.append('Alt') if flags & NSCommandKeyMask: modifiers.append('Cmd') if flags & (NSShiftKeyMask | NSAlphaShiftKeyMask): modifiers.append('Shift') # TODO determine why we have to use keycodes here for enter and # backspace but can rely on the charachter list for the others # get the charachter of the pressed key character = event.charactersIgnoringModifiers() if event.keyCode() is 51: character = "Backspace" elif event.keyCode() is 8 and modifiers == ["Cmd"]: # trigger clipboard check self.sniffer.clr.get_clipboard_contents() elif event.keyCode() is 36: character = "Enter" elif event.keyCode() is 39 and modifiers == ["Shift"]: character = "\\\"" elif event.keyCode() is 42 and modifiers != ["Shift"]: character = "\\\\" elif event.keyCode() is 46 and modifiers == ["Cmd", "Shift"]: self.sniffer.delegate.showExperience_("Fake Notification") string = KEYCODES.get(character, character) if string in SKIP_MODIFIERS: return # write JSON object to keylog file text = '{"time": ' + str(cfg.NOW( )) + ' , "key": "' + string + '" , "modifiers": ' + str( modifiers) + '}' utils_cocoa.write_to_file(text, cfg.KEYLOG)