Skip to content

Commit

Permalink
Seems like everything works, now with all files
Browse files Browse the repository at this point in the history
  • Loading branch information
David Fendrich committed Apr 12, 2012
1 parent 461e28c commit 84058cc
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 128 deletions.
34 changes: 21 additions & 13 deletions activity_store.py
Expand Up @@ -3,7 +3,7 @@
NOW = datetime.now

import Xlib.error
import cPickle #remove after mem profile
import sqlalchemy

import sniff_x
import models
Expand All @@ -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
Expand All @@ -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()
Expand All @@ -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
Expand All @@ -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""
Expand All @@ -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()
Expand All @@ -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
Expand All @@ -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):
Expand All @@ -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

Expand Down
46 changes: 35 additions & 11 deletions period.py
@@ -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
50 changes: 16 additions & 34 deletions selfspy.py
Expand Up @@ -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 ;)
Expand Down Expand Up @@ -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'])
Expand All @@ -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 = {
Expand All @@ -120,6 +99,9 @@ def parse_config():
}


if args['no_text']:
args['password'] = ""

if args['password'] is None:
args['password'] = get_password()

Expand All @@ -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()
Expand Down

0 comments on commit 84058cc

Please sign in to comment.