diff --git a/osx-requirements.txt b/osx-requirements.txt index 4aa6b79..12870f2 100644 --- a/osx-requirements.txt +++ b/osx-requirements.txt @@ -1,4 +1,4 @@ -SQLAlchemy==0.7.6 +SQLAlchemy==0.9.4 lockfile==0.9.1 pycrypto==2.5 pyobjc-core==2.5.1 diff --git a/requirements.txt b/requirements.txt index 08682ab..c9eff87 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -SQLAlchemy==0.7.6 +SQLAlchemy==0.9.4 lockfile==0.9.1 pycrypto==2.5 keyring==1.2.2 diff --git a/selfspy/__init__.py b/selfspy/__init__.py index ae5f9a4..2a2692d 100755 --- a/selfspy/__init__.py +++ b/selfspy/__init__.py @@ -34,6 +34,7 @@ from selfspy import config as cfg + def parse_config(): conf_parser = argparse.ArgumentParser(description=__doc__, add_help=False, formatter_class=argparse.RawDescriptionHelpFormatter) @@ -54,7 +55,7 @@ def parse_config(): parser.add_argument('-n', '--no-text', action='store_true', help='Do not store what you type. This will make your database smaller and less sensitive to security breaches. Process name, window titles, window geometry, mouse clicks, number of keys pressed and key timings will still be stored, but not the actual letters. Key timings are stored to enable activity calculation in selfstats. If this switch is used, you will never be asked for password.') parser.add_argument('-r', '--no-repeat', action='store_true', help='Do not store special characters as repeated characters.') - + parser.add_argument('--change-password', action="store_true", help='Change the password used to encrypt the keys columns and exit.') return parser.parse_args() @@ -83,7 +84,7 @@ def check_with_encrypter(password): pass lockname = os.path.join(args['data_dir'], cfg.LOCK_FILE) - cfg.LOCK = LockFile(lockname) + cfg.LOCK = LockFile(lockname) if cfg.LOCK.is_locked(): print '%s is locked! I am probably already running.' % lockname print 'If you can find no selfspy process running, it is a stale lock and you can safely remove it.' @@ -125,7 +126,7 @@ def check_with_encrypter(password): cfg.LOCK.acquire() try: astore.run() - except SystemExit: + except SystemExit: astore.close() except KeyboardInterrupt: pass diff --git a/selfspy/activity_store.py b/selfspy/activity_store.py index 2254fa0..66eb329 100644 --- a/selfspy/activity_store.py +++ b/selfspy/activity_store.py @@ -77,14 +77,14 @@ def __init__(self, db_name, encrypter=None, store_text=True, repeat_char=True): def trycommit(self): self.last_commit = time.time() - for _ in xrange(1000): + for _ in range(1000): try: self.session.commit() break except sqlalchemy.exc.OperationalError: time.sleep(1) except: - self.session.rollback() + self.session.rollback() def run(self): self.session = self.session_maker() @@ -98,23 +98,33 @@ def run(self): self.sniffer.run() def got_screen_change(self, process_name, window_name, win_x, win_y, win_width, win_height): - """ Receives a screen change and stores any changes. If the process or window has - changed it will also store any queued pressed keys. - process_name is the name of the process running the current window - window_name is the name of the window - win_x is the x position of the window - win_y is the y position of the window - win_width is the width of the window - win_height is the height of the window """ - cur_process = self.session.query(Process).filter_by(name=process_name).scalar() + """Receives a screen change and stores any changes. + If the process or window has changed it will also store any queued pressed keys. + Keyword arguments: + process_name -- the name of the process running the current window + window_name -- the name of the window + win_x -- the x position of the window + win_y -- the y position of the window + win_width -- the width of the window + win_height -- the height of the window""" + + cur_process = self.session.query( + Process + ).filter_by( + name=process_name + ).scalar() if not cur_process: cur_process = Process(process_name) self.session.add(cur_process) - cur_geometry = self.session.query(Geometry).filter_by(xpos=win_x, - ypos=win_y, - width=win_width, - height=win_height).scalar() + cur_geometry = self.session.query( + Geometry + ).filter_by( + xpos=win_x, + ypos=win_y, + width=win_width, + height=win_height + ).scalar() if not cur_geometry: cur_geometry = Geometry(win_x, win_y, win_width, win_height) self.session.add(cur_geometry) diff --git a/selfspy/models.py b/selfspy/models.py index fa9f33e..f2cb312 100644 --- a/selfspy/models.py +++ b/selfspy/models.py @@ -22,7 +22,10 @@ import datetime from sqlalchemy.ext.declarative import declarative_base, declared_attr -from sqlalchemy import Index, Column, Boolean, Integer, Unicode, DateTime, Binary, ForeignKey, create_engine +from sqlalchemy import ( + Index, Column, Boolean, Integer, Unicode, DateTime, Binary, ForeignKey, + create_engine +) from sqlalchemy.orm import sessionmaker, relationship, backref @@ -69,7 +72,7 @@ def __init__(self, title, process_id): def __repr__(self): return "" % (self.title) - + class Geometry(SpookMixin, Base): xpos = Column(Integer, nullable=False) ypos = Column(Integer, nullable=False) @@ -87,7 +90,7 @@ def __init__(self, x, y, width, height): def __repr__(self): return "" % (self.xpos, self.ypos, self.width, self.height) - + class Click(SpookMixin, Base): button = Column(Integer, nullable=False) press = Column(Boolean, nullable=False) @@ -100,7 +103,7 @@ class Click(SpookMixin, Base): window_id = Column(Integer, ForeignKey('window.id'), nullable=False) window = relationship("Window", backref=backref('clicks')) - + geometry_id = Column(Integer, ForeignKey('geometry.id'), nullable=False) geometry = relationship("Geometry", backref=backref('clicks')) @@ -118,7 +121,7 @@ def __init__(self, button, press, x, y, nrmoves, process_id, window_id, geometry def __repr__(self): return "" % (self.x, self.y, self.button, self.press, self.nrmoves) - + def pad(s, padnum): ls = len(s) if ls % padnum == 0: diff --git a/selfspy/period.py b/selfspy/period.py index 81e3777..329c836 100644 --- a/selfspy/period.py +++ b/selfspy/period.py @@ -27,7 +27,7 @@ def __init__(self, cutoff, maxtime): def append(self, time): ltimes = len(self.times) end = min(time + self.cutoff, self.maxtime) - + def check_in(i): if self.times[i][0] <= time <= self.times[i][1]: self.times[i] = (self.times[i][0], max(end, self.times[i][1])) @@ -52,7 +52,7 @@ def maybe_merge(i): else: self.times.insert(i, (time, end)) maybe_merge(i) - + def extend(self, times): for time in times: self.append(time) diff --git a/selfspy/sniff_cocoa.py b/selfspy/sniff_cocoa.py index a703566..dfb0566 100644 --- a/selfspy/sniff_cocoa.py +++ b/selfspy/sniff_cocoa.py @@ -17,20 +17,27 @@ from Foundation import NSObject from AppKit import NSApplication, NSApp, NSWorkspace -from Cocoa import (NSEvent, - NSKeyDown, NSKeyDownMask, NSKeyUp, NSKeyUpMask, - NSLeftMouseUp, NSLeftMouseDown, NSLeftMouseUpMask, NSLeftMouseDownMask, - NSRightMouseUp, NSRightMouseDown, NSRightMouseUpMask, NSRightMouseDownMask, - NSMouseMoved, NSMouseMovedMask, - NSScrollWheel, NSScrollWheelMask, - NSFlagsChanged, NSFlagsChangedMask, - NSAlternateKeyMask, NSCommandKeyMask, NSControlKeyMask, - NSShiftKeyMask, NSAlphaShiftKeyMask, - NSApplicationActivationPolicyProhibited) -from Quartz import CGWindowListCopyWindowInfo, kCGWindowListOptionOnScreenOnly, kCGNullWindowID +from Cocoa import ( + NSEvent, + NSKeyDown, NSKeyDownMask, NSKeyUpMask, + NSLeftMouseDown, NSLeftMouseUpMask, NSLeftMouseDownMask, + NSRightMouseDown, NSRightMouseUpMask, NSRightMouseDownMask, + NSMouseMoved, NSMouseMovedMask, + NSScrollWheel, NSScrollWheelMask, + NSFlagsChangedMask, + NSAlternateKeyMask, NSCommandKeyMask, NSControlKeyMask, + NSShiftKeyMask, NSAlphaShiftKeyMask, + NSApplicationActivationPolicyProhibited +) +from Quartz import ( + CGWindowListCopyWindowInfo, + kCGWindowListOptionOnScreenOnly, + kCGNullWindowID +) from PyObjCTools import AppHelper import config as cfg + class Sniffer: def __init__(self): self.key_hook = lambda x: True @@ -55,14 +62,23 @@ def applicationDidFinishLaunching_(self, notification): | NSFlagsChangedMask) NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(mask, sc.handler) - def applicationWillTerminate_(self, application): + def applicationWillResignActive(self, notification): + self.applicationWillTerminate_(notification) + return True + + def applicationShouldTerminate_(self, notification): + self.applicationWillTerminate_(notification) + return True + + def applicationWillTerminate_(self, notification): # need to release the lock here as when the # application terminates it does not run the rest the # original main, only the code that has crossed the # pyobc bridge. if cfg.LOCK.is_locked(): cfg.LOCK.release() - print "Exiting ..." + print("Exiting") + return None return AppDelegate @@ -80,7 +96,8 @@ def cancel(self): def handler(self, event): try: activeApps = self.workspace.runningApplications() - #Have to look into this if it is too slow on move and scoll, + windowNumber = event.windowNumber() + #Have to look into this if it is too slow on move and scroll, #right now the check is done for everything. for app in activeApps: if app.isActive(): @@ -88,7 +105,7 @@ def handler(self, event): windowList = CGWindowListCopyWindowInfo(options, kCGNullWindowID) for window in windowList: - if (window['kCGWindowNumber'] == event.windowNumber() + if (window['kCGWindowNumber'] == windowNumber or (not event.windowNumber() and window['kCGWindowOwnerName'] == app.localizedName())): geometry = window['kCGWindowBounds'] @@ -137,9 +154,9 @@ def handler(self, event): character = event.charactersIgnoringModifiers() # these two get a special case because I am unsure of # their unicode value - if event.keyCode() is 36: + if event.keyCode() == 36: character = "Enter" - elif event.keyCode() is 51: + elif event.keyCode() == 51: character = "Backspace" self.key_hook(event.keyCode(), modifiers, @@ -158,77 +175,78 @@ def handler(self, event): # Cocoa does not provide a good api to get the keycodes, therefore we # have to provide our own. keycodes = { - u"\u0009": "Tab", - u"\u001b": "Escape", - u"\uf700": "Up", - u"\uF701": "Down", - u"\uF702": "Left", - u"\uF703": "Right", - u"\uF704": "F1", - u"\uF705": "F2", - u"\uF706": "F3", - u"\uF707": "F4", - u"\uF708": "F5", - u"\uF709": "F6", - u"\uF70A": "F7", - u"\uF70B": "F8", - u"\uF70C": "F9", - u"\uF70D": "F10", - u"\uF70E": "F11", - u"\uF70F": "F12", - u"\uF710": "F13", - u"\uF711": "F14", - u"\uF712": "F15", - u"\uF713": "F16", - u"\uF714": "F17", - u"\uF715": "F18", - u"\uF716": "F19", - u"\uF717": "F20", - u"\uF718": "F21", - u"\uF719": "F22", - u"\uF71A": "F23", - u"\uF71B": "F24", - u"\uF71C": "F25", - u"\uF71D": "F26", - u"\uF71E": "F27", - u"\uF71F": "F28", - u"\uF720": "F29", - u"\uF721": "F30", - u"\uF722": "F31", - u"\uF723": "F32", - u"\uF724": "F33", - u"\uF725": "F34", - u"\uF726": "F35", - u"\uF727": "Insert", - u"\uF728": "Delete", - u"\uF729": "Home", - u"\uF72A": "Begin", - u"\uF72B": "End", - u"\uF72C": "PageUp", - u"\uF72D": "PageDown", - u"\uF72E": "PrintScreen", - u"\uF72F": "ScrollLock", - u"\uF730": "Pause", - u"\uF731": "SysReq", - u"\uF732": "Break", - u"\uF733": "Reset", - u"\uF734": "Stop", - u"\uF735": "Menu", - u"\uF736": "User", - u"\uF737": "System", - u"\uF738": "Print", - u"\uF739": "ClearLine", - u"\uF73A": "ClearDisplay", - u"\uF73B": "InsertLine", - u"\uF73C": "DeleteLine", - u"\uF73D": "InsertChar", - u"\uF73E": "DeleteChar", - u"\uF73F": "Prev", - u"\uF740": "Next", - u"\uF741": "Select", - u"\uF742": "Execute", - u"\uF743": "Undo", - u"\uF744": "Redo", - u"\uF745": "Find", - u"\uF746": "Help", - u"\uF747": "ModeSwitch"} + u"\u0009": "Tab", + u"\u001b": "Escape", + u"\uf700": "Up", + u"\uF701": "Down", + u"\uF702": "Left", + u"\uF703": "Right", + u"\uF704": "F1", + u"\uF705": "F2", + u"\uF706": "F3", + u"\uF707": "F4", + u"\uF708": "F5", + u"\uF709": "F6", + u"\uF70A": "F7", + u"\uF70B": "F8", + u"\uF70C": "F9", + u"\uF70D": "F10", + u"\uF70E": "F11", + u"\uF70F": "F12", + u"\uF710": "F13", + u"\uF711": "F14", + u"\uF712": "F15", + u"\uF713": "F16", + u"\uF714": "F17", + u"\uF715": "F18", + u"\uF716": "F19", + u"\uF717": "F20", + u"\uF718": "F21", + u"\uF719": "F22", + u"\uF71A": "F23", + u"\uF71B": "F24", + u"\uF71C": "F25", + u"\uF71D": "F26", + u"\uF71E": "F27", + u"\uF71F": "F28", + u"\uF720": "F29", + u"\uF721": "F30", + u"\uF722": "F31", + u"\uF723": "F32", + u"\uF724": "F33", + u"\uF725": "F34", + u"\uF726": "F35", + u"\uF727": "Insert", + u"\uF728": "Delete", + u"\uF729": "Home", + u"\uF72A": "Begin", + u"\uF72B": "End", + u"\uF72C": "PageUp", + u"\uF72D": "PageDown", + u"\uF72E": "PrintScreen", + u"\uF72F": "ScrollLock", + u"\uF730": "Pause", + u"\uF731": "SysReq", + u"\uF732": "Break", + u"\uF733": "Reset", + u"\uF734": "Stop", + u"\uF735": "Menu", + u"\uF736": "User", + u"\uF737": "System", + u"\uF738": "Print", + u"\uF739": "ClearLine", + u"\uF73A": "ClearDisplay", + u"\uF73B": "InsertLine", + u"\uF73C": "DeleteLine", + u"\uF73D": "InsertChar", + u"\uF73E": "DeleteChar", + u"\uF73F": "Prev", + u"\uF740": "Next", + u"\uF741": "Select", + u"\uF742": "Execute", + u"\uF743": "Undo", + u"\uF744": "Redo", + u"\uF745": "Find", + u"\uF746": "Help", + u"\uF747": "ModeSwitch" +}