def do_select_encoding(term, session): editor_colors = {'highlight': term.black_on_green} dirty = True while True: if session.poll_event('refresh') or dirty: # attempt to coerce encoding of terminal to match session. coerce_terminal_encoding(term, session.encoding) # display artwork and prompt vertical_padding = 2 if term.height >= 24 else 0 display_banner(filepattern=art_file, encoding=art_encoding, vertical_padding=vertical_padding) display_prompt(term) dirty = False inp = LineEditor(1, colors=editor_colors).read() if inp is None or inp.lower() == 'd': break elif len(inp): # bad input -- reposition cursor for next LineEditor() echo(u'\b') if inp.lower() == u'u' and session.encoding != 'utf8': # switch to utf8, session.encoding = 'utf8' dirty = True elif inp.lower() == 'c' and session.encoding != 'cp437': # or cp437 session.encoding = 'cp437' dirty = True
def prompt_recipient(term, msg, colors, public=True): """ Prompt for recipient of message. """ xpos = max(0, (term.width // 2) - (80 // 2)) echo(term.move_x(xpos) + term.clear_eos) echo(u'Enter handle{0}.\r\n'.format( u', empty to address to all' if public else u'')) echo(term.move_x(xpos) + ':: ') inp = LineEditor(username_max_length, msg.recipient, colors={ 'highlight': colors['backlight'] }).read() if inp is None: echo(u''.join((term.move_x(xpos), colors['highlight']('Canceled.'), term.clear_eol))) term.inkey(1) return False elif not inp.strip(): # empty, recipient is None msg.recipient = None if public: return True return False inp = inp.strip() # validate/find user userlist = list_users() if inp in userlist: # exact match, msg.recipient = inp echo(u'\r\n') return True # nearest match for match in difflib.get_close_matches(inp.strip(), userlist): echo(u''.join( (term.move_x(xpos), u'{0} [yn]'.format(colors['highlight'](match)), term.clear_eol, u' ?\b\b'))) while True: inp = term.inkey() if inp.code == term.KEY_ESCAPE: # escape/cancel return False elif inp.lower() == u'y': # accept match msg.recipient = match echo(u'\r\n') return True elif inp.lower() == u'n': # next match break echo(u''.join( (term.move_x(xpos), colors['highlight']('No match.'), term.clear_eol))) term.inkey(1) return False
def prompt_yesno(question): """ yes/no user prompt. """ term = getterminal() sep = getattr(term, color_secondary)(u'**') colors = {'highlight': getattr(term, color_secondary)} echo(fixate_next(term, newlines=1)) while True: echo(u'{sep} {question} [yn] ?\b\b'.format(sep=sep, question=question)) yn = LineEditor(colors=colors, width=1).read() or u'' if yn.lower() in (u'y', u'n'): return yn.lower() == u'y' echo(term.move_x(0) + term.clear_eol) echo(fixate_next(term, newlines=0))
def prompt_recipient(term, msg, colors, public=True): """ Prompt for recipient of message. """ xpos = max(0, (term.width // 2) - (80 // 2)) echo(term.move_x(xpos) + term.clear_eos) echo(u"Enter handle{0}.\r\n".format(u", empty to address to all" if public else u"")) echo(term.move_x(xpos) + ":: ") inp = LineEditor(username_max_length, msg.recipient, colors={"highlight": colors["backlight"]}).read() if inp is None: echo(u"".join((term.move_x(xpos), colors["highlight"]("Canceled."), term.clear_eol))) term.inkey(1) return False elif not inp.strip(): # empty, recipient is None msg.recipient = None if public: return True return False inp = inp.strip() # validate/find user userlist = list_users() if inp in userlist: # exact match, msg.recipient = inp echo(u"\r\n") return True # nearest match for match in difflib.get_close_matches(inp.strip(), userlist): echo(u"".join((term.move_x(xpos), u"{0} [yn]".format(colors["highlight"](match)), term.clear_eol, u" ?\b\b"))) while True: inp = term.inkey() if inp.code == term.KEY_ESCAPE: # escape/cancel return False elif inp.lower() == u"y": # accept match msg.recipient = match echo(u"\r\n") return True elif inp.lower() == u"n": # next match break echo(u"".join((term.move_x(xpos), colors["highlight"]("No match."), term.clear_eol))) term.inkey(1) return False
def do_login(term): sep_ok = getattr(term, color_secondary)(u'::') sep_bad = getattr(term, color_primary)(u'::') colors = {'highlight': getattr(term, color_primary)} for _ in range(login_max_attempts): term.goto_y(10) echo(u'\r\n\r\n{sep} Login: '******'' if handle.strip() == u'': continue # user says goodbye if handle.lower() in bye_usernames: return # user applies for new account if new_allowed and handle.lower() in new_usernames: gosub(new_script) display_banner(term) continue # user wants to reset password if reset_allowed and handle.lower() == 'reset': gosub(reset_script) display_banner(term) continue # user wants to login anonymously if anonymous_allowed and handle.lower() in anonymous_names: user = User('anonymous') else: # authenticate password echo(u'\r\n\r\n{sep} Password: '******'' user = authenticate_user(handle, password) if not user: echo(u'\r\n\r\n{sep} Login failed.'.format(sep=sep_bad)) continue goto(top_script, handle=user.handle) echo(u'\r\n\r\n{sep} Too many authentication attempts.\r\n'.format( sep=sep_bad))
def do_login(term): sep_ok = getattr(term, color_secondary)(u'::') sep_bad = getattr(term, color_primary)(u'::') colors = {'highlight': getattr(term, color_primary)} for _ in range(login_max_attempts): echo(u'\r\n\r\n{sep} Login: '******'' if handle.strip() == u'': continue # user says goodbye if handle.lower() in bye_usernames: return # user applies for new account if new_allowed and handle.lower() in new_usernames: gosub(new_script) display_banner(term) continue # user wants to reset password if reset_allowed and handle.lower() == 'reset': gosub(reset_script) display_banner(term) continue # user wants to login anonymously if anonymous_allowed and handle.lower() in anonymous_names: user = User('anonymous') else: # authenticate password echo(u'\r\n\r\n{sep} Password: '******'' user = authenticate_user(handle, password) if not user: echo(u'\r\n\r\n{sep} Login failed.'.format(sep=sep_bad)) continue goto(top_script, handle=user.handle) echo(u'\r\n\r\n{sep} Too many authentication attempts.\r\n' .format(sep=sep_bad))
def do_intro_art(term, session): """ Display random art file, prompt for quick login. Bonus: allow chosing other artfiles with '<' and '>'. """ from x84.bbs import ini show_intro_art = False if ini.CFG.has_option('system', 'show_intro_art'): show_intro_art = ini.CFG.getboolean('system', 'show_intro_art') # set syncterm font, if any if syncterm_font and term.kind.startswith('ansi'): echo(syncterm_setfont(syncterm_font)) index = int(time.time()) % len(art_files) dirty = True echo(u'\r\n') while True: session.activity = 'top' if session.poll_event('refresh') or dirty: if show_intro_art: display_intro(term, index) else: return True display_prompt(term) dirty = False dirty = True inp = LineEditor(1, colors={'highlight': term.normal}).read() if inp is None or inp.lower() == u'y': # escape/yes: quick login return True # issue #242 : set 'N' as default, by adding a check for an empty # unicode string. elif inp.lower() in (u'n', u'\r', u'\n', u''): break if len(inp) == 1: echo(u'\b') if inp == u'!': echo(u'\r\n' * 3) gosub('charset') dirty = True elif inp == u'<': index -= 1 elif inp == u'>': index += 1 else: dirty = False
def do_intro_art(term, session): """ Display random art file, prompt for quick login. Bonus: allow chosing other artfiles with '<' and '>'. """ editor_colors = {'highlight': term.black_on_red} # set syncterm font, if any if syncterm_font and term._kind.startswith('ansi'): echo(syncterm_setfont(syncterm_font)) index = int(time.time()) % len(art_files) dirty = True echo(u'\r\n') while True: session.activity = 'top' if session.poll_event('refresh') or dirty: display_intro(term, index) display_prompt(term) dirty = False dirty = True inp = LineEditor(1, colors=editor_colors).read() if inp is None or inp.lower() == u'y': # escape/yes: quick login return True elif inp.lower() == u'n': break if len(inp) == 1: echo(u'\b') if inp == u'!': echo(u'\r\n' * 3) gosub('charset') dirty = True elif inp == u'<': index -= 1 elif inp == u'>': index += 1 else: dirty = False
def do_intro_art(term, session): """ Display random art file, prompt for quick login. Bonus: allow chosing other artfiles with '<' and '>'. """ # set syncterm font, if any if syncterm_font and term.kind.startswith('ansi'): echo(syncterm_setfont(syncterm_font)) index = int(time.time()) % len(art_files) dirty = True echo(u'\r\n') while True: session.activity = 'top' if session.poll_event('refresh') or dirty: display_intro(term, index) display_prompt(term) dirty = False dirty = True inp = LineEditor(1, colors={'highlight': term.normal}).read() if inp is None or inp.lower() == u'y': # escape/yes: quick login return True # issue #242 : set 'N' as default, by adding a check for an empty # unicode string. elif inp.lower() in (u'n', u'\r', u'\n', u''): break if len(inp) == 1: echo(u'\b') if inp == u'!': echo(u'\r\n' * 3) gosub('charset') dirty = True elif inp == u'<': index -= 1 elif inp == u'>': index += 1 else: dirty = False
def do_select_encoding(term, session): editor_colors = {'highlight': term.black_on_green} dirty = True while True: if session.poll_event('refresh') or dirty: vertical_padding = 2 if term.height >= 24 else 0 display_banner(filepattern=art_file, encoding=art_encoding, vertical_padding=vertical_padding) display_prompt(term) echo ({ # ESC %G activates UTF-8 with an unspecified implementation # level from ISO 2022 in a way that allows to go back to # ISO 2022 again. 'utf8': unichr(27) + u'%G', # ESC %@ returns to ISO 2022 in case UTF-8 had been entered. # ESC ) U Sets character set G1 to codepage 437, such as on # Linux vga console. 'cp437': unichr(27) + u'%@' + unichr(27) + u')U', }.get(session.encoding, u'')) dirty = False inp = LineEditor(1, colors=editor_colors).read() if inp is None or inp.lower() == 'd': break elif len(inp) == 1: # position cursor for next call to LineEditor() echo(u'\b') if inp.lower() == u'u' and session.encoding != 'utf8': session.encoding = 'utf8' dirty = True elif inp.lower() == 'c' and session.encoding != 'cp437': session.encoding = 'cp437' dirty = True
def do_cmd(term): session = getsession() sep_ok = getattr(term, color_secondary)(u'::') sep_bad = getattr(term, color_primary)(u'::') colors = {'highlight': getattr(term, color_primary)} echo(u'\r\n\r\n i hope you know glftpd cmds :)' ) # feel free to change these echoes to personalize your installation echo(u'\r\n\r\n if you dont, type quit') echo( u'\r\n\r\n basically all this is good for at the moment is for msg and request' ) echo(u'\r\n\r\n e.g \'msg kniffy hi\' or \'request coolthing -for:<you>\'') for _ in range(max_attempts): echo(u'\r\n\r\n{sep} tYPE cMD -> '.format(sep=sep_ok)) handle = LineEditor(max_length, colors=colors).read() or u'' if handle.strip() == u'': continue # user says goodbye if handle.lower() in bye_u: return else: # do cmd person = session.user.handle # session.user.handle = person ftps = FTP_TLS() #ftps.set_debuglevel(2) # if you broke something, uncomment this (run it directly, not from eggdrop) ftps.connect( '127.0.0.1', '1234') # enter your server and port within the quotes ftps.login( 'cmd', '<make this a non-sysop user please>' ) # enter your user and pass within the quotes (remember, not a user with privs) # ftps.login(person, auth) ftps.prot_p() ftps.sendcmd('site ' + handle) echo(u'\r\n\r\n cmd sent') ftps.quit()
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 get_username(handle=u''): """ Prompt for a login handle. If unfound, script change to 'nua' when allow_apply is enabled (default=yes). Also allow 'anonymous' when enabled (default=no). A unicode handle of non-zero length is returned when the login handle matches a userbase record. """ # pylint: disable=R0914,R0911 # Too many local variables # Too many return statements from x84.bbs import getterminal, ini, echo, LineEditor, gosub, goto from x84.bbs import find_user, getch term = getterminal() prompt_user = u'user: '******'\r\n\r\n --> Create new account? [ynq] <--' + '\b' * 5 allow_apply = ini.CFG.getboolean('nua', 'allow_apply') enable_anonymous = ini.CFG.getboolean('matrix', 'enable_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() byecmds = ini.CFG.get('matrix', 'byecmds').split() denied_msg = u'\r\n\r\nfiRSt, YOU MUSt AbANdON YOUR libERtIES.' badanon_msg = u"\r\n " + term.bright_red + u"'%s' login denied." max_user = ini.CFG.getint('nua', 'max_user') nuascript = ini.CFG.get('nua', 'script') topscript = ini.CFG.get('matrix', 'topscript') echo(prompt_user) handle = LineEditor(max_user, handle).read() if handle is None or 0 == len(handle.strip()): echo(u'\r\n') return u'' elif handle.lower() in newcmds: if allow_apply: gosub('nua', u'') return u'' denied(denied_msg) return u'' elif handle.lower() in byecmds: goto('logoff') elif handle.lower() == u'anonymous': if enable_anonymous: goto(topscript, 'anonymous') denied(badanon_msg % (handle, )) return u'' u_handle = find_user(handle) if u_handle is not None: return u_handle # matched if allow_apply is False: denied(denied_msg) return u'' echo(apply_msg) ynq = getch() if ynq in (u'q', u'Q', term.KEY_EXIT): # goodbye goto('logoff') elif ynq in (u'y', u'Y'): # new user application goto(nuascript, handle) echo(u'\r\n') return u''
def prompt_pager(content, line_no=0, colors=None, width=None, breaker=u'- '): """ Display text, using a command-prompt pager. :param content: iterable of text contents. :param line_no: line number to offset beginning of pager. :param colors: optional dictionary containing terminal styling attributes, for keys 'highlight' and 'lowlight'. When unset, yellow and green are used. """ term = getterminal() colors = colors or {'highlight': term.yellow, 'lowlight': term.green} pager_prompt = (u'{bl}{s}{br}top, {bl}{c}{br}ontinuous, or ' u'{bl}{enter}{br} for next page {br} {bl}\b\b'.format( bl=colors['lowlight'](u'['), br=colors['lowlight'](u']'), s=colors['highlight'](u's'), c=colors['highlight'](u'c'), enter=colors['highlight'](u'return'))) should_break = lambda line_no, height: line_no % (height - 3) == 0 def show_breaker(): if not breaker: return u'' attr = colors['highlight'] breaker_bar = breaker * (min(80, term.width - 1) // len(breaker)) echo(attr(term.center(breaker_bar).rstrip())) continuous = False for txt in content: lines = term.wrap(txt, width) or [txt] for txt in lines: if width: txt = term.center(term.ljust(txt, max(0, width))) echo(txt.rstrip() + term.normal + term.clear_eol + u'\r\n') line_no += 1 if not continuous and should_break(line_no, term.height): show_breaker() echo(u'\r\n') if term.width > 80: echo(term.move_x((term.width // 2) - 40)) echo(pager_prompt) while True: inp = LineEditor(1, colors=colors).read() if inp is None or inp and inp.lower() in u'sqx': # s/q/x/escape: quit echo(u'\r\n') return if len(inp) == 1: echo(u'\b') if inp.lower() == 'c': # c: enable continuous continuous = True break elif inp == u'': # return: next page break # remove pager echo(term.move_x(0) + term.clear_eol) echo(term.move_up() + term.clear_eol) show_breaker() echo(u'\r\n') if term.width > 80: echo(term.move_x((term.width // 2) - 40)) echo(u'Press {enter}.'.format(enter=colors['highlight'](u'return'))) inp = LineEditor(0, colors=colors).read()
def do_reader_prompt(session, term, index, message_indices, colors): xpos = max(0, (term.width // 2) - (80 // 2)) opts = [] if index: opts += (('p', 'rev'),) if index < len(message_indices) - 1: opts += (('n', 'ext'),) if allow_tag(session, message_indices[index]): opts += (('e', 'dit tags'),) if can_delete(session): opts += (('D', 'elete'),) opts += (('r', 'eply'),) opts += (('q', 'uit'),) opts += (('idx', ''),) while True: echo(term.move_x(xpos)) echo(u''.join(( colors['lowlight'](u'['), colors['highlight'](str(index + 1)), u'/{0}'.format(len(message_indices)), colors['lowlight'](u']'), u' ', u', '.join(( u''.join((colors['lowlight'](u'['), colors['highlight'](key), colors['lowlight'](u']'), value )) for key, value in opts)), u': ', term.clear_eol, ))) width = max(2, len(str(len(message_indices)))) inp = LineEditor(width, colors={'highlight': colors['backlight']}).read() if inp is None or inp.lower() == u'q': return None elif inp in (u'n', u''): # 'n'ext or return key echo(term.move_x(xpos) + term.clear_eol) if index == len(message_indices) - 1: # no more messages, return None return index + 1 elif inp == u'p' and index > 0: # prev echo(term.move_x(xpos) + term.clear_eol) return index - 1 elif inp == u'e' and allow_tag(session, message_indices[index]): msg = get_msg(message_indices[index]) echo(u'\r\n') if prompt_tags(session=session, term=term, msg=msg, colors=colors, public='public' in msg.tags): echo(u'\r\n') msg.save() return index elif inp == u'D' and can_delete(session): delete_message(msg=get_msg(message_indices[index])) return None elif inp == u'r': # write message reply msg = create_reply_message(session=session, idx=message_indices[index]) if ( prompt_subject( term=term, msg=msg, colors=colors ) and prompt_body( term=term, msg=msg, colors=colors ) and prompt_tags( session=session, term=term, msg=msg, colors=colors, public='public' in msg.tags )): do_send_message(session=session, term=term, msg=msg, colors=colors) break else: # not a valid input option, is it a valid integer? (even '-1'!) try: val = int(inp) except ValueError: # some garbage; try again term.inkey(0.15) continue try: # allow a message index, (even pythonic '-1' for 'last') if val > 0: # 1-based indexing val -= 1 nxt_idx = message_indices.index(message_indices[val]) if nxt_idx != index: echo(term.move_x(xpos) + term.clear_eol) return nxt_idx except (IndexError, ValueError): # invalid index; try again term.inkey(0.15) continue
def prompt_pager(content, line_no=0, colors=None, width=None, breaker=u'- ', end_prompt=True, **kwargs): """ Display text, using a stop/continuous/next-page prompt. :param iterable content: iterable of text contents. :param int line_no: line number to offset beginning of pager. :param dict colors: optional dictionary containing terminal styling attributes, for keys ``'highlight'`` and ``'lowlight'``. When unset, yellow and green are used. :param int width: When set, text is left-justified-centered by width. :param str breaker: repeated decoration for page breaks :param bool end_prompt: Whether prompt should be displayed at end. :param kwargs: additional arguments to :func:`textwrap.wrap` :param bool end_prompt: use 'press enter prompt' at end. """ # This is unfortunate, we should use 'term' as first argument term = getterminal() colors = colors or { 'highlight': term.yellow, 'lowlight': term.green } pager_prompt = (u'{bl}{s}{br}top, {bl}{c}{br}ontinuous, or ' u'{bl}{enter}{br} for next page {br} {bl}\b\b' .format(bl=colors['lowlight'](u'['), br=colors['lowlight'](u']'), s=colors['highlight'](u's'), c=colors['highlight'](u'c'), enter=colors['highlight'](u'return'))) should_break = lambda line_no, height: line_no % (height - 3) == 0 def show_breaker(): if not breaker: return u'' attr = colors['highlight'] breaker_bar = breaker * (min(80, term.width - 1) // len(breaker)) echo(attr(term.center(breaker_bar).rstrip())) continuous = False # we must parse the entire tree, so that we can avoid the needless # call to show_breaker() on the final line. result = [] for txt in content: if txt.rstrip(): result.extend(term.wrap(txt, width, **kwargs)) else: result.append(u'\r\n') xpos = 0 if term.width: xpos = max(0, int((term.width / 2) - width / 2)) for line_no, txt in enumerate(result): if xpos: echo(term.move_x(xpos)) echo(txt.rstrip() + term.normal + term.clear_eol + u'\r\n') if (line_no and line_no != len(result) - 1 and not continuous and should_break(line_no, term.height)): show_breaker() echo(u'\r\n') if xpos: echo(term.move_x(xpos)) echo(pager_prompt) while True: inp = LineEditor(1, colors=colors).read() if inp is None or inp and inp.lower() in u'sqx': # s/q/x/escape: quit echo(u'\r\n') return if len(inp) == 1: echo(u'\b') if inp.lower() == 'c': # c: enable continuous continuous = True break elif inp == u'': # return: next page break # remove pager echo(term.move_x(0) + term.clear_eol) if breaker: # and breaker, echo(term.move_up() + term.clear_eol) if end_prompt: show_breaker() echo(u'\r\n') if term.width > 80: echo(term.move_x(max(0, (term.width // 2) - 40))) echo(u'Press {enter}.'.format( enter=colors['highlight'](u'enter'))) inp = LineEditor(0, colors=colors).read()
def prompt_recipient(term, msg, colors, public=True): """ Prompt for recipient of message. """ xpos = max(0, (term.width // 2) - (80 // 2)) echo(term.move_x(xpos) + term.clear_eos) echo(u'Enter handle{0}.\r\n'.format( u', empty to address to all' if public else u'')) echo(term.move_x(xpos) + ':: ') inp = LineEditor(username_max_length, msg.recipient, colors={'highlight': colors['backlight']} ).read() if inp is None: echo(u''.join((term.move_x(xpos), colors['highlight']('Canceled.'), term.clear_eol))) term.inkey(1) return False elif not inp.strip(): # empty, recipient is None msg.recipient = None if public: return True return False inp = inp.strip() # validate/find user userlist = list_users() if inp in userlist: # exact match, msg.recipient = inp echo(u'\r\n') return True # nearest match for match in difflib.get_close_matches(inp.strip(), userlist): echo(u''.join(( term.move_x(xpos), u'{0} [yn]'.format(colors['highlight'](match)), term.clear_eol, u' ?\b\b'))) while True: inp = term.inkey() if inp.code == term.KEY_ESCAPE: # escape/cancel return False elif inp.lower() == u'y': # accept match msg.recipient = match echo(u'\r\n') return True elif inp.lower() == u'n': # next match break echo(u''.join((term.move_x(xpos), colors['highlight']('No match.'), term.clear_eol))) term.inkey(1) return False
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 prompt_pager(content, line_no=0, colors=None, width=None, breaker=u'- ', end_prompt=True, **kwargs): """ Display text, using a stop/continuous/next-page prompt. :param iterable content: iterable of text contents. :param int line_no: line number to offset beginning of pager. :param dict colors: optional dictionary containing terminal styling attributes, for keys ``'highlight'`` and ``'lowlight'``. When unset, yellow and green are used. :param int width: When set, text is left-justified-centered by width. :param str breaker: repeated decoration for page breaks :param bool end_prompt: Whether prompt should be displayed at end. :param kwargs: additional arguments to :func:`textwrap.wrap` :param bool end_prompt: use 'press enter prompt' at end. """ # This is unfortunate, we should use 'term' as first argument term = getterminal() colors = colors or { 'highlight': term.yellow, 'lowlight': term.green } pager_prompt = (u'{bl}{s}{br}top, {bl}{c}{br}ontinuous, or ' u'{bl}{enter}{br} for next page {br} {bl}\b\b' .format(bl=colors['lowlight'](u'['), br=colors['lowlight'](u']'), s=colors['highlight'](u's'), c=colors['highlight'](u'c'), enter=colors['highlight'](u'return'))) should_break = lambda line_no, height: line_no % (height - 3) == 0 def show_breaker(): if not breaker: return u'' attr = colors['highlight'] breaker_bar = breaker * (min(80, term.width - 1) // len(breaker)) echo(attr(term.center(breaker_bar).rstrip())) continuous = False # we must parse the entire tree, so that we can avoid the needless # call to show_breaker() on the final line. result = [] for txt in content: if txt.rstrip(): result.extend(term.wrap(txt, width, **kwargs)) else: result.append(u'') xpos = 0 if term.width: xpos = max(0, int((term.width / 2) - width / 2)) for txt in result: line_no += 1 if xpos: echo(term.move_x(xpos)) echo(txt.rstrip() + term.normal + term.clear_eol + u'\r\n') if (line_no and line_no != len(result) - 1 and not continuous and should_break(line_no, term.height)): show_breaker() echo(u'\r\n') if xpos: echo(term.move_x(xpos)) echo(pager_prompt) while True: inp = LineEditor(1, colors=colors).read() if inp is None or inp and inp.lower() in u'sqx': # s/q/x/escape: quit echo(u'\r\n') return if len(inp) == 1: echo(u'\b') if inp.lower() == 'c': # c: enable continuous continuous = True break elif inp == u'': # return: next page break # remove pager echo(term.move_x(0) + term.clear_eol) if breaker: # and breaker, echo(term.move_up() + term.clear_eol) if end_prompt: show_breaker() echo(u'\r\n') if term.width > 80: echo(term.move_x(max(0, (term.width // 2) - 40))) echo(u'Press {enter}.'.format( enter=colors['highlight'](u'enter'))) inp = LineEditor(0, colors=colors).read()
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 do_reader_prompt(session, term, index, message_indices, colors): xpos = max(0, (term.width // 2) - (80 // 2)) opts = [] if index: opts += (("p", "rev"),) if index < len(message_indices) - 1: opts += (("n", "ext"),) if allow_tag(session, message_indices[index]): opts += (("e", "dit tags"),) if can_delete(session): opts += (("D", "elete"),) opts += (("r", "eply"),) opts += (("q", "uit"),) opts += (("idx", ""),) while True: echo(term.move_x(xpos)) echo( u"".join( ( colors["lowlight"](u"["), colors["highlight"](str(index + 1)), u"/{0}".format(len(message_indices)), colors["lowlight"](u"]"), u" ", u", ".join( ( u"".join( (colors["lowlight"](u"["), colors["highlight"](key), colors["lowlight"](u"]"), value) ) for key, value in opts ) ), u": ", term.clear_eol, ) ) ) width = max(2, len(str(len(message_indices)))) inp = LineEditor(width, colors={"highlight": colors["backlight"]}).read() if inp is None or inp.lower() == u"q": return None elif inp in (u"n", u""): # 'n'ext or return key echo(term.move_x(xpos) + term.clear_eol) if index == len(message_indices) - 1: # no more messages, return None return index + 1 elif inp == u"p" and index > 0: # prev echo(term.move_x(xpos) + term.clear_eol) return index - 1 elif inp == u"e" and allow_tag(session, message_indices[index]): msg = get_msg(message_indices[index]) echo(u"\r\n") if prompt_tags(session=session, term=term, msg=msg, colors=colors, public="public" in msg.tags): echo(u"\r\n") msg.save() return index elif inp == u"D" and can_delete(session): delete_message(msg=get_msg(message_indices[index])) return None elif inp == u"r": # write message reply msg = create_reply_message(session=session, idx=message_indices[index]) if ( prompt_subject(term=term, msg=msg, colors=colors) and prompt_body(term=term, msg=msg, colors=colors) and prompt_tags(session=session, term=term, msg=msg, colors=colors, public="public" in msg.tags) ): do_send_message(session=session, term=term, msg=msg, colors=colors) break else: # not a valid input option, is it a valid integer? (even '-1'!) try: val = int(inp) except ValueError: # some garbage; try again term.inkey(0.15) continue try: # allow a message index, (even pythonic '-1' for 'last') if val > 0: # 1-based indexing val -= 1 nxt_idx = message_indices.index(message_indices[val]) if nxt_idx != index: echo(term.move_x(xpos) + term.clear_eol) return nxt_idx except (IndexError, ValueError): # invalid index; try again term.inkey(0.15) continue
def do_reader_prompt(session, term, index, message_indices, colors): xpos = max(0, (term.width // 2) - (80 // 2)) opts = [] if index: opts += (('p', 'rev'), ) if index < len(message_indices) - 1: opts += (('n', 'ext'), ) if allow_tag(session, message_indices[index]): opts += (('e', 'dit tags'), ) if can_delete(session): opts += (('D', 'elete'), ) opts += (('r', 'eply'), ) opts += (('q', 'uit'), ) opts += (('idx', ''), ) while True: echo(term.move_x(xpos)) echo(u''.join(( colors['lowlight'](u'['), colors['highlight'](str(index + 1)), u'/{0}'.format(len(message_indices)), colors['lowlight'](u']'), u' ', u', '.join((u''.join( (colors['lowlight'](u'['), colors['highlight'](key), colors['lowlight'](u']'), value)) for key, value in opts)), u': ', term.clear_eol, ))) width = max(2, len(str(len(message_indices)))) inp = LineEditor(width, colors={ 'highlight': colors['backlight'] }).read() if inp is None or inp.lower() == u'q': return None elif inp in (u'n', u''): # 'n'ext or return key echo(term.move_x(xpos) + term.clear_eol) if index == len(message_indices) - 1: # no more messages, return None return index + 1 elif inp == u'p' and index > 0: # prev echo(term.move_x(xpos) + term.clear_eol) return index - 1 elif inp == u'e' and allow_tag(session, message_indices[index]): msg = get_msg(message_indices[index]) echo(u'\r\n') if prompt_tags(session=session, term=term, msg=msg, colors=colors, public='public' in msg.tags): echo(u'\r\n') msg.save() return index elif inp == u'D' and can_delete(session): delete_message(msg=get_msg(message_indices[index])) return None elif inp == u'r': # write message reply msg = create_reply_message(session=session, idx=message_indices[index]) if (prompt_subject(term=term, msg=msg, colors=colors) and prompt_body(term=term, msg=msg, colors=colors) and prompt_tags(session=session, term=term, msg=msg, colors=colors, public='public' in msg.tags)): do_send_message(session=session, term=term, msg=msg, colors=colors) break else: # not a valid input option, is it a valid integer? (even '-1'!) try: val = int(inp) except ValueError: # some garbage; try again term.inkey(0.15) continue try: # allow a message index, (even pythonic '-1' for 'last') if val > 0: # 1-based indexing val -= 1 nxt_idx = message_indices.index(message_indices[val]) if nxt_idx != index: echo(term.move_x(xpos) + term.clear_eol) return nxt_idx except (IndexError, ValueError): # invalid index; try again term.inkey(0.15) continue
def get_username(handle=u''): """ Prompt for a login handle. If unfound, script change to 'nua' when allow_apply is enabled (default=yes). Also allow 'anonymous' when enabled (default=no). A unicode handle of non-zero length is returned when the login handle matches a userbase record. """ # pylint: disable=R0914,R0911 # Too many local variables # Too many return statements from x84.bbs import getterminal, ini, echo, LineEditor, gosub, goto from x84.bbs import find_user, getch term = getterminal() prompt_user = u'\r\n user: '******'\r\n\r\n --> Create new account? [ynq] <--' + '\b' * 5 allow_apply = ini.CFG.getboolean('nua', 'allow_apply') enable_anonymous = ini.CFG.getboolean('matrix', 'enable_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() byecmds = ini.CFG.get('matrix', 'byecmds').split() denied_msg = u'\r\n\r\nfiRSt, YOU MUSt AbANdON YOUR libERtIES.' badanon_msg = u"\r\n " + term.bright_red + u"'%s' login denied." max_user = ini.CFG.getint('nua', 'max_user') nuascript = ini.CFG.get('nua', 'script') topscript = ini.CFG.get('matrix', 'topscript') echo(prompt_user) handle = LineEditor(max_user, handle).read() if handle is None or 0 == len(handle.strip()): echo(u'\r\n') return u'' elif handle.lower() in newcmds: if allow_apply: gosub('nua', u'') return u'' denied(denied_msg) return u'' elif handle.lower() in byecmds: goto('logoff') elif handle.lower() == u'anonymous': if enable_anonymous: goto(topscript, 'anonymous') denied(badanon_msg % (handle,)) return u'' u_handle = find_user(handle) if u_handle is not None: return u_handle # matched if allow_apply is False: denied(denied_msg) return u'' echo(apply_msg) ynq = getch() if ynq in (u'q', u'Q', term.KEY_EXIT): # goodbye goto('logoff') elif ynq in (u'y', u'Y'): # new user application goto(nuascript, handle) echo(u'\r\n') return u''
def prompt_pager(content, line_no=0, colors=None, width=None, breaker=u'- '): """ Display text, using a command-prompt pager. :param content: iterable of text contents. :param line_no: line number to offset beginning of pager. :param colors: optional dictionary containing terminal styling attributes, for keys 'highlight' and 'lowlight'. When unset, yellow and green are used. """ term = getterminal() colors = colors or { 'highlight': term.yellow, 'lowlight': term.green } pager_prompt = (u'{bl}{s}{br}top, {bl}{c}{br}ontinuous, or ' u'{bl}{enter}{br} for next page {br} {bl}\b\b' .format(bl=colors['lowlight'](u'['), br=colors['lowlight'](u']'), s=colors['highlight'](u's'), c=colors['highlight'](u'c'), enter=colors['highlight'](u'return'))) should_break = lambda line_no, height: line_no % (height - 3) == 0 def show_breaker(): if not breaker: return u'' attr = colors['highlight'] breaker_bar = breaker * (min(80, term.width - 1) // len(breaker)) echo(attr(term.center(breaker_bar).rstrip())) continuous = False for txt in content: lines = term.wrap(txt, width) or [txt] for txt in lines: if width: txt = term.center(term.ljust(txt, max(0, width))) echo(txt.rstrip() + term.normal + term.clear_eol + u'\r\n') line_no += 1 if not continuous and should_break(line_no, term.height): show_breaker() echo(u'\r\n') if term.width > 80: echo(term.move_x((term.width // 2) - 40)) echo(pager_prompt) while True: inp = LineEditor(1, colors=colors).read() if inp is None or inp and inp.lower() in u'sqx': # s/q/x/escape: quit echo(u'\r\n') return if len(inp) == 1: echo(u'\b') if inp.lower() == 'c': # c: enable continuous continuous = True break elif inp == u'': # return: next page break # remove pager echo(term.move_x(0) + term.clear_eol) echo(term.move_up() + term.clear_eol) show_breaker() echo(u'\r\n') if term.width > 80: echo(term.move_x((term.width // 2) - 40)) echo(u'Press {enter}.'.format( enter=colors['highlight'](u'return'))) inp = LineEditor(0, colors=colors).read()
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()