Example #1
0
	def run(self):
		# set up the application
		self.app = NSApplication.sharedApplication()
		self.delegate = self.createAppDelegate().alloc().init()
		self.delegate.activity_tracker = self.activity_tracker
		self.app.setDelegate_(self.delegate)
		self.app.setActivationPolicy_(NSApplicationActivationPolicyAccessory)
		self.workspace = NSWorkspace.sharedWorkspace()

		# start listeners for event recorders
		self.cr = ClickRecorder(self)
		self.cr.start_click_listener()
		self.kr = KeyRecorder(self)
		self.kr.start_key_listener()
		self.mr = MoveRecorder(self)
		self.mr.start_move_listener()
		self.sr = ScrollRecorder(self)
		self.sr.start_scroll_listener()
		self.clr = ClipboardRecorder(self)
		# AppRecorder starts accessibility listeners that need to be on a
		# separate thread
		self.ar = AppRecorder(self)
		self.art = threading.Thread(target=self.ar.start_app_observers)
		self.art.start()

		#TODO add file system tracking
		# https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html

		# run the Traces application
		AppHelper.runEventLoop()
Example #2
0
class Sniffer:
	def __init__(self, activity_tracker):
		# set reference to tracker object that spawned this sniffer
		self.activity_tracker = activity_tracker
		self.delegate = None

	def createAppDelegate(self):
		sc = self

		class AppDelegate(NSObject):
			"""
			The delegate recieves messages from outside the application ( such
			as to quit the application) and handles them.
			"""
			def __init__(self):
				self.statusbar = None
				self.state = 'pause'
				self.screenshot = True
				self.recordingAudio = False

			def applicationDidFinishLaunching_(self, notification):
				print "Traces finished launching..."

				# save recorder turned on event
				t = cfg.NOW()

				text = '{"time": '+ str(t) + ' , "type": "Start"}'
				utils_cocoa.write_to_file(text, cfg.RECORDERLOG)

				# set inital values for the preferences
				preferences.setInitialPreferenceValues()

				# create status-bar menu
				self.createStatusMenu()

				# start loops to take screenshots and parse log files
				self.activity_tracker.checkLoops()

			def applicationWillTerminate_(self, application):
				t = cfg.NOW()

				# close all open app and window listeners
				sc.ar.stop_app_observers()

				# save recorder turned off event
				text = '{"time": '+ str(t) + ' , "type": "Exit"}'
				utils_cocoa.write_to_file(text, cfg.RECORDERLOG)

				#TODO tell application to parse logs one last time before quiting
				sc.activity_tracker.storage.parseToSqlite()
				sc.activity_tracker.storage.sqlcommit()

				print "Exiting Traces..."
				sc.cancel()

			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 changeIcon(self):
				record = preferences.getValueForPreference('recording')
				if(record):
					self.statusitem.setImage_(self.icon)
				else:
					self.statusitem.setImage_(self.iconGray)

			def showPreferences_(self, notification):
				self.prefContr = PreferencesController.show(sc)

			def showExperience_(self, notification):
				ExperienceController.show(sc)

			# could do this in Interface BUilder, but creating here works too
			def createStatusMenu(self):
				print "Creating app menu"
				statusbar = NSStatusBar.systemStatusBar()

				# Create the statusbar item
				self.statusitem = statusbar.statusItemWithLength_(NSVariableStatusItemLength)
				# self.statusitem.setTitle_(u"Selfspy")

				# Load all images
				self.icon = NSImage.alloc().initByReferencingFile_('../Resources/clock.png')
				self.icon.setScalesWhenResized_(True)
				self.size_ = self.icon.setSize_((20, 20))
				self.statusitem.setImage_(self.icon)

				self.iconGray = NSImage.alloc().initByReferencingFile_('../Resources/clock_grey.png')
				self.iconGray.setScalesWhenResized_(True)
				self.iconGray.setSize_((20, 20))

				self.changeIcon()

				# Let it highlight upon clicking
				self.statusitem.setHighlightMode_(1)
				# Set a tooltip
				self.statusitem.setToolTip_('Traces')

				# Build a very simple menu
				self.menu = NSMenu.alloc().init()
				self.menu.setAutoenablesItems_(False)

				if NSUserDefaultsController.sharedUserDefaultsController().values().valueForKey_('recording'):
					menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Pause Recording', 'toggleLogging:', 'r')
				else:
					menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Start Recording', 'toggleLogging:', 'r')
				#menuitem.setEnabled_(False)
				self.menu.addItem_(menuitem)
				self.loggingMenuItem = menuitem

				menuitem = NSMenuItem.separatorItem()
				self.menu.addItem_(menuitem)

				menuitem = NSMenuItem.separatorItem()
				self.menu.addItem_(menuitem)

				menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Preferences...', 'showPreferences:', ',')
				self.menu.addItem_(menuitem)

				menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Leave a message', 'showExperience:', 'm')
				menuitem.setKeyEquivalentModifierMask_(NSShiftKeyMask + NSCommandKeyMask)
				self.menu.addItem_(menuitem)

				menuitem = NSMenuItem.separatorItem()
				self.menu.addItem_(menuitem)

				menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('Quit Traces', 'terminate:', 'q')
				self.menu.addItem_(menuitem)

				# Bind it to the status item
				self.statusitem.setMenu_(self.menu)

				self.statusitem.setEnabled_(TRUE)
				self.statusitem.retain()

		return AppDelegate

	def run(self):
		# set up the application
		self.app = NSApplication.sharedApplication()
		self.delegate = self.createAppDelegate().alloc().init()
		self.delegate.activity_tracker = self.activity_tracker
		self.app.setDelegate_(self.delegate)
		self.app.setActivationPolicy_(NSApplicationActivationPolicyAccessory)
		self.workspace = NSWorkspace.sharedWorkspace()

		# start listeners for event recorders
		self.cr = ClickRecorder(self)
		self.cr.start_click_listener()
		self.kr = KeyRecorder(self)
		self.kr.start_key_listener()
		self.mr = MoveRecorder(self)
		self.mr.start_move_listener()
		self.sr = ScrollRecorder(self)
		self.sr.start_scroll_listener()
		self.clr = ClipboardRecorder(self)
		# AppRecorder starts accessibility listeners that need to be on a
		# separate thread
		self.ar = AppRecorder(self)
		self.art = threading.Thread(target=self.ar.start_app_observers)
		self.art.start()

		#TODO add file system tracking
		# https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html

		# run the Traces application
		AppHelper.runEventLoop()

	def cancel(self):
		AppHelper.stopEventLoop()

	#https://pythonhosted.org/pyobjc/examples/Quartz/Core%20Graphics/CGRotation/index.html
	def screenshot(self, path, region = None):
	  try:
		# Set to capture entire screen, including multiple monitors
		if region is None:
		  region = CG.CGRectInfinite

		# Create CGImage, composite image of windows in region
		image = CG.CGWindowListCreateImage(
		  region,
		  CG.kCGWindowListOptionOnScreenOnly,
		  CG.kCGNullWindowID,
		  CG.kCGWindowImageDefault
		)

		# 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

		nativeHeight = CGImageGetHeight(image)*1.0
		nativeWidth = CGImageGetWidth(image)*1.0
		nativeRatio = nativeWidth/nativeHeight

		prefHeight = NSUserDefaultsController.sharedUserDefaultsController().values().valueForKey_('imageSize')
		height = int(prefHeight/scr[0].frame().size.height*nativeHeight)
		width = int(nativeRatio * height)
		heightScaleFactor = height/nativeHeight
		widthScaleFactor = width/nativeWidth

		mouseLoc = NSEvent.mouseLocation()
		x = int(mouseLoc.x)
		y = int(mouseLoc.y)
		w = 16
		h = 24
		scale_x = int((x-xmin) * widthScaleFactor)
		scale_y = int((y-h+5-ymin) * heightScaleFactor)
		scale_w = w*widthScaleFactor
		scale_h = h*heightScaleFactor

		#Allocate image data and create context for drawing image
		imageData = LaunchServices.objc.allocateBuffer(int(4 * width * height))
		bitmapContext = Quartz.CGBitmapContextCreate(
		  imageData, # image data we just allocated...
		  width,
		  height,
		  8, # 8 bits per component
		  4 * width, # bytes per pixel times number of pixels wide
		  Quartz.CGImageGetColorSpace(image), # use the same colorspace as the original image
		  Quartz.kCGImageAlphaPremultipliedFirst # use premultiplied alpha
		)

		#Draw image on context at new scale
		rect = CG.CGRectMake(0.0,0.0,width,height)
		Quartz.CGContextDrawImage(bitmapContext, rect, image)

		# Add Mouse cursor to the screenshot
		cursorPath = "../Resources/cursor.png"
		cursorPathStr = NSString.stringByExpandingTildeInPath(cursorPath)
		cursorURL = NSURL.fileURLWithPath_(cursorPathStr)

		# Create a CGImageSource object from 'url'.
		cursorImageSource = Quartz.CGImageSourceCreateWithURL(cursorURL, None)

		# Create a CGImage object from the first image in the file. Image
		# indexes are 0 based.
		cursorOverlay = Quartz.CGImageSourceCreateImageAtIndex(cursorImageSource, 0, None)

		Quartz.CGContextDrawImage(bitmapContext,
		  CG.CGRectMake(scale_x, scale_y, scale_w, scale_h),
		  cursorOverlay)

		#Recreate image from context
		imageOut = Quartz.CGBitmapContextCreateImage(bitmapContext)

		#Image properties dictionary
		dpi = 72 # FIXME: Should query this from somewhere, e.g for retina display
		properties = {
		  Quartz.kCGImagePropertyDPIWidth: dpi,
		  Quartz.kCGImagePropertyDPIHeight: dpi,
		  Quartz.kCGImageDestinationLossyCompressionQuality: 0.6,
		}

		#Convert path to url for saving image
		pathWithCursor = path[0:-4] + "_" + str(x) + "_" + str(y) + '.jpg'
		pathStr = NSString.stringByExpandingTildeInPath(pathWithCursor)
		url = NSURL.fileURLWithPath_(pathStr)

		#Set image destination (where it will be saved)
		dest = Quartz.CGImageDestinationCreateWithURL(
		  url,
		  LaunchServices.kUTTypeJPEG, # file type
		  1, # 1 image in file
		  None
		)

		# Add the image to the destination, with certain properties
		Quartz.CGImageDestinationAddImage(dest, imageOut, properties)

		# finalize the CGImageDestination object.
		Quartz.CGImageDestinationFinalize(dest)

		#For testing how long it takes to take screenshot
		print 'took ' + str(height) + 'px screenshot'

	  except KeyboardInterrupt:
		print "Keyboard interrupt"
		AppHelper.stopEventLoop()
	  except errno.ENOSPC:
		  print "No space left on storage device. Turning off Traces recording."
		  self.delegate.toggleLogging_(self)
	  except:
		print "Could not save image"

	def got_location_change(self, latitude, longitude, latitudeRange, longitudeRange):
		print "location_change", latitude, longitude