Permalink
Browse files

Seems like everything works, now with all files

  • Loading branch information...
David Fendrich
David Fendrich committed Apr 12, 2012
1 parent 461e28c commit 84058cc32dce7e86c331989f8528e52ed322250a
Showing with 187 additions and 128 deletions.
  1. +21 −13 activity_store.py
  2. +35 −11 period.py
  3. +16 −34 selfspy.py
  4. +115 −70 selfstats.py
View
@@ -3,7 +3,7 @@
NOW = datetime.now
import Xlib.error
-import cPickle #remove after mem profile
+import sqlalchemy
import sniff_x
import models
@@ -14,12 +14,14 @@
#Mouse buttons: left button: 1, middle: 2, right: 3, scroll up: 4, down:5
class ActivityStore:
- def __init__(self, db_name, encrypter=None):
+ def __init__(self, db_name, encrypter=None, store_text=True):
self.session_maker = models.initialize(db_name)
self.session = None
models.ENCRYPTER = encrypter
+ self.store_text = store_text
+
self.nrmoves = 0
self.latestx = 0
self.latesty = 0
@@ -39,6 +41,14 @@ def __init__(self, db_name, encrypter=None):
self.cur_win_id = None
self.cur_win_proc = None
+ def trycommit(self):
+ for _ in xrange(1000):
+ try:
+ self.session.commit()
+ break
+ except sqlalchemy.exc.OperationalError:
+ time.sleep(1)
+
def run(self):
self.sniffer = sniff_x.SniffX()
self.log_cur_window()
@@ -57,7 +67,7 @@ def store_window(self):
if cur_window is None:
cur_window = Window(self.cur_name.decode('latin1'), self.cur_process_id)
self.session.add(cur_window)
- self.session.commit()
+ self.trycommit()
self.cur_win_id = cur_window.id
self.cur_process_id = cur_window.process_id
@@ -73,17 +83,20 @@ def maybe_end_specials(self):
def store_click(self, button, press):
if press:
- print 'mouse', button, self.nrmoves
self.session.add(Click(button, press, self.latestx, self.latesty, self.nrmoves, self.cur_win_proc, self.cur_win_id, self.cur_geo_id))
- self.session.commit()
+ self.trycommit()
self.nrmoves = 0
def store_keys(self):
if self.timings:
self.maybe_end_specials()
+ if not self.store_text:
+ self.keys = []
+ self.curtext = u""
+
self.session.add(Keys(self.curtext.encode('utf8'), self.keys, self.timings, self.started, self.cur_win_proc, self.cur_win_id, self.cur_geo_id))
- self.session.commit()
+ self.trycommit()
self.started = NOW()
self.curtext = u""
@@ -100,7 +113,6 @@ def get_cur_window(self):
cur_name = None
while cur_class is None and cur_class is None:
if type(cur_window) is int:
- print 'int?'
return None, None, None
cur_name = cur_window.get_wm_name()
@@ -109,10 +121,8 @@ def get_cur_window(self):
cur_window = cur_window.query_tree().parent
except Xlib.error.XError:
- print 'Badwin'
i += 1
if i >= 10:
- print 'Really bad win..'
return None, None, None
continue
break
@@ -126,17 +136,15 @@ def check_geometry(self):
geo = self.cur_window.get_geometry()
break
except Xlib.error.XError:
- print 'Badwin in geo'
i += 1
if i >= 10:
- print 'Really bad win in geo'
return
cur_geo = self.session.query(Geometry).filter_by(xpos=geo.x, ypos=geo.y, width=geo.width, height=geo.height).scalar()
if cur_geo is None:
cur_geo = Geometry(geo)
self.session.add(cur_geo)
- self.session.commit()
+ self.trycommit()
self.cur_geo_id = cur_geo.id
def log_cur_window(self):
@@ -152,7 +160,7 @@ def log_cur_window(self):
if cur_process is None:
cur_process = Process(proc_name)
self.session.add(cur_process)
- self.session.commit()
+ self.trycommit()
self.cur_process_id = cur_process.id
View
@@ -1,18 +1,42 @@
-#merge to intervals immediately
+import bisect
class Period:
- def __init__(self):
+ def __init__(self, cutoff):
self.times = []
+ self.cutoff = cutoff
+
+ def append(self, time):
+ ltimes = len(self.times)
+ def check_in(i):
+ if self.times[i][0] <= time <= self.times[i][1]:
+ self.times[i] = (self.times[i][0], max(time + self.cutoff, self.times[i][1]))
+ return True
+ return False
+
+ def maybe_merge(i):
+ if ltimes > i + 1:
+ if self.times[i][1] >= self.times[i+1][0]:
+ self.times[i] = (self.times[i][0], self.times[i+1][1])
+ self.times.pop(i+1)
+
+ if ltimes == 0:
+ self.times.append((time, time + self.cutoff))
+ return
+
+ i = bisect.bisect(self.times, (time,))
+ if i >= 1 and check_in(i - 1):
+ maybe_merge(i - 1)
+ elif i < ltimes and check_in(i):
+ maybe_merge(i)
+ else:
+ self.times.insert(i, (time, time + self.cutoff))
+ maybe_merge(i)
+
def extend(self, times):
- self.times.extend(times)
+ for time in times:
+ self.append(time)
- def calc_total(self, cutoff):
- self.times.sort()
- tot = 0
+ def calc_total(self):
+ return sum(t2 - t1 for t1, t2 in self.times)
- last = -cutoff
- for t in self.times:
- tot += max(min(t - last, cutoff), 0)
- last = t
- return tot
View
@@ -23,23 +23,17 @@
"""
Todo:
- implement selfstats functionality
---
- allow not-text argument to avoid storing text at all. This makes the program never ask for passwords
- remove guid and uid flags
-
--
- README
-
- periodic emails from selfspy (or perhaps just have note in the README on how this can be accomplished with cron, mail and selfstats?)
--
- no printing
- remove stdout and stderr from DaemonContext
- remove cPickle
- requirements file
+ put weekly mail in cron
+
+ try rebooting with .xinitrc
+
+ try all queries from README
+ copy Pages to README
+
+ tag release in github
+
--
test keymap switch
- general testing
---Later
code documentation, unittests, pychecker ;)
@@ -70,27 +64,15 @@ def parse_config():
parser.set_defaults(**defaults)
parser.add_argument('-p', '--password', help='Encryption password. If you want to keep your database unencrypted, specify -p "" here. If you don\'t specify a password in the command line arguments or in a config file, a dialog will pop up, asking for the password. The most secure is to not use either command line or config file and instead type it in on startup.')
parser.add_argument('-d', '--data-dir', help='Data directory for selfspy, where the database is stored. Remember that Selfspy must have read/write access. Default is %s' % DATA_DIR, default=DATA_DIR)
- #These are probably pointless, as the daemon should be run by the local user anyway
- parser.add_argument('-u', '--uid', help='User ID to switch process to on daemon start. You can specify either name or number. Default is to keep process uid, which is probably what you want.', default=os.getuid())
- parser.add_argument('-g', '--gid', help='Group ID to switch process to on daemon start. You can specify either name or number. Default is to keep process gid, which is probably what you want.', default=os.getgid())
+
+ 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.py. If this switch is used, you will never be asked for password.')
return parser.parse_args()
if __name__ == '__main__':
args = vars(parse_config())
- try:
- args['gid'] = int(args['gid'])
- except ValueError:
- args['gid'] = grp.getgrnam(args['gid'])
-
- try:
- args['uid'] = int(args['uid'])
- except ValueError:
- args['uid'] = pwd.getpwnam(args['uid']).pw_gid
-
args['data_dir'] = os.path.expanduser(args['data_dir'])
- print args #TODO: remove
try:
os.makedirs(args['data_dir'])
@@ -108,10 +90,7 @@ def parse_config():
context = daemon.DaemonContext(
working_directory=args['data_dir'],
pidfile=lock,
- stdout=sys.stdout,
- stderr=sys.stderr,
- uid=args['uid'],
- gid=args['gid']
+ stderr=sys.stderr
)
context.signal_map = {
@@ -120,6 +99,9 @@ def parse_config():
}
+ if args['no_text']:
+ args['password'] = ""
+
if args['password'] is None:
args['password'] = get_password()
@@ -133,7 +115,7 @@ def parse_config():
sys.exit(1)
with context:
- astore = ActivityStore(os.path.join(args['data_dir'], DBNAME), encrypter)
+ astore = ActivityStore(os.path.join(args['data_dir'], DBNAME), encrypter, store_text=(not args['no_text']))
try:
astore.run()
Oops, something went wrong.

0 comments on commit 84058cc

Please sign in to comment.