# CCS Computer Science # Finger Daemon # # Douglas Thrift # # $Id$ import contextlib from datetime import datetime import os.path import pytz import re import servicemanager import socket import SocketServer import win32net, win32profile, win32service, win32serviceutil, win32ts try: win32net.NetQueryDisplayInformation except AttributeError: import _win32net win32net.NetQueryDisplayInformation = _win32net.NetQueryDisplayInformation tz = pytz.timezone('US/Pacific') class Login(object): def __init__(self, name, id = None): self._info = win32net.NetUserGetInfo(None, name, 11) self._id = id if id is not None: self._session = win32ts.WTSQuerySessionInformation(None, id, win32ts.WTSWinStationName) self._client = win32ts.WTSQuerySessionInformation(None, id, win32ts.WTSClientName) self._protocol = win32ts.WTSQuerySessionInformation(None, id, win32ts.WTSClientProtocolType) self._status = win32ts.WTSQuerySessionInformation(None, id, win32ts.WTSConnectState) def client(self): return self._client if self._client else 'the Console' def directory(self): name = self._info['name'] directory = r'\\Zweihander' + '\\' + name if not os.path.exists(directory): directory = win32profile.GetProfilesDirectory() + '\\' + name home = self._info['home_dir'] return directory if not home else home def id(self): return self._id def last_logon(self): return datetime.fromtimestamp(self._info['last_logon'], tz) def name(self): return self._info['full_name'] def session(self): return self._session def status(self): if self._status == 0: return 'Active' elif self._status == 1: return 'Connected' elif self._status == 2: return 'ConnectQuery' elif self._status == 3: return 'Shadow' elif self._status == 4: return 'Disconnected' elif self._status == 5: return 'Idle' elif self._status == 6: return 'Listen' elif self._status == 7: return 'Reset' elif self._status == 8: return 'Down' else: return 'Init' class Finger(object): def __init__(self, file, full = False, name = None): self._file = file self._forward = False self._nonexistent = [] if not name: self._logins = self._sessions() if len(self._logins) == 0: self._file.write('No one logged on.\r\n') elif full: self._full() else: self._file.write('Login Name Id Session Status\r\n') for name, logins in sorted(self._logins.iteritems()): for login in logins: self._file.write('%-16s %-20s %-6s %-13s %s\r\n' % (name, login.name()[:20], login.id(), login.session(), login.status())) else: users = win32net.NetQueryDisplayInformation(None, 1) logins = self._sessions() self._logins = {} for name in name.lower().split(): nonexistant = True for user in users: if user['full_name'] is None: user['full_name'] = '' if name == user['name'].lower() or name in user['full_name'].lower().split(): user_name = user['name'] self._logins[user_name] = logins[user_name] if user_name in logins else [Login(user_name)] nonexistant = False if nonexistant: self._nonexistent.append(name) self._full() def _full(self): for nonexistent in self._nonexistent: self._file.write('finger: %s: no such user\r\n' % nonexistent) first = True for name, logins in sorted(self._logins.iteritems()): if first: first = False else: self._file.write('\r\n') login = logins[0] self._file.write('Login: %-32s Name: %s\r\nDirectory: %s\r\n' % (name, login.name(), login.directory())) if login.id() is not None: for login in logins: self._file.write('%s on %s, from %s\r\n' % (login.status(), login.id(), login.client())) last_logon = login.last_logon() if last_logon != datetime.fromtimestamp(0, tz): self._file.write('Last login %s\r\n' % last_logon.strftime('%a %b %d %H:%M %Y %Z')) else: self._file.write('Never logged in.\r\n') self._file.write('No Mail.\r\n') for file_name in ('.project', '.plan'): file = os.path.join(login.directory(), file_name) if os.path.exists(file): if file_name == '.project': self._file.write('Project:\r\n') elif file_name == '.plan': self._file.write('Plan:\r\n') with open(file) as file: self._file.write(file.read()) elif file_name == '.plan': self._file.write('No Plan.\r\n') @staticmethod def _sessions(): logins = {} for session in win32ts.WTSEnumerateSessions(): id = session['SessionId'] name = win32ts.WTSQuerySessionInformation(None, id, win32ts.WTSUserName) if name: logins.setdefault(name, []).append(Login(name, id)) return logins class FingerHandler(SocketServer.StreamRequestHandler): _query1 = re.compile(r'^(?:(/W)|(?:(/W) )?(.*))$') _query2 = re.compile(r'^((?:/W )?[^@]*(?:@[^@]+)*)@([^@]+)$') def handle(self): if servicemanager.Debugging(): print '%s:%u' % self.client_address try: query = self.rfile.readline().strip() match = self._query2.match(query) if match is not None: host = match.group(2) try: for info in socket.getaddrinfo(host, 79, socket.AF_UNSPEC, socket.SOCK_STREAM): try: finger = socket.socket(*info[:3]) except socket.error, exception: finger = None continue try: finger.connect(info[4]) except socket.error, exception: finger.close() finger = None continue break if finger is None: self.wfile.write('finger: %s\r\n' % exception) self.wfile.write('[%s]\r\n' % socket.getfqdn(host)) if finger is None: return with contextlib.closing(finger.makefile('r+b')) as finger: finger.write('%s\r\n' % match.group(1)) finger.flush() self.wfile.write(finger.read()) except socket.gaierror, exception: self.wfile.write('finger: %s\r\n' % exception) else: match = self._query1.match(query) Finger(self.wfile, bool(match.group(1) or match.group(2)), match.group(3)) except Exception, exception: servicemanager.LogErrorMsg('%s: %s' % (str(exception.__class__), str(exception))) class FingerServerService(win32serviceutil.ServiceFramework, SocketServer.ThreadingTCPServer): _svc_name_ = 'CCSCSLabFinger' _svc_display_name_ = 'CCS CS Lab Finger' _svc_description_ = 'Provides user information.' _svc_deps_ = ('lanmanworkstation', 'TermService') def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) SocketServer.ThreadingTCPServer.__init__(self, ('', 79), FingerHandler) def handle_error(self, request, client_address): if not servicemanager.Debugging(): import traceback servicemanager.LogErrorMsg(traceback.format_exc()) else: SocketServer.ThreadingTCPServer.handle_error(self, request, client_address) def SvcDoRun(self): self.serve_forever() def SvcStop(self): self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) self.shutdown() if __name__ == '__main__': import sys try: index = sys.argv.index('--prompt') except ValueError: pass else: import getpass import win32console win32console.SetConsoleTitle(FingerServerService._svc_display_name_) while True: try: username = raw_input('Account: ') if not username: print 'Empty account name.' continue password = getpass.getpass('Password: ') if not password: print 'Empty password.' continue if password != getpass.getpass('Confirm Password: '): print 'Password and confirm password mismatched.' continue sys.argv[index:index + 1] = ['--username', '.\\' + username, '--password', password] except KeyboardInterrupt: print else: break win32serviceutil.HandleCommandLine(FingerServerService)