def lc_retrieve(): """ Returns tuple of ([nicknames,] u'text'), where 'text' describes in Ansi color the last callers to the system, and 'nicknames' is simply a list of last callers (for lightbar selection key). """ # pylint: disable=R0914 # Too many local variables from x84.bbs import list_users, get_user, ini, timeago, getterminal import time term = getterminal() udb = dict() for handle in list_users(): user = get_user(handle) udb[(user.lastcall, handle)] = (user.calls, user.location) padd_handle = (ini.CFG.getint('nua', 'max_user') + 2) padd_origin = (ini.CFG.getint('nua', 'max_location') + 2) rstr = u'' nicks = [] for ((tm_lc, handle), (_nc, origin)) in (reversed(sorted(udb.items()))): is_sysop = 'sysop' in get_user(handle).groups rstr += (term.bold_red(u'@') if is_sysop else u'' ) + (handle.ljust(padd_handle - (2 if is_sysop else 1))) rstr += term.red(origin.ljust(padd_origin)) rstr += timeago(time.time() - tm_lc) rstr += u'\n' nicks.append(handle) return (nicks, rstr.rstrip())
def locate_user(term, point): """ Prompt for search pattern and return discovered User. """ _color1, _color2, _color3 = [ getattr(term, _color) for _color in (color_lowlight, color_highlight, color_field_edit) ] # show help width = term.width - (point.x * 2) help_txt = (u'Enter username or glob pattern. Press escape to cancel.') y_offset = 0 for y_offset, txt in enumerate(term.wrap(help_txt, width=width)): echo(term.move(point.y + y_offset, point.x)) echo(_color1(txt) + term.clear_eol) point_prompt = Point(y=point.y + y_offset + 2, x=point.x) editor = LineEditor(nua.username_max_length, colors={'highlight': _color3}) while True: # prompt for handle echo(term.move(*point_prompt)) echo(u'handle: ' + term.clear_eol) inp = editor.read() point = Point(y=point_prompt.y + 2, x=point.x) if inp is None: # canceled (escape) return elif u'*' in inp or u'?' in inp: # a glob pattern, fetch all usernames handles = fnmatch.filter(list_users(), inp) if len(handles) == 0: echo(u''.join((term.move(*point), u'No matches for {0}.'.format(_color2(inp)), term.clear_eos))) elif len(handles) == 1: return get_user(handles[0]) else: matches_text = ( u'{0} accounts matched, chose one: {1}.'.format( _color2(str(len(handles))), u', '.join(_color2(handle) for handle in handles))) echo(term.move(*point)) for y_offset, txt in enumerate( term.wrap(matches_text, width=width)): echo(term.move(point.y + y_offset, point.x)) echo(txt + term.clear_eol) if point.y + y_offset > term.height - 3: # we simply cannot display anymore break echo(term.clear_eos) else: handle = find_user(inp) if handle is not None: return get_user(handle) echo(u''.join( (term.move(*point), u'No matches for {0}.'.format(_color2(inp)), term.clear_eos)))
def toplist(parameter): session, term = getsession(), getterminal() handle = session.user.handle counter = 0 user_handles = list_users() username = {} feature = {} location = {} database = {} echo(term.red + u' crunching data..') for handle in user_handles: user_record = get_user(handle) if u'sysop' in user_record.groups: continue if parameter == 'calls': database[user_record.handle.encode('utf8')] = user_record.calls if parameter == 'msgs': database[user_record.handle.encode('utf8')] = user_record.get( 'msgs_sent', 0) for name in sorted(database, key=database.get, reverse=True): username[counter] = name user_record = get_user(name) location[counter] = user_record.location feature[counter] = str(database[name]) counter = counter + 1 if counter > 10: counter = 10 # we only want to display the top ten users echo(term.clear()) showansi('topten.ans') if parameter == 'calls': echo(term.yellow + term.move(7, 1) + u'[ % ] - tOP tEN cALLERS [ % ]') if parameter == 'msgs': echo(term.yellow + term.move(7, 1) + u'[ % ] - tOP tEN wRITERS [ % ]') echo(term.cyan + term.move(9, 3) + u'username' + term.move_x(27) + u'group/location' + term.move_x(67) + parameter + u'\n\n') for i in range(0, counter): echo(term.white + term.move_x(3) + username[i] + term.move_x(27) + location[i] + term.move_x(67) + feature[i] + u'\r\n') waitprompt()
def locate_user(term, point): """ Prompt for search pattern and return discovered User. """ _color1, _color2, _color3 = [ getattr(term, _color) for _color in ( color_lowlight, color_highlight, color_field_edit)] # show help width = term.width - (point.x * 2) help_txt = (u'Enter username or glob pattern. Press escape to cancel.') y_offset = 0 for y_offset, txt in enumerate(term.wrap(help_txt, width=width)): echo(term.move(point.y + y_offset, point.x)) echo(_color1(txt) + term.clear_eol) point_prompt = Point(y=point.y + y_offset + 2, x=point.x) editor = LineEditor(nua.username_max_length, colors={'highlight': _color3}) while True: # prompt for handle echo(term.move(*point_prompt)) echo(u'handle: ' + term.clear_eol) inp = editor.read() point = Point(y=point_prompt.y + 2, x=point.x) if inp is None: # canceled (escape) return elif u'*' in inp or u'?' in inp: # a glob pattern, fetch all usernames handles = fnmatch.filter(list_users(), inp) if len(handles) == 0: echo(u''.join((term.move(*point), u'No matches for {0}.'.format(_color2(inp)), term.clear_eos))) elif len(handles) == 1: return get_user(handles[0]) else: matches_text = ( u'{0} accounts matched, chose one: {1}.'.format( _color2(str(len(handles))), u', '.join( _color2(handle) for handle in handles))) echo(term.move(*point)) for y_offset, txt in enumerate( term.wrap(matches_text, width=width)): echo(term.move(point.y + y_offset, point.x)) echo(txt + term.clear_eol) if point.y + y_offset > term.height - 3: # we simply cannot display anymore break echo(term.clear_eos) else: handle = find_user(inp) if handle is not None: return get_user(handle) echo(u''.join((term.move(*point), u'No matches for {0}.'.format(_color2(inp)), term.clear_eos)))
def toplist(parameter): session, term = getsession(), getterminal() handle = session.user.handle counter = 0 user_handles = list_users() username = {} feature = {} location = {} database = {} echo(term.red+u' crunching data..') for handle in user_handles: user_record = get_user(handle) if u'sysop' in user_record.groups: continue if parameter == 'calls': database[user_record.handle.encode('utf8')] = user_record.calls if parameter == 'msgs': database[user_record.handle.encode('utf8')] = user_record.get('msgs_sent',0) for name in sorted(database, key=database.get, reverse=True): username[counter] = name user_record = get_user(name) location[counter] = user_record.location feature[counter] = str(database[name]) counter = counter + 1 if counter > 10: counter = 10 # we only want to display the top ten users echo(term.clear()) showansi('topten.ans') if parameter == 'calls': echo(term.yellow+term.move(7,1)+u'[ % ] - tOP tEN cALLERS [ % ]') if parameter == 'msgs': echo(term.yellow+term.move(7,1)+u'[ % ] - tOP tEN wRITERS [ % ]') echo(term.cyan+term.move(9,3)+u'username'+term.move_x(27)+u'group/location'+term.move_x(67)+parameter+u'\n\n') for i in range (0, counter): echo(term.white+term.move_x(3)+username[i]+term.move_x(27)+location[i]+term.move_x(67)+feature[i]+u'\r\n') waitprompt()
def refresh_opts(pager, handle): """ Refresh pager border with command keys available. """ from x84.bbs import getsession, getterminal, get_user, find_user, Ansi session, term = getsession(), getterminal() if not handle or not find_user(handle): has_plan = 0 else: has_plan = 0 != len(get_user(handle).get('.plan', u'').strip()) decorate = lambda key, desc: u''.join(( term.red_underline(key,), u':', term.yellow(desc.split()[0]), u' ', u' '.join(desc.split()[1:]), u' ' if len(desc.split()) > 1 else u'',)) statusline = u''.join(( term.bold_yellow(u'- '), decorate(u'Escape/q', 'Uit'), decorate(u'v', 'iEW .PLAN') if has_plan else u'', decorate(u'e', 'dit USR') if 'sysop' in session.user.groups else u'', term.bold_yellow(u'-'), )) if len(Ansi(statusline)) < (pager.visible_width - 4): return pager.border() + pager.footer(statusline) else: return pager.border() + pager.footer(term.bold_red('q') + u':uit')
def iter_userlist(): handles = sorted(list_users(), key=unicode.lower) timenow = time.time() return (user_record(handle=user.handle, location=user.location, timeago=timenow - user.lastcall) for user in (get_user(handle) for handle in handles))
def main(anonymous=False, new=False, username=''): """ Main procedure. """ from x84.bbs import ( getsession, getterminal, find_user, get_user, User, ) session, term = getsession(), getterminal() session.activity = 'sftp' if anonymous: user = User(u'anonymous') else: assert not new, ("new@ user not supported by SFTP.") # ProperCase user-specified handle handle = find_user(username) assert handle is not None, handle # fetch user record user = get_user(handle) # assign session user, just as top.py function login() session.user = user while True: inp = term.inkey() # should block indefinately log = logging.getLogger(__name__) log.warn('Got inkey: {0!r}'.format(inp))
def authenticate_user(handle, password): """ Return True if the given handle and password are correct. """ # artificial delay -- this ensures people cannot guess # for user accounts, where existing ones would delay a # long while, but unknown users are quickly denied. artificial_delay = max(1.0, random.randrange(0, unknown_sleep * 100) / 100) matching_handle = find_user(handle) if matching_handle is None: log.debug('Failed login for {handle}: no such user.' .format(handle=handle)) time.sleep(artificial_delay) return False elif not password.strip(): log.debug('Failed login for {handle}: password not provided.' .format(handle=handle)) time.sleep(artificial_delay) return False user = get_user(matching_handle) if user.auth(password): # success ! log.debug('Login succeeded for {handle}.' .format(handle=handle)) return user log.debug('Failed login for {handle}: wrong password.' .format(handle=handle)) return False
def authenticate_user(handle, password): """ Return True if the given handle and password are correct. """ # artificial delay -- this ensures people cannot guess # for user accounts, where existing ones would delay a # long while, but unknown users are quickly denied. artificial_delay = max(1.0, random.randrange(0, unknown_sleep * 100) / 100) matching_handle = find_user(handle) if matching_handle is None: log.debug( 'Failed login for {handle}: no such user.'.format(handle=handle)) time.sleep(artificial_delay) return False elif not password.strip(): log.debug('Failed login for {handle}: password not provided.'.format( handle=handle)) time.sleep(artificial_delay) return False user = get_user(matching_handle) if user.auth(password): # success ! log.debug('Login succeeded for {handle}.'.format(handle=handle)) return user log.debug( 'Failed login for {handle}: wrong password.'.format(handle=handle)) return False
def view_plan(handle): """ Display .plan file for handle. """ from x84.bbs import getterminal, echo, Ansi, get_user term = getterminal() echo(u'\r\n\r\n') echo(Ansi(get_user(handle).get('.plan', u'No Plan.')).wrap(term.width)) echo(u'\r\n') pak()
def get_next_user(tgt_user): """ Get next user in sorted order. """ handles = sorted(list_users()) try: current_idx = handles.index(tgt_user.handle) except ValueError: # what if we just deleted the target user? # inject it back into `handles' list and try again handles = sorted(handles + [tgt_user.handle]) current_idx = handles.index(tgt_user.handle) idx = min(current_idx + 1, len(handles) - 1) try: return get_user(handles[idx]) except KeyError: # and what if we deleted the last user of the list? # take the previous one. return get_user(handles[idx - 1])
def migrate_105lc(): from x84.bbs import echo, DBProxy, list_users, get_user # migrating lastcallers database for 1.0.5 upgrade lc = DBProxy('lastcalls') for handle in list_users(): user = get_user(handle) lc[(handle)] = (user.lastcall, user.calls, user.location) echo('\r\n' + user.handle + '.') echo('\r\n\r\nlast callers db rebuilt!')
def migrate_105lc(): # migrating lastcallers database for 1.0.5 upgrade from x84.bbs import echo, DBProxy, list_users, get_user lc = DBProxy('lastcalls') for handle in list_users(): user = get_user(handle) lc[(handle)] = (user.lastcall, user.calls, user.location) echo(u'\r\n' + user.handle + '.') echo('\r\n\r\nlast callers db rebuilt!')
def iter_userlist(): handles = sorted(list_users(), key=unicode.lower) timenow = time.time() user_record = collections.namedtuple('userlist', ['handle', 'location', 'timeago']) return (user_record(handle=user.handle, location=user.location, timeago=timenow - user.lastcall) for user in (get_user(handle) for handle in handles))
def iter_userlist(): handles = sorted(list_users(), key=unicode.lower) timenow = time.time() user_record = collections.namedtuple('userlist', [ 'handle', 'location', 'timeago']) return (user_record(handle=user.handle, location=user.location, timeago=timenow - user.lastcall) for user in (get_user(handle) for handle in handles))
def check_user_password(username, password): """ Boolean return when username and password match user record. """ from x84.bbs import find_user, get_user handle = find_user(username) if handle is None: return False user = get_user(handle) if user is None: return False return password and user.auth(password)
def view_plan(handle): """ Display .plan file for handle. """ from x84.bbs import getterminal, echo, Ansi, get_user, find_user term = getterminal() echo(u'\r\n\r\n') if not find_user(handle): echo(Ansi(u'No Plan.')) else: echo(Ansi(get_user(handle).get('.plan', u'No Plan.')).wrap(term.width)) echo(u'\r\n') pak()
def get_user_record(handle): """ Find and return User class instance by given ``handle``. If handle is ``anonymous``, Create and return a new User object. """ if handle == u'anonymous': log.debug('anonymous login'.format(handle)) return User(u'anonymous') log.debug('login by {0!r}'.format(handle)) return get_user(handle)
def get_user_record(handle): """ Find and return User class instance by given ``handle``. If handle is ``anonymous``, Create and return a new User object. """ if handle == u'anonymous': log.debug('anonymous login') return User(u'anonymous') log.debug('login by {0!r}'.format(handle)) return get_user(handle)
def main(handle=None): """ Main procedure. """ # pylint: disable=W0603 # Using the global statement from x84.bbs import getsession, getterminal from x84.bbs import get_user session, term = getsession(), getterminal() user = session.user if ('sysop' not in session.user.groups ) or (handle is None) else get_user(handle) global EXIT while not EXIT: session.activity = 'User profile editor' dummy_pager(user)
def get_user_record(handle): """ 根据 handle 参数(最前面的 matrix 传递过来的)获得用户信息 Find and return User class instance by given ``handle``. If handle is ``anonymous``, Create and return a new User object. """ log = logging.getLogger(__name__) if handle == u'anonymous': log.debug('anonymous login'.format(handle)) return User(u'anonymous') log.debug('login by {0!r}'.format(handle)) return get_user(handle)
def matches_email(handle, email): """ Return User record for handle if it matches email. """ matching_handle = find_user(handle) user = matching_handle and get_user(matching_handle) if not user: log.debug("password reset failed, no such user {0} for email {1}.".format(handle, email)) return False elif not user.email.strip(): log.debug("password reset failed, user {0} has no email on file.".format(handle)) return False elif email.lower() != user.email.lower(): log.debug("pasword reset failed, email mismatch: {0} != {1}.".format(email, user.email)) return False # success ! return user
def show_say(handle, tgt_channel, mesg): """ return terminal sequence for /say performed by handle. """ from x84.bbs import getsession, getterminal, get_user session, term = getsession(), getterminal() return u''.join(( time.strftime('%H:%M'), u' ', term.bold_black(u'<'), (term.bold_red(u'@') if handle != 'anonymous' and 'sysop' in get_user(handle).groups else u''), (handle if handle != session.handle else term.bold(handle)), (u':%s' % (tgt_channel, ) if 'sysop' in session.user.groups else u''), term.bold_black(u'>'), u' ', mesg, ))
def show_say(handle, tgt_channel, mesg): """ return terminal sequence for /say performed by handle. """ from x84.bbs import getsession, getterminal, get_user session, term = getsession(), getterminal() return u''.join(( time.strftime('%H:%M'), u' ', term.bold_black(u'<'), (term.bold_red(u'@') if handle != 'anonymous' and 'sysop' in get_user(handle).groups else u''), (handle if handle != session.handle else term.bold(handle)), (u':%s' % (tgt_channel,) if 'sysop' in session.user.groups else u''), term.bold_black(u'>'), u' ', mesg,))
def merge_mystic(): """ Example script to merge csv records into userbase. """ # pylint: disable=R0914 # Too many local variables from x84.bbs import ini, echo, getch, User, get_user, find_user import os # you must modify variable ``do_write`` to commit changes, # csv format; 'user:pass:origin:email\n', in iso8859-1 encoding. do_write = False inp_file = os.path.join(ini.CFG.get('system', 'datapath'), 'mystic_dat.csv') lno = 0 for lno, line in enumerate(open(inp_file, 'r')): handle = line.split(':', 1)[0].strip().decode('iso8859-1') attrs = line.rstrip().split(':')[2:] (_password, _location, _email) = attrs (_password, _location, _email) = (_password.strip().decode('iso8859-1'), _location.strip().decode('iso8859-1'), _email.strip().decode('iso8859-1')) echo(u''.join(( u'\r\n', handle, u': ', '%d ' % (len(_password)), '%s ' % (_location), '%s ' % (_email), ))) match = find_user(handle) if match is None: user = User(handle) user.location = _location user.email = _email user.password = _password else: user = get_user(match) user.groups.add('old-school') if do_write: user.save() echo('\r\n\r\n%d lines processed.' % (lno, )) getch()
def check_user_pubkey(username, public_key): """ Boolean return when public_key matches user record. """ from x84.bbs import find_user, get_user log = logging.getLogger(__name__) handle = find_user(username) if handle is None: return False user_pubkey = get_user(handle).get('pubkey', False) if not user_pubkey: log.debug('{0} attempted pubkey authentication, ' 'but no public key on record for the user.'.format(username)) return False try: stored_pubkey = parse_public_key(user_pubkey) except (ValueError, Exception): import sys (exc_type, exc_value, exc_traceback) = sys.exc_info() log.debug('{0} for stored public key of user {1!r}: ' '{2}'.format(exc_type, username, exc_value)) else: return stored_pubkey == public_key
def matches_email(handle, email): """ Return User record for handle if it matches email. """ matching_handle = find_user(handle) user = matching_handle and get_user(matching_handle) if not user: log.debug( 'password reset failed, no such user {0} for email {1}.'.format( handle, email)) return False elif not user.email.strip(): log.debug( 'password reset failed, user {0} has no email on file.'.format( handle)) return False elif email.lower() != user.email.lower(): log.debug('pasword reset failed, email mismatch: {0} != {1}.'.format( email, user.email)) return False # success ! return user
def lc_retrieve(): """ Returns tuple of ([nicknames,] u'text'), where 'text' describes in Ansi color the last callers to the system, and 'nicknames' is simply a list of last callers (for lightbar selection key). """ # pylint: disable=R0914 # Too many local variables from x84.bbs import get_user, ini, timeago, getterminal from x84.bbs import DBProxy import time term = getterminal() udb = DBProxy('lastcalls') # re-order by time called; unfortunate ..; note that sqlite # encodes unicode as utf-8; but doesn't decode it on retrieval, # of dict keys; possible upstream patching opportunity here, sortdb = {} for ((handle), (tm_lc, _nc, origin)) in (udb.items()): while tm_lc in sortdb: tm_lc += 0.1 sortdb[tm_lc] = [handle.decode('utf-8'), _nc, origin] padd_handle = (ini.CFG.getint('nua', 'max_user') + 2) padd_origin = (ini.CFG.getint('nua', 'max_location') + 2) rstr = u'' nicks = [] for tm_lc, (handle, _nc, origin) in (reversed(sorted(sortdb.items()))): try: is_sysop = 'sysop' in get_user(handle).groups except KeyError: # anonymous/deleted accts, is_sysop = False rstr += (term.bold_red(u'@') if is_sysop else u'' ) + (term.ljust(handle, (padd_handle - (2 if is_sysop else 1)))) rstr += term.red(origin.ljust(padd_origin)) rstr += timeago(time.time() - tm_lc) rstr += u'\n' nicks.append(handle) return (nicks, rstr.rstrip())
def check_user_pubkey(username, public_key): """ Boolean return when public_key matches user record. """ from x84.bbs import find_user, get_user log = logging.getLogger(__name__) handle = find_user(username) if handle is None: return False user_pubkey = get_user(handle).get('pubkey', False) if not user_pubkey: log.debug('{0} attempted pubkey authentication, ' 'but no public key on record for the user.' .format(username)) return False try: stored_pubkey = parse_public_key(user_pubkey) except (ValueError, Exception): import sys (exc_type, exc_value, exc_traceback) = sys.exc_info() log.debug('{0} for stored public key of user {1!r}: ' '{2}'.format(exc_type, username, exc_value)) else: return stored_pubkey == public_key
def merge_mystic(): """ Example script to merge csv records into userbase. """ # pylint: disable=R0914 # Too many local variables from x84.bbs import ini, echo, getch, User, get_user, find_user import os # you must modify variable ``do_write`` to commit changes, # csv format; 'user:pass:origin:email\n', in iso8859-1 encoding. do_write = False inp_file = os.path.join( ini.CFG.get('system', 'datapath'), 'mystic_dat.csv') lno = 0 for lno, line in enumerate(open(inp_file, 'r')): handle = line.split(':', 1)[0].strip().decode('iso8859-1') attrs = line.rstrip().split(':')[2:] (_password, _location, _email) = attrs (_password, _location, _email) = ( _password.strip().decode('iso8859-1'), _location.strip().decode('iso8859-1'), _email.strip().decode('iso8859-1')) echo(u''.join((u'\r\n', handle, u': ', '%d ' % (len(_password)), '%s ' % (_location), '%s ' % (_email),))) match = find_user(handle) if match is None: user = User(handle) user.location = _location user.email = _email user.password = _password else: user = get_user(match) user.groups.add('old-school') if do_write: user.save() echo(u'\r\n\r\n%d lines processed.' % (lno,)) getch()
def main(handle=None): """ Main procedure. """ dirty = -1 session, term = getsession(), getterminal() tgt_user = get_user(handle) if handle else session.user legal_input_characters = string.letters + u'<>' # re-display entire screen on loop, while True: # full-screen refresh on -1; otherwise only the fields (such as when an # edit has occurred). dirty = -1 is set on screen resize, for example. if dirty == -1: # display banner, discover (y,x) point after, point_margin = show_banner(term) # forward-calculate the prompt (y,x) point point_prompt = Point(y=point_margin.y + 15, x=point_margin.x) # get all field values and locations, fields = get_display_fields(tgt_user, point=point_margin) # display all fields and prompt echo(display_options(term, fields)) echo(display_prompt(term, session, point=point_prompt)) dirty = 0 # blocking loop until screen refresh or keystroke event = None while event is None: # This dual input/refresh trick only works here, receiving # raw (undecoded) keyboard data as 'inp' because we don't # require the use of any application keys or multi-byte # sequences, only alphabetic characters. event, data = session.read_events(('input', 'refresh')) if event == 'refresh': dirty = -1 break inp = data if inp in legal_input_characters: # display command input echo(inp.decode('ascii')) if inp == u'q': # [q]uit echo(u'\r\n') return elif inp == u'\x0c': # [^L] refresh dirty = -1 break elif inp == u'f' and session.user.is_sysop: tgt_user = locate_user(term, point_prompt) or tgt_user break elif inp == u'd': # yes, you can delete yourself !! if delete_user(term, tgt_user, point_prompt): if tgt_user == session.user: # but if you delete yourself, # you must logoff. goto('logoff') # otherwise, move to next user tgt_user = get_next_user(tgt_user) break elif inp == u'<' and session.user.is_sysop: tgt_user = get_prev_user(tgt_user) break elif inp == u'>' and session.user.is_sysop: tgt_user = get_next_user(tgt_user) break elif inp in string.letters: if do_command(term, session, inp, fields, tgt_user, point_prompt): # when returning True, perform full-screen refresh, break else: # otherwise, clean prompt field time.sleep(0.2) echo(u'\b \b') elif inp in legal_input_characters: # though legal, not authorized: clean prompt field time.sleep(0.2) echo(u'\b \b') event = None dirty = dirty or 1
def get_prev_user(tgt_user): """ Get previous user in sorted order. """ handles = sorted(list_users()) idx = max(handles.index(tgt_user.handle) - 1, 0) return get_user(handles[idx])
def dummy_pager(last_callers): """ Dummy pager for displaying last callers """ # pylint: disable=R0914 # Too many local variables from x84.bbs import getterminal, getsession, echo, getch, ini, find_user from x84.bbs import LineEditor, Ansi, list_users, get_user, gosub session, term = getsession(), getterminal() msg_prompt = ( u'\r\n%sONtiNUE, %stOP, %sON-StOP %siEW .PlAN%s ?\b\b' % ( term.bold(u'[c]'), term.bold(u'[s]'), term.bold(u'n'), term.bold(u'[v]'), u' [e]dit USR' if ( 'sysop' in session.user.groups) else u'',)) msg_partial = u'PARtiAl MAtChES' msg_prompt_handle = u'ViEW .PlAN ::- ENtER hANdlE: ' redraw() echo(u'\r\n\r\n') nonstop = False row = 10 # after-art, for txt in last_callers: echo(Ansi(txt).ljust(term.width / 2).center(term.width)) echo(u'\r\n') row += 1 if ((not nonstop and row > 0 and 0 == (row % (term.height - 3))) or (row == len(last_callers) - 1)): echo(msg_prompt) inp = getch() row = 2 if inp in (u's', u'S', u'q', u'Q', term.KEY_EXIT): return if inp in (u'v', u'V') or 'sysop' in session.user.groups and ( inp in (u'e', u'E')): echo(u'\r\n\r\n') echo(msg_prompt_handle) handle = LineEditor(ini.CFG.getint('nua', 'max_user')).read() usrlist = list_users() if handle is None or 0 == len(handle.strip()): continue handle = handle.strip() if handle.lower() in [nick.lower() for nick in list_users()]: nick = ((_nick for _nick in usrlist if _nick.lower() == handle.lower()).next()) if find_user(nick): user = get_user(nick) if 'sysop' in session.user.groups and ( inp in (u'e', u'E')): gosub('profile', user.handle) else: view_plan(user.handle) else: misses = [nick for nick in usrlist.keys() if nick.lower().startswith(handle[:1].lower())] if len(misses) > 0: echo(u'%s:\r\n\r\n%s\r\n' % (msg_partial, Ansi(', '.join(misses)).wrap(term.width))) continue if inp in ('n', u'N'): nonstop = True echo(u'\r\n\r\n') pak()
def main(handle=None): """ Main procedure. """ # pylint: disable=R0914,R0912,R0915 # Too many local variables # Too many branches # Too many statements from x84.bbs import getsession, getterminal, echo, getch from x84.bbs import goto, gosub, User, get_user, DBProxy import logging import time session, term = getsession(), getterminal() session.activity = 'top' logger = logging.getLogger() # 0. just a gimmicky example, gosub('productive') # 1. determine & assign user record, if handle in (None, u'', 'anonymous',): logger.info('anonymous login by %s.', session.sid) session.user = User(u'anonymous') else: logger.debug('%r logged in.', handle) session.user = get_user(handle) timeout = session.user.get('timeout', None) if timeout is not None: echo(u'\r\n\r\nUsing preferred timeout of %ss.\r\n' % ( timeout,)) session.send_event('set-timeout', timeout) # 2. update call records session.user.calls += 1 session.user.lastcall = time.time() if session.user.handle != 'anonymous': session.user.save() # record into " last caller " record key = (session.user.handle) lcall = (session.user.lastcall, session.user.calls, session.user.location) db = DBProxy('lastcalls') db[key] = lcall # 3. if no preferred charset run charset.py selector if (session.user.get('charset', None) is None or session.user.handle == 'anonymous'): gosub('charset') session.activity = 'top' else: # load default charset session.encoding = session.user.get('charset') fun = term.bold_green(' (EXCEllENt!)') if session.encoding != 'utf8': fun = term.bold_red(u' (bUMMER!)') echo(u'\r\n\r\nUsing preferred charset, %s%s.\r\n' % ( session.encoding, fun)) # 4. impress with art, prompt for quick login (goto 'main'), if session.user.get('expert', False): dirty = True while True: if session.poll_event('refresh'): dirty = True if dirty: session.activity = 'top' display_intro() echo(u'\r\n QUiCk lOGiN [yn] ?\b\b') dirty = False inp = getch(1) if inp in (u'y', u'Y'): goto('main') elif inp in (u'n', u'N'): break elif inp in (u'!',): gosub('charset') dirty = True else: ynbar = get_ynbar() dirty = True while not ynbar.selected: if session.poll_event('refresh'): dirty = True if dirty: # redraw yes/no session.activity = 'top' swp = ynbar.selection ynbar = get_ynbar() ynbar.selection = swp display_intro() echo(redraw_quicklogin(ynbar)) dirty = False inp = getch(1) if inp in (u'!',): gosub('charset') dirty = True elif inp is not None: echo(ynbar.process_keystroke(inp)) if ynbar.quit: goto('main') if ynbar.selection == ynbar.left: goto('main') # 5. last callers gosub('lc') session.activity = 'top' # 6. check for new public/private msgs, gosub('readmsgs', set()) session.activity = 'top' # 7. news gosub('news') session.activity = 'top' # 8. one-liners gosub('ol') session.activity = 'top' # 9. weather if session.user.get('location', None): gosub('weather') session.activity = 'top' # 10. automsg gosub('automsg') goto('main')
def main(handle): """ Main procedure. """ # pylint: disable=R0914,R0915,R0911 # Too many local variables # Too many statements # Too many return statements # by request from midget; a password reset form session, term = getsession(), getterminal() session.activity = 'resetting password for %s' % (handle, ) user = get_user(handle) logger = logging.getLogger() prompt_email = u'\r\n\r\nENtER E-MAil fOR %r: ' msg_nfound = u'\r\n\r\n%r NOt fOUNd iN USERbASE.' msg_cancelled = u'\r\n CANCEllEd.' msg_wrong = u'\r\n\r\nWRONG' msg_mailsubj = u'passkey for %s' % (ini.CFG.get('system', 'bbsname')) msg_mailbody = u'Your passkey is %r' msg_mailfrom = ini.CFG.get('system', 'mail_addr') msg_sent = u'\r\n\r\nPASSkEY hAS bEEN SENt tO %s.' prompt_passkey = u'\r\n\r\nENtER PASSkEY: ' msg_verified = u'\r\n\r\nYOU hAVE bEEN VERifiEd.' echo(term.normal) if not handle in list_users(): echo(term.bold_red(msg_nfound)) getch() return False width = ini.CFG.getint('nua', 'max_email') email = None tries = 0 while True: tries += 1 if tries > 5: logger.warn('%r email retries exceeded', handle) return False echo(prompt_email % (handle, )) try_email = LineEditor(width).read() if try_email is None or 0 == len(try_email): echo(term.normal + msg_cancelled) return False # fetch user record email email = get_user(handle).email if email is None or 0 == len(email): logger.warn('%r missing email address, cannot send', handle) echo(term.bold_red(msg_wrong)) elif email.lower() != try_email.lower(): logger.warn('%r failed email %r (try: %r)', handle, email, try_email) echo(term.bold_red(msg_wrong)) else: logger.info('%r requests password reset to %r', handle, email) break # generate a 'passkey' and e-mail out of band, and request input passkey = base64.encodestring(os.urandom(ini.CFG.getint( 'nua', 'max_pass')))[:ini.CFG.getint('nua', 'max_pass')] msg = MIMEText(msg_mailbody % (passkey, )) msg['From'] = msg_mailfrom msg['To'] = email msg['Subject'] = msg_mailsubj # pylint: disable=W0703 # Catching too general exception Exception err = None try: smtp = smtplib.SMTP(ini.CFG.get('system', 'mail_smtphost')) smtp.sendmail(msg_mailfrom, [email], msg.as_string()) smtp.quit() except Exception as err: logger.exception(err) echo('u\r\n\r\n' + term.bold_red(str(err)) + u'\r\n') getch(2) return False echo(msg_sent % (email, )) width = len(passkey) email = None tries = 0 while True: tries += 1 if tries > 5: logger.warn("passkey retries exceeded for user '%s'", handle) return False echo(term.normal + u'\r\n\r\n') echo(prompt_passkey) try_passkey = LineEditor(width).read() if try_passkey is None or 0 == len(try_passkey): echo(term.normal + msg_cancelled) logger.warn("cancelled passkey for user '%s'", handle) return False if passkey == try_passkey: echo(term.bold_green(msg_verified)) echo(u'\r\n\r\n') break logger.warn("failed passkey for user '%s': '%s', tried '%s')", handle, passkey, try_passkey) echo(term.bold_red(msg_wrong)) set_password(user) user.save() return True
def main(): """ Main procedure. """ # pylint: disable=R0914,R0911 # Too many local variables import logging from x84.bbs import getsession, getterminal, ini, echo, get_user, goto from x84.bbs import find_user, showcp437 from x84.engine import __url__ as url logger = logging.getLogger() session, term = getsession(), getterminal() session.activity = u'Logging in' handle = (session.env.get('USER', '').decode('iso8859-1', 'replace')) anon_allowed_msg = u"'%s' login enabled.\r\n" % ( term.bold_cyan('anonymous',)) # pylint: disable=E1103 # Instance of '_Chainmap' has no 'split' member # (but some types could not be inferred) newcmds = ini.CFG.get('matrix', 'newcmds').split() apply_msg = u"'%s' to create new account.\r\n" % ( term.bold_cyan(newcmds[0]),) allow_apply = ini.CFG.getboolean('nua', 'allow_apply') enable_anonymous = ini.CFG.getboolean('matrix', 'enable_anonymous') enable_pwreset = ini.CFG.getboolean('matrix', 'enable_pwreset') bbsname = ini.CFG.get('system', 'bbsname') artfile = os.path.join(os.path.dirname(__file__), 'art', 'xz-1984.ans') topscript = ini.CFG.get('matrix', 'topscript') max_tries = 10 session.flush_event('refresh') #uname() # display banner echo(u''.join(( term.normal, u'\r\n', u'Connected to %s, see %s for source\r\n' % (bbsname, url),))) for line in showcp437(artfile): echo(line) echo(term.normal) echo (u''.join(( u'\r\n\r\n', term.bold(u'tERM'), u': ', term.cyan_underline(session.env['TERM']), u'\r\n', term.bold(u'diMENSiONs'), u': ', '%s%s%s' % ( term.bold_cyan(str(term.width)), term.cyan(u'x'), term.bold_cyan(str(term.height)),), u'\r\n', term.bold(u'ENCOdiNG'), u': ', term.cyan_underline(session.encoding), u'\r\n\r\n', anon_allowed_msg if enable_anonymous else u'', apply_msg if allow_apply else u'', ))) # http://www.termsys.demon.co.uk/vtansi.htm # disable line-wrapping echo(unichr(27) + u'[7l') # http://www.xfree86.org/4.5.0/ctlseqs.html # Save xterm icon and window title on stack. echo(unichr(27) + u'[22;0t') if handle: echo('\r\nHello, %s!' % (handle,)) match = find_user(handle) if match is not None: handle = match else: handle = '' # prompt for username & password for _num in range(0, max_tries): handle = get_username(handle) if handle != u'': session.activity = u'Logging in' user = get_user(handle) if try_pass(user): goto(topscript, user.handle) echo(u'\r\n\r\n') if enable_pwreset: try_reset(user) else: logger.info('%r failed password', handle) logger.warn('maximum tries exceeded') goto('logoff')
def main(handle): """ Main procedure. """ # pylint: disable=R0914,R0915,R0911 # Too many local variables # Too many statements # Too many return statements # by request from midget; a password reset form session, term = getsession(), getterminal() session.activity = 'resetting password for %s' % (handle,) user = get_user(handle) logger = logging.getLogger() prompt_email = u'\r\n\r\nENtER E-MAil fOR %r: ' msg_nfound = u'\r\n\r\n%r NOt fOUNd iN USERbASE.' msg_cancelled = u'\r\n CANCEllEd.' msg_wrong = u'\r\n\r\nWRONG' msg_mailsubj = u'passkey for %s' % (ini.CFG.get('system', 'bbsname')) msg_mailbody = u'Your passkey is %r' msg_mailfrom = ini.CFG.get('system', 'mail_addr') msg_sent = u'\r\n\r\nPASSkEY hAS bEEN SENt tO %s.' prompt_passkey = u'\r\n\r\nENtER PASSkEY: ' msg_verified = u'\r\n\r\nYOU hAVE bEEN VERifiEd.' echo(term.normal) if not handle in list_users(): echo(term.bold_red(msg_nfound)) getch() return False width = ini.CFG.getint('nua', 'max_email') email = None tries = 0 while True: tries += 1 if tries > 5: logger.warn('%r email retries exceeded', handle) return False echo(prompt_email % (handle,)) try_email = LineEditor(width).read() if try_email is None or 0 == len(try_email): echo(term.normal + msg_cancelled) return False # fetch user record email email = get_user(handle).email if email is None or 0 == len(email): logger.warn('%r missing email address, cannot send', handle) echo(term.bold_red(msg_wrong)) elif email.lower() != try_email.lower(): logger.warn('%r failed email %r (try: %r)', handle, email, try_email) echo(term.bold_red(msg_wrong)) else: logger.info('%r requests password reset to %r', handle, email) break # generate a 'passkey' and e-mail out of band, and request input passkey = base64.encodestring( os.urandom(ini.CFG.getint('nua', 'max_pass')) )[:ini.CFG.getint('nua', 'max_pass')] msg = MIMEText(msg_mailbody % (passkey,)) msg['From'] = msg_mailfrom msg['To'] = email msg['Subject'] = msg_mailsubj # pylint: disable=W0703 # Catching too general exception Exception err = None try: smtp = smtplib.SMTP(ini.CFG.get('system', 'mail_smtphost')) smtp.sendmail(msg_mailfrom, [email], msg.as_string()) smtp.quit() except Exception as err: logger.exception(err) echo('u\r\n\r\n' + term.bold_red(str(err)) + u'\r\n') getch(2) return False echo(msg_sent % (email,)) width = len(passkey) email = None tries = 0 while True: tries += 1 if tries > 5: logger.warn("passkey retries exceeded for user '%s'", handle) return False echo(term.normal + u'\r\n\r\n') echo(prompt_passkey) try_passkey = LineEditor(width).read() if try_passkey is None or 0 == len(try_passkey): echo(term.normal + msg_cancelled) logger.warn("cancelled passkey for user '%s'", handle) return False if passkey == try_passkey: echo(term.bold_green(msg_verified)) echo(u'\r\n\r\n') break logger.warn("failed passkey for user '%s': '%s', tried '%s')", handle, passkey, try_passkey) echo(term.bold_red(msg_wrong)) set_password(user) user.save() return True
def dummy_pager(last_callers): """ Dummy pager for displaying last callers """ # pylint: disable=R0914 # Too many local variables from x84.bbs import getterminal, getsession, echo, getch, ini from x84.bbs import LineEditor, Ansi, list_users, get_user, gosub session, term = getsession(), getterminal() msg_prompt = ( u'\r\n%sONtiNUE, %stOP, %sON-StOP %siEW .PlAN%s ?\b\b' % ( term.bold(u'[c]'), term.bold(u'[s]'), term.bold(u'n'), term.bold(u'[v]'), u' [e]dit USR' if ( 'sysop' in session.user.groups) else u'',)) msg_partial = u'PARtiAl MAtChES' msg_prompt_handle = u'ViEW .PlAN ::- ENtER hANdlE: ' redraw() echo(u'\r\n\r\n') nonstop = False row = 10 # after-art, for txt in last_callers: echo(Ansi(txt).ljust(term.width / 2).center(term.width)) echo(u'\r\n') row += 1 if ((not nonstop and row > 0 and 0 == (row % (term.height - 3))) or (row == len(last_callers) - 1)): echo(msg_prompt) inp = getch() row = 2 if inp in (u's', u'S', u'q', u'Q', term.KEY_EXIT): return if inp in (u'v', u'V') or 'sysop' in session.user.groups and ( inp in (u'e', u'E')): echo(u'\r\n\r\n') echo(msg_prompt_handle) handle = LineEditor(ini.CFG.getint('nua', 'max_user')).read() usrlist = list_users() if handle is None or 0 == len(handle.strip()): continue handle = handle.strip() if handle.lower() in [nick.lower() for nick in list_users()]: user = get_user((nick for nick in usrlist if nick.lower() == handle.lower()).next()) if 'sysop' in session.user.groups and ( inp in (u'e', u'E')): gosub('profile', user.handle) else: view_plan(user.handle) else: misses = [nick for nick in usrlist.keys() if nick.lower().startswith(handle[:1].lower())] if len(misses) > 0: echo(u'%s:\r\n\r\n%s\r\n' % (msg_partial, Ansi(', '.join(misses)).wrap(term.width))) continue if inp in ('n', u'N'): nonstop = True echo(u'\r\n\r\n') pak()
def main(handle=None): """ Main procedure. """ dirty = -1 session, term = getsession(), getterminal() tgt_user = get_user(handle) if handle else session.user legal_input_characters = string.letters + u'<>' # re-display entire screen on loop, while True: # full-screen refresh on -1; otherwise only the fields (such as when an # edit has occurred). dirty = -1 is set on screen resize, for example. if dirty == -1: # display banner, discover (y,x) point after, point_margin = show_banner(term) # forward-calculate the prompt (y,x) point point_prompt = Point(y=point_margin.y + 15, x=point_margin.x) # get all field values and locations, fields = get_display_fields(tgt_user, point=point_margin) # display all fields and prompt echo(display_options(term, fields)) echo(display_prompt(term, session, point=point_prompt)) dirty = 0 # blocking loop until screen refresh or keystroke event = None while event is None: # This dual input/refresh trick only works here, receiving # raw (undecoded) keyboard data as 'inp' because we don't # require the use of any application keys or multi-byte # sequences, only alphabetic characters. event, data = session.read_events(('input', 'refresh')) if event == 'refresh': dirty = -1 break inp = data if inp in legal_input_characters: # display command input echo(inp.decode('ascii')) if inp == u'q': # [q]uit echo(u'\r\n') return elif inp == u'\x0c': # [^L] refresh dirty = -1 break elif inp == u'f' and session.user.is_sysop: tgt_user = locate_user(term, point_prompt) or tgt_user break elif inp == u'd': # yes, you can delete yourself !! if delete_user(term, tgt_user, point_prompt): if tgt_user == session.user: # but if you delete yourself, # you must logoff. goto('logoff') # otherwise, move to next user tgt_user = get_next_user(tgt_user) break elif inp == u'<' and session.user.is_sysop: tgt_user = get_prev_user(tgt_user) break elif inp == u'>' and session.user.is_sysop: tgt_user = get_next_user(tgt_user) break elif inp in string.letters: if do_command( term, session, inp, fields, tgt_user, point_prompt): # when returning True, perform full-screen refresh, break else: # otherwise, clean prompt field time.sleep(0.2) echo(u'\b \b') elif inp in legal_input_characters: # though legal, not authorized: clean prompt field time.sleep(0.2) echo(u'\b \b') event = None dirty = dirty or 1
def main(): """ Main procedure. """ # pylint: disable=R0914,R0911 # Too many local variables import logging from x84.bbs import getsession, getterminal, ini, echo, get_user, goto from x84.bbs import find_user, showcp437 from x84.engine import __url__ as url import random, time, glob logger = logging.getLogger() session, term = getsession(), getterminal() session.activity = u'Logging in' handle = (session.env.get('USER', '').decode('iso8859-1', 'replace')) anon_allowed_msg = u"'%s' login enabled.\r\n" % (term.bold_cyan( 'anonymous', )) # pylint: disable=E1103 # Instance of '_Chainmap' has no 'split' member # (but some types could not be inferred) newcmds = ini.CFG.get('matrix', 'newcmds').split() apply_msg = u"'%s' to create new account.\r\n" % (term.bold_cyan( newcmds[0]), ) allow_apply = ini.CFG.getboolean('nua', 'allow_apply') enable_anonymous = ini.CFG.getboolean('matrix', 'enable_anonymous') enable_pwreset = ini.CFG.getboolean('matrix', 'enable_pwreset') bbsname = ini.CFG.get('system', 'bbsname') headers = glob.glob( os.path.join(os.path.dirname(__file__), "art", "YOSBBS*.ANS")) bannername = "YOSBBS" + str(random.randrange( 1, len(headers))).zfill(2) + ".ANS" artfile = os.path.join(os.path.dirname(__file__), 'art', bannername) topscript = ini.CFG.get('matrix', 'topscript') max_tries = 10 session.flush_event('refresh') #uname() # display banner echo(u''.join(( term.normal, u'\r\n', u'Connected to %s, see %s for source\r\n' % (bbsname, url), ))) time.sleep(1) echo(term.clear()) for line in showcp437(artfile): echo(line) echo(term.normal) echo(term.move(term.height - 2, 0)) echo(u''.join(( term.bold(u'tERM'), u': ', term.cyan_underline(session.env['TERM']), term.bold(u' diMENSiONs'), u': ', '%s%s%s' % ( term.bold_cyan(str(term.width)), term.cyan(u'x'), term.bold_cyan(str(term.height)), ), term.bold(u' ENCOdiNG'), u': ', term.cyan_underline(session.encoding), anon_allowed_msg if enable_anonymous else u'', u' ', apply_msg if allow_apply else u'', ))) # http://www.termsys.demon.co.uk/vtansi.htm # disable line-wrapping echo(unichr(27) + u'[7l') # http://www.xfree86.org/4.5.0/ctlseqs.html # Save xterm icon and window title on stack. echo(unichr(27) + u'[22;0t') if handle: echo('\r\nHello, %s!' % (handle, )) match = find_user(handle) if match is not None: handle = match else: handle = '' # prompt for username & password for _num in range(0, max_tries): handle = get_username(handle) if handle != u'': session.activity = u'Logging in' user = get_user(handle) if try_pass(user): goto(topscript, user.handle) echo(u'\r\n\r\n') if enable_pwreset: try_reset(user) else: logger.info('%r failed password', handle) logger.warn('maximum tries exceeded') goto('logoff')
def main(handle=None): """ Main procedure. """ # pylint: disable=R0914,R0912,R0915 # Too many local variables # Too many branches # Too many statements from x84.bbs import getsession, getterminal, echo, getch from x84.bbs import goto, gosub, User, get_user, DBProxy import logging import time session, term = getsession(), getterminal() session.activity = 'top' logger = logging.getLogger() # 0. just a gimmicky example, # gosub('productive') # 1. determine & assign user record, if handle in (None, u'', 'anonymous',): logger.info('anonymous login by %s.', session.sid) session.user = User(u'anonymous') else: logger.debug('%r logged in.', handle) session.user = get_user(handle) timeout = session.user.get('timeout', None) if timeout is not None: echo(u'\r\n\r\nUsing preferred timeout of %ss.\r\n' % ( timeout,)) session.send_event('set-timeout', timeout) # 2. update call records session.user.calls += 1 session.user.lastcall = time.time() if session.user.handle != 'anonymous': session.user.save() # record into " last caller " record key = (session.user.handle) lcall = (session.user.lastcall, session.user.calls, session.user.location) db = DBProxy('lastcalls') db[key] = lcall # 3. if no preferred charset run charset.py selector if (session.user.get('charset', None) is None or session.user.handle == 'anonymous'): gosub('charset') session.activity = 'top' else: # load default charset session.encoding = session.user.get('charset') fun = term.bold_green(' (EXCEllENt!)') if session.encoding != 'utf8': fun = term.bold_red(u' (bUMMER!)') echo(u'\r\n\r\nUsing preferred charset, %s%s.\r\n' % ( session.encoding, fun)) echo(term.clear()) # 4. impress with art, prompt for quick login (goto 'main'), if session.user.get('expert', False): dirty = True while True: if session.poll_event('refresh'): dirty = True if dirty: session.activity = 'top' display_intro() echo(u'\r\n QUiCk lOGiN [yn] ?\b\b') dirty = False inp = getch(1) if inp in (u'y', u'Y'): goto('main') elif inp in (u'n', u'N'): break elif inp in (u'!',): gosub('charset') dirty = True else: ynbar = get_ynbar() dirty = True while not ynbar.selected: if session.poll_event('refresh'): dirty = True if dirty: # redraw yes/no session.activity = 'top' swp = ynbar.selection ynbar = get_ynbar() ynbar.selection = swp display_intro() echo(redraw_quicklogin(ynbar)) dirty = False inp = getch(1) if inp in (u'!',): gosub('charset') dirty = True elif inp is not None: echo(ynbar.process_keystroke(inp)) if ynbar.quit: goto('main') if ynbar.selection == ynbar.left: goto('main') # 5. last callers gosub('lc') session.activity = 'top' # 6. check for new public/private msgs, # gosub('readmsgs', set()) # session.activity = 'top' # 7. news gosub('news') session.activity = 'top' # 8. one-liners gosub('ol') session.activity = 'top' # 9. weather # if session.user.get('location', None): # gosub('weather') goto('main')