Skip to content

Commit

Permalink
Now unthreaded and with better filenames
Browse files Browse the repository at this point in the history
  • Loading branch information
David Fendrich committed Mar 22, 2012
1 parent 91bd3ce commit b73ea6f
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 25 deletions.
24 changes: 12 additions & 12 deletions activity_store.py
Expand Up @@ -4,25 +4,25 @@

import Xlib.error

import hook_manager
import sniff_x
import models
from models import Process, Window, Geometry, Click, Keys

SKIP_SET = {'Shift_L', 'Shift_R'}

"""
Todo:
change name to selfspy
-
make unthreaded
change git name to selfspy
--
optional crypto on Keys.text and Keys.timings
timings in json
compress text and timings (check size difference on existing db)
ask for pw in tk, if not command line
--
simple utility for reading and stats
take pw from default config file, if exists
--
ask for pw in tk, if not command line
-
README
-
test map switch
Expand Down Expand Up @@ -63,16 +63,16 @@ def __init__(self, db_name):
self.cur_win_id = None

def run(self):
self.hm = hook_manager.HookManager()
self.sniffer = sniff_x.SniffX()
self.log_cur_window()
self.hm.key_hook = self.got_key
self.hm.mouse_button_hook = self.got_mouse_click
self.hm.mouse_move_hook = self.got_mouse_move
self.sniffer.key_hook = self.got_key
self.sniffer.mouse_button_hook = self.got_mouse_click
self.sniffer.mouse_move_hook = self.got_mouse_move

self.hm.start()
self.sniffer.run()

def close(self):
self.hm.cancel()
self.sniffer.cancel()

def store_window(self):
cur_window = self.session.query(Window).filter_by(title=self.cur_name.decode('latin1'), process_id=self.cur_process_id).scalar()
Expand Down Expand Up @@ -114,7 +114,7 @@ def get_cur_window(self):
i = 0
while True:
try:
cur_window = self.hm.the_display.get_input_focus().focus
cur_window = self.sniffer.the_display.get_input_focus().focus
cur_class = None
cur_name = None
while cur_class is None and cur_class is None:
Expand Down
6 changes: 1 addition & 5 deletions hook_manager.py
Expand Up @@ -18,11 +18,8 @@ def state_to_idx(state): #this could be a dict, but after improvements a dict wi
if state == 129: return 5
return 0

class HookManager(threading.Thread):
class SniffX:
def __init__(self):
threading.Thread.__init__(self)
self.finished = threading.Event()

self.keysymdict = {}
for name in dir(XK):
if name.startswith("XK_"):
Expand Down Expand Up @@ -69,7 +66,6 @@ def run(self):
self.record_display.record_free_context(self.ctx)

def cancel(self):
self.finished.set()
self.the_display.record_disable_context(self.ctx)
self.the_display.flush()

Expand Down
17 changes: 9 additions & 8 deletions selfspy.py
Expand Up @@ -44,14 +44,17 @@ def parse_config():

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

print args

lock = lockfile.FileLock(args['lock_file'])
Expand All @@ -74,12 +77,10 @@ def parse_config():
signal.SIGTERM: 'terminate',
signal.SIGHUP: 'terminate'
}
try:
with context:
astore = ActivityStore(os.path.join(args['data_dir'], DBNAME))

with context:
astore = ActivityStore(os.path.join(args['data_dir'], DBNAME))
try:
astore.run()
while True:
time.sleep(1000000000)
except SystemExit:
astore.close()
except SystemExit:
astore.close()
116 changes: 116 additions & 0 deletions sniff_x.py
@@ -0,0 +1,116 @@
# This file is loosely based on examples/record_demo.py in python-xlib

import sys
import os
import re
import time
import threading

from Xlib import X, XK, display
from Xlib.ext import record
from Xlib.protocol import rq

def state_to_idx(state): #this could be a dict, but I might want to extend it.
if state == 1: return 1
if state == 128: return 4
if state == 129: return 5
return 0

class SniffX:
def __init__(self):
self.keysymdict = {}
for name in dir(XK):
if name.startswith("XK_"):
self.keysymdict[getattr(XK, name)] = name[3:]

self.key_hook = lambda x: True
self.mouse_button_hook = lambda x: True
self.mouse_move_hook = lambda x: True

self.contextEventMask = [X.KeyPress, X.MotionNotify] #X.MappingNotify?

self.the_display = display.Display()
self.record_display = display.Display()
self.keymap = self.the_display._keymap_codes

def run(self):
# Check if the extension is present
if not self.record_display.has_extension("RECORD"):
print "RECORD extension not found"
sys.exit(1)
else:
print "RECORD extension present"

# Create a recording context; we only want key and mouse events
self.ctx = self.record_display.record_create_context(
0,
[record.AllClients],
[{
'core_requests': (0, 0),
'core_replies': (0, 0),
'ext_requests': (0, 0, 0, 0),
'ext_replies': (0, 0, 0, 0),
'delivered_events': (0, 0),
'device_events': tuple(self.contextEventMask),
'errors': (0, 0),
'client_started': False,
'client_died': False,
}])

# Enable the context; this only returns after a call to record_disable_context,
# while calling the callback function in the meantime
self.record_display.record_enable_context(self.ctx, self.processevents)
# Finally free the context
self.record_display.record_free_context(self.ctx)

def cancel(self):
self.the_display.record_disable_context(self.ctx)
self.the_display.flush()

def processevents(self, reply):
if reply.category != record.FromServer:
return
if reply.client_swapped:
print "* received swapped protocol data, cowardly ignored"
return
if not len(reply.data) or ord(reply.data[0]) < 2:
# not an event
return
data = reply.data
while len(data):
event, data = rq.EventField(None).parse_binary_value(data, self.record_display.display, None, None)
if event.type in [X.KeyPress, X.KeyRelease]:
self.key_hook(*self.key_event(event))
elif event.type in [X.ButtonPress, X.ButtonRelease]:
self.mouse_button_hook(*self.button_event(event))
elif event.type == X.MotionNotify:
self.mouse_move_hook(event.root_x, event.root_y)
elif event.type == X.MappingNotify:
self.the_display.refresh_keyboard_mapping()
newkeymap = self.the_display._keymap_codes
print 'Change keymap!', newkeymap == self.keymap
self.keymap = newkeymap


def get_key_name(self, keycode, state):
state_idx = state_to_idx(state)
cn = self.keymap[keycode][state_idx]
if cn < 256:
return chr(cn).decode('latin1')#.encode('utf8')
else:
return self.lookup_keysym(cn)

def key_event(self, event):
return event.detail, event.state, self.get_key_name(event.detail, event.state), event.type == X.KeyPress

def button_event(self, event):
return event.detail, event.type == X.ButtonPress

def lookup_keysym(self, keysym):
if keysym in self.keysymdict:
return self.keysymdict[keysym]
return "[%d]" % keysym




0 comments on commit b73ea6f

Please sign in to comment.