def __init__(self, client, iqueue, oqueue, lock): from x84.bbs.ini import CFG self.client = client self.iqueue = iqueue self.oqueue = oqueue self.lock = lock self.timeout = CFG.getint('system', 'timeout')
def start_polling(): """ launch method for polling process """ def polling_thread(poll_interval): import time last_poll = 0 while True: now = time.time() if now - last_poll >= poll_interval: poll() last_poll = now time.sleep(1) from threading import Thread from x84.bbs.ini import CFG import logging log = logging.getLogger('x84.engine') poll_interval = CFG.getint('msg', 'poll_interval') t = Thread(target=polling_thread, args=(poll_interval,)) t.daemon = True t.start() log.info('msgpoll will poll at {0}s intervals.' .format(poll_interval))
def dummy_pager(user): """ A dummy selector for profile attributes """ from x84.bbs import getsession, getterminal, echo, Ansi, getch session, term = getsession(), getterminal() plan = user.get('.plan', False) from x84.bbs.ini import CFG def_timeout = CFG.getint('system', 'timeout') menu = ['(c)%-20s - %s' % (u'hARACtER ENCOdiNG', term.bold(session.encoding),), '(t)%-20s - %s' % (u'ERMiNAl tYPE', term.bold(session.env.get('TERM', 'unknown')),), '(h)%-20s - %s' % (u'ERMiNAl hEiGht', term.bold(str(term.height)),), '(w)%-20s - %s' % (u'ERMiNAl WidtH', term.bold(str(term.width)),), '(l)%-20s - %s' % (u'OCAtiON', term.bold(user.location),), '(p)%-20s - %s' % (u'ASSWORd', term.bold_black(u'******'),), '(!)%-20s - %s' % (u'Set SA User Cookie', term.bold(user['sausercookie']),), '(@)%-20s - %s' % (u'Set SA Pass Cookie', term.bold_black(user['sapasscookie']),), '(e)%-20s - %s' % (u'-MAil AddRESS', term.bold(user.email),), # '(!)%-20s - %s' % (u'SA User Cookie', # term.bold(user['sausercookie']),), # '(@)%-70s - %s' % (u'SA Pass Cookie', # term.bold(user['sapasscookie']),), (term.bold('t') + '(i)%-19s - %s' % (u'MEOUt', term.bold( str(user.get('timeout', def_timeout))),)), '(s)%-20s - %s' % (u'YSOP ACCESS', term.bold(u'ENAblEd' if 'sysop' in user.groups else 'diSAblEd')), '(m)%-20s - %s' % (u'ESG', term.bold(u'[%s]' % ('y' if user.get( 'mesg', True) else 'n',),)), '(.)%-20s - %s' % (u'PlAN filE', '%d bytes' % ( len(plan),) if plan else '(NO PlAN.)'), '(x)%-20s - %s' % (u'PERt MOdE', term.bold(u'ENAblEd' if user.get('expert', False) else 'diSAblEd')), '(q)Uit', ] echo(term.normal + term.clear() ) lines = Ansi('\n'.join(menu)).wrap(term.width).splitlines() xpos = max(1, int(term.width / 2) - (40 / 2)) for row, line in enumerate(lines): if row and (0 == row % (term.height - 2)): echo(term.reverse(u'\r\n-- More --')) getch() echo(u'\r\n' + ' ' * xpos + line) echo(u'\r\n\r\n Enter option [ctle.xq]: ') return process_keystroke(getch(), user)
def connect_bot(botname): """ Make a zombie telnet connection to the board as the given bot. """ def read_forever(client): client.read_all() import telnetlib from x84.bbs.ini import CFG from x84.bbs.session import BOTLOCK, BOTQUEUE telnet_addr = CFG.get('telnet', 'addr') telnet_port = CFG.getint('telnet', 'port') with BOTLOCK: client = telnetlib.Telnet() client.set_option_negotiation_callback(callback_cmdopt) client.open(telnet_addr, telnet_port) BOTQUEUE.put(botname) t = threading.Thread(target=read_forever, args=(client,)) t.daemon = True t.start()
def process_keystroke(inp, user): """ Process keystroke, ``inp``, for target ``user``. """ # pylint: disable=R0914,R0912,R0915,R0911,W0603 # Too many local variables # Too many branches # Too many statements # Too many return statements # Using the global statement # ^ lol, this is one of those things that should be # refactored into smaller subroutines =) from x84.bbs import getsession, getterminal, echo, getch, gosub from x84.bbs import LineEditor, ScrollingEditor from x84.default.nua import set_email, set_location from x84.bbs.ini import CFG def_timeout = CFG.getint('system', 'timeout') global EXIT session, term = getsession(), getterminal() is_self = bool(user.handle == session.user.handle) invalid = u'\r\niNVAlid.' assert is_self or 'sysop' in session.user.groups if is_self and inp in (u'c', u'C'): gosub('charset') elif is_self and inp in (u't', u'T'): echo(term.move(term.height - 1, 0)) echo(ABOUT_TERM + u'\r\n') echo(u'\r\ntERMiNAl tYPE: ') term = LineEditor(30, session.env.get('TERM')).read() echo(u"\r\n\r\nset TERM to '%s'? [yn]" % (term,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): session.env['TERM'] = term break elif inp2 in (u'n', u'N'): break elif is_self and inp in (u'w', u'W'): echo(u'\r\ntERMiNAl Width: ') width = LineEditor(3, str(term.width)).read() try: width = int(width) except ValueError: echo(invalid) return True if width < 0 or width > 999: echo(invalid) return True echo(u"\r\n\r\nset COLUMNS=%d? [yn]" % (width,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): term.columns = width break elif inp2 in (u'n', u'N'): break elif is_self and inp in (u'h', u'H'): echo(u'\r\ntERMiNAl hEiGht: ') height = LineEditor(3, str(term.height)).read() try: height = int(height) except ValueError: echo(invalid) return True if height < 0 or height > 999: echo(invalid) return True echo(u"\r\n\r\nset LINES=%d? [yn]" % (height,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): term.rows = height break elif inp2 in (u'n', u'N'): break elif 'sysop' in session.user.groups and inp in (u'd', u'D',): echo(u"\r\n\r\ndElEtE %s ? [yn]" % (user.handle,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.delete() break elif inp2 in (u'n', u'N'): break EXIT = True elif 'sysop' in session.user.groups and inp in (u's', u'S',): sysop = not 'sysop' in user.groups echo(u"\r\n\r\n%s SYSOP ACCESS? [yn]" % ( 'ENAblE' if sysop else 'diSAblE',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): if sysop: user.groups.add('sysop') else: user.groups.remove('sysop') user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'p', u'P'): from x84.default.nua import set_password set_password(user) echo(u"\r\n\r\nSEt PASSWORd ? [yn]") while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'k', 'K',): change = True if user.get('pubkey', False): echo(u"\r\n\r\nSSh PUbliC kEY AlREAdY SEt, ChANGE ? [yn]") while True: inp2 = getch() if inp2 in (u'y', u'Y'): break elif inp2 in (u'n', u'N'): change = False break if change: echo(u"\r\n\r\njUSt PAStE iN YOUR PUbliC kEY, MAkE SURE itS " u"All iN ONE lINE!\r\n\r\n\r\n") editor = ScrollingEditor(width=term.width - 6, yloc=term.height - 3, xloc=3) echo(term.reverse_yellow) pubkey = editor.read() echo(term.normal) if pubkey is not None: if not pubkey.strip(): if user.get('pubkey', None): del user['pubkey'] echo(u'\r\n\r\nYOUR SECREtS ARE NEVER SAfE!\r\n') else: echo(u'\r\n\r\nWElCOME tO thE bROthERhOOd!\r\n') user['pubkey'] = pubkey user.save() getch(timeout=1.5) else: echo(u'\r\n\r\nCANCElEd!!\r\n') getch(timeout=0.5) elif inp in (u'.',): echo(term.move(0, 0) + term.normal + term.clear) echo(term.move(int(term.height * .8), 0)) for line in term.wrap(ABOUT_DOT_PLAN, term.width / 3): echo(line.center(term.width).rstrip() + u'\r\n') echo(u'\r\n\r\nPRESS ANY kEY ...') getch() if is_self: gosub('editor', '.plan') else: tmpkey = '%s-%s' % (user.handle, user.plan) draft = user.get('.plan', u'') session.user[tmpkey] = draft gosub('editor', tmpkey) if session.user.get(tmpkey, u'') != draft: echo(u"\r\n\r\nSEt .PlAN ? [yn]") while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['.plan'] = session.user[tmpkey] break elif inp2 in (u'n', u'N'): break elif inp in (u'l', u'L'): echo(term.move(term.height - 1, 0)) set_location(user) echo(u"\r\n\r\nSEt lOCAtiON tO '%s'? [yn]" % (user.location,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'e', u'E'): echo(term.move(term.height - 1, 0)) set_email(user) echo(u"\r\n\r\nSEt EMAil tO '%s'? [yn]" % (user.email,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'i', u'I'): echo(u'\r\ntiMEOUt (0=NONE): ') timeout = LineEditor(6, str(user.get('timeout', def_timeout))).read() try: timeout = int(timeout) except ValueError: echo(invalid) return True if timeout < 0: echo(invalid) return True echo(u"\r\n\r\nSet tiMEOUt=%s? [yn]" % ( timeout if timeout else 'None',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['timeout'] = timeout session.send_event('set-timeout', timeout) break elif inp2 in (u'n', u'N'): break elif inp in (u'm', u'M'): mesg = False if user.get('mesg', True) else True echo(u"\r\n\r\n%s iNStANt MESSAGiNG? [yn]" % ( 'ENAblE' if mesg else 'DiSAblE',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['mesg'] = mesg break elif inp2 in (u'n', u'N'): break elif inp in (u'x', u'X'): expert = not user.get('expert', False) echo(u"\r\n\r\n%s EXPERt MOdE? [yn]" % ( 'ENAblE' if expert else 'DiSAblE',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['expert'] = expert break elif inp2 in (u'n', u'N'): break elif inp in (u'q', u'Q',): EXIT = True else: return False return True
def __init__(self, client, sid, master_pipes): from x84.bbs.ini import CFG self.client = client self.sid = sid (self.master_write, self.master_read) = master_pipes self.timeout = CFG.getint('system', 'timeout')
def _loop(telnetd): """ Main event loop. Never returns. """ # pylint: disable=R0912,R0914,R0915 # Too many local variables (24/15) import logging import select import socket import time import sys from x84.bbs.exception import Disconnected from x84.terminal import terminals, ConnectTelnet, unregister from x84.bbs.ini import CFG from x84.telnet import TelnetClient from x84.db import DBHandler logger = logging.getLogger() if sys.maxunicode == 65535: logger.warn('Python not built with wide unicode support!') logger.info('listening %s/tcp', telnetd.port) timeout_ipc = CFG.getint('system', 'timeout_ipc') fileno_telnetd = telnetd.server_socket.fileno() locks = dict() def lookup(client): """ Given a telnet client, return a matching session of tuple (client, inp_queue, out_queue, lock). If no session matches a telnet client, (None, None, None, None) is returned. """ for _sid, tty in terminals(): if client == tty.client: return tty return None def telnet_recv(fds): """ test all telnet clients with file descriptors in list fds for recv(). If any are disconnected, signal exit to subprocess, unregister the session (if any). """ # read in any telnet input for client in (clt for (fno, clt) in telnetd.clients.iteritems() if fno in fds): try: client.socket_recv() except Disconnected as err: logger.info('%s Connection Closed: %s.', client.addrport(), err) tty = lookup(client) if tty is None: # no session found, just de-activate this client client.deactivate() else: # signal exit to sub-process and shutdown send_event, send_data = 'exception', Disconnected(err) tty.iqueue.send((send_event, send_data)) unregister(tty) def telnet_send(recv_list): """ test all telnet clients for send(). If any are disconnected, signal exit to subprocess, unregister the session, and return recv_list pruned of their file descriptors. """ for sid, tty in terminals(): if tty.client.send_ready(): try: tty.client.socket_send() except Disconnected as err: logger.debug('%s Disconnected: %s.', sid, err) # client.sock.fileno() can raised 'bad file descriptor', # so, to remove it from the recv_list, reverse match by # instance saved with its FD as a key for telnetd.clients! for _fd, _client in telnetd.clients.items(): if tty.client == _client: if _fd in recv_list: recv_list.remove(_fd) break send_event, send_data = 'exception', Disconnected(err) tty.iqueue.send((send_event, send_data,)) unregister(tty) return recv_list def accept(): """ accept new connection from telnetd.server_socket, and instantiate a new TelnetClient, registering it with dictionary telnetd.clients, and spawning an unmanaged thread for negotiating TERM. """ try: sock, address_pair = telnetd.server_socket.accept() # busy signal if telnetd.client_count() > telnetd.MAX_CONNECTIONS: try: sock.shutdown(socket.SHUT_RDWR) except socket.error: pass sock.close() logger.error('refused new connect; maximum reached.') return client = TelnetClient(sock, address_pair, telnetd.on_naws) telnetd.clients[client.sock.fileno()] = client # spawn negotiation and process registration thread ConnectTelnet(client).start() logger.info('%s Connected.', client.addrport()) except socket.error as err: logger.error('accept error %d:%s', err[0], err[1],) @timeout_alarm(timeout_ipc, False) # REMOVEME def f_send_event(iqueue, event, data): """ Send event to subprocess, signaling an alarm timeout if blocked. """ iqueue.send((event, data)) return True def send_input(client, iqueue): """ Send tcp input to subprocess as 'input' event, signaling an alarm timeout and re-buffering input if blocked. The reasons for the input buffer to block are vaugue """ inp = client.get_input() retval = f_send_event(iqueue, 'input', inp) # if timeout occured, re-buffer input if not retval: client.recv_buffer.fromstring(inp) return retval def session_send(): """ Test all sessions for idle timeout, and signal exit to subprocess, unregister the session. Also test for data received by telnet client, and send to subprocess as 'input' event. """ # accept session event i/o, such as output for sid, tty in terminals(): # poll about and kick off idle users if tty.timeout and tty.client.idle() > tty.timeout: send_event = 'exception' send_data = Disconnected('timeout: %ds' % (tty.client.idle())) tty.iqueue.send((send_event, send_data)) continue elif tty.client.input_ready(): # input buffered on tcp socket, attempt to send to client # with a signal alarm timeout; raising a warning if exceeded. if not send_input(tty.client, tty.iqueue): logger.warn('%s input buffer exceeded', sid) tty.client.deactivate() def handle_lock(tty, event, data): """ handle locking event of (lock-key, (method, stale)) """ method, stale = data if method == 'acquire': if event in locks: # lock already held; check for and display owner, or # acquire a lock from a now-deceased session. held=False for _sid, _tty in terminals(): if _sid == locks[event][1] and _sid != tty.sid: logger.debug('[%s] %r not acquired, held by %s.', tty.sid, (event, data), _sid) held=_sid break if held is not False: logger.debug('[%s] %r discovered stale lock, previously ' 'held by %s.', tty.sid, (event, data), held) del locks[event] if not event in locks: locks[event] = (time.time(), tty.sid) tty.iqueue.send((event, True,)) logger.debug('[%s] %r granted.', tty.sid, (event, data)) else: # caller signals this kind of thread is short-lived, and any # existing lock older than 'stale' should be released. if (stale is not None and time.time() - locks[event][0] > stale): tty.iqueue.send((event, True,)) locks[event] = (time.time(), tty.sid) logger.warn('[%s] %r stale %fs.', tty.sid, (event, data), time.time() - locks[event][0]) # signal busy with matching event, data=False else: tty.iqueue.send((event, False,)) logger.debug('[%s] %r not acquired.', tty.sid, (event, data)) elif method == 'release': if not event in locks: logger.error('[%s] %r failed: no match', tty.sid, (event, data)) else: del locks[event] logger.debug('[%s] %r removed.', tty.sid, (event, data)) def session_recv(fds): """ receive data waiting for session; all data received from subprocess is in form (event, data), and is handled by ipc_recv. if stale is not None, elapsed time lock was held to consider stale and acquire anyway. no actual locks are held or released, just a simple dictionary state/time tracking system. """ disp_handle = lambda handle: ((handle + u' ') if handle is not None and 0 != len(handle) else u'') disp_origin = lambda client: client.addrport().split(':', 1)[0] for sid, tty in terminals(): while tty.oqueue.fileno() in fds and tty.oqueue.poll(): # receive data from pipe, unregister if any error, try: event, data = tty.oqueue.recv() except (EOFError, IOError) as err: # sub-process unexpectedly closed logger.exception(err) unregister(tty) return # 'exit' event, unregisters client if event == 'exit': unregister(tty) return # 'logger' event, propogated upward elif event == 'logger': # prefix message with 'ip:port/nick ' data.msg = '%s[%s] %s' % ( disp_handle(data.handle), disp_origin(tty.client), data.msg) logger.handle(data) # 'output' event, buffer for tcp socket elif event == 'output': tty.client.send_unicode(ucs=data[0], encoding=data[1]) # 'remote-disconnect' event, hunt and destroy elif event == 'remote-disconnect': send_to = data[0] for _sid, _tty in terminals(): if send_to == _sid: send_event = 'exception' send_val = Disconnected( 'remote-disconnect by %s' % (sid,)) tty.iqueue.send((send_event, send_val)) unregister(tty) break # 'route': message passing directly from one session to another elif event == 'route': logger.debug('route %r', (event, data)) tgt_sid, send_event, send_val = data[0], data[1], data[2:] for _sid, _tty in terminals(): if tgt_sid == _sid: _tty.iqueue.send((send_event, send_val)) break # 'global': message broadcasting to all sessions elif event == 'global': logger.debug('broadcast %r', (event, data)) for _sid, _tty in terminals(): if sid != _sid: _tty.iqueue.send((event, data,)) # 'set-timeout': set user-preferred timeout elif event == 'set-timeout': logger.debug('set-timeout %d', data) tty.timeout = data # 'db*': access DBProxy API for shared sqlitedict elif event.startswith('db'): thread = DBHandler(tty.iqueue, event, data) thread.start() # 'lock': access fine-grained bbs-global locking elif event.startswith('lock'): handle_lock(tty, event, data) else: assert False, 'unhandled %r' % ((event, data),) # # main event loop # while True: # shutdown, close & delete inactive sockets, for fileno, client in [(_fno, _client) for (_fno, _client) in telnetd.clients.items() if not _client.active]: logger.info('close %s', telnetd.clients[fileno].addrport(),) try: client.sock.shutdown(socket.SHUT_RDWR) except socket.error: pass client.sock.close() # signal exit to any matching session for _sid, tty in terminals(): if client == tty.client: send_event = 'exception' send_data = Disconnected('deactivated') tty.iqueue.send((send_event, send_data,)) break # unregister del telnetd.clients[fileno] # send tcp data, pruning list of any clients d/c during activity fds = telnet_send([fileno_telnetd] + telnetd.clients.keys()) # extend fd list with all session Pipes fds.extend([tty.oqueue.fileno() for _sid, tty in terminals()]) try: fds = select.select(fds, [], [], 0.1)[0] except select.error as err: logger.exception(err) fds = list() if fileno_telnetd in fds: accept() # recv telnet data, telnet_recv(fds) # recv and handle session events, session_recv(fds) # send any session input data, poll timeout session_send()
def process_keystroke(inp, user): """ Process keystroke, ``inp``, for target ``user``. """ # pylint: disable=R0914,R0912,R0915,R0911,W0603 # Too many local variables # Too many branches # Too many statements # Too many return statements # Using the global statement # ^ lol, this is one of those things that should be # refactored into smaller subroutines =) from x84.bbs import getsession, getterminal, echo, getch, gosub from x84.bbs import LineEditor, Ansi from x84.default.nua import set_email, set_location from x84.bbs.ini import CFG def_timeout = CFG.getint('system', 'timeout') global EXIT session, term = getsession(), getterminal() is_self = bool(user.handle == session.user.handle) invalid = u'\r\niNVAlid.' assert is_self or 'sysop' in session.user.groups if is_self and inp in (u'^',): user['sausercookie'] = u'' user['sapasscookie'] = u'' if is_self and inp in (u'c', u'C'): gosub('charset') elif is_self and inp in (u'@',): echo(u'\r\neNTER SA \'pass\' cookie: ') sapasscookie = LineEditor(50, session.user['sapasscookie']).read() echo(u"\r\n\r\nset SA pass cookie to '%s'? [yn]" % (sapasscookie,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): session.user['sapasscookie'] = sapasscookie break elif inp2 in (u'n', u'N'): break elif is_self and inp in (u'!',): echo(u'\r\neNTER SA \'user\' cookie: ') sausercookie = LineEditor(30, session.user['sausercookie']).read() echo(u"\r\n\r\nset SA user cookie to '%s'? [yn]" % (sausercookie,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): session.user['sausercookie'] = sausercookie break elif inp2 in (u'n', u'N'): break elif is_self and inp in (u't', u'T'): echo(term.move(term.height - 1, 0)) echo(ABOUT_TERM + u'\r\n') echo(u'\r\ntERMiNAl tYPE: ') term = LineEditor(30, session.env.get('TERM')).read() echo(u"\r\n\r\nset TERM to '%s'? [yn]" % (term,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): session.env['TERM'] = term break elif inp2 in (u'n', u'N'): break elif is_self and inp in (u'w', u'W'): echo(u'\r\ntERMiNAl Width: ') width = LineEditor(3, str(term.width)).read() try: width = int(width) except ValueError: echo(invalid) return True if width < 0 or width > 999: echo(invalid) return True echo(u"\r\n\r\nset COLUMNS=%d? [yn]" % (width,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): term.columns = width break elif inp2 in (u'n', u'N'): break elif is_self and inp in (u'h', u'H'): echo(u'\r\ntERMiNAl hEiGht: ') height = LineEditor(3, str(term.height)).read() try: height = int(height) except ValueError: echo(invalid) return True if height < 0 or height > 999: echo(invalid) return True echo(u"\r\n\r\nset LINES=%d? [yn]" % (height,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): term.rows = height break elif inp2 in (u'n', u'N'): break elif 'sysop' in session.user.groups and inp in (u'd', u'D',): echo(u"\r\n\r\ndElEtE %s ? [yn]" % (user.handle,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.delete() break elif inp2 in (u'n', u'N'): break EXIT = True elif 'sysop' in session.user.groups and inp in (u's', u'S',): sysop = not 'sysop' in user.groups echo(u"\r\n\r\n%s SYSOP ACCESS? [yn]" % ( 'ENAblE' if sysop else 'diSAblE',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): if sysop: user.groups.add('sysop') else: user.groups.remove('sysop') user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'p', u'P'): from x84.default.nua import set_password set_password(user) echo(u"\r\n\r\nSEt PASSWORd ? [yn]") while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'.',): echo(term.move(0, 0) + term.normal + term.clear) echo(term.move(int(term.height * .8), 0)) for line in Ansi(ABOUT_DOT_PLAN).wrap( term.width / 3).splitlines(): echo(line.center(term.width).rstrip() + u'\r\n') echo(u'\r\n\r\nPRESS ANY kEY ...') getch() if is_self: gosub('editor', '.plan') else: tmpkey = '%s-%s' % (user.handle, user.plan) draft = user.get('.plan', u'') session.user[tmpkey] = draft gosub('editor', tmpkey) if session.user.get(tmpkey, u'') != draft: echo(u"\r\n\r\nSEt .PlAN ? [yn]") while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['.plan'] = session.user[tmpkey] break elif inp2 in (u'n', u'N'): break elif inp in (u'l', u'L'): echo(term.move(term.height - 1, 0)) set_location(user) echo(u"\r\n\r\nSEt lOCAtiON tO '%s'? [yn]" % (user.location,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'e', u'E'): echo(term.move(term.height - 1, 0)) set_email(user) echo(u"\r\n\r\nSEt EMAil tO '%s'? [yn]" % (user.email,)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user.save() break elif inp2 in (u'n', u'N'): break elif inp in (u'i', u'I'): echo(u'\r\ntiMEOUt (0=NONE): ') timeout = LineEditor(6, str(user.get('timeout', def_timeout))).read() try: timeout = int(timeout) except ValueError: echo(invalid) return True if timeout < 0: echo(invalid) return True echo(u"\r\n\r\nSet tiMEOUt=%s? [yn]" % ( timeout if timeout else 'None',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['timeout'] = timeout session.send_event('set-timeout', timeout) break elif inp2 in (u'n', u'N'): break elif inp in (u'm', u'M'): mesg = False if user.get('mesg', True) else True echo(u"\r\n\r\n%s iNStANt MESSAGiNG? [yn]" % ( 'ENAblE' if mesg else 'DiSAblE',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['mesg'] = mesg break elif inp2 in (u'n', u'N'): break elif inp in (u'x', u'X'): expert = not user.get('expert', False) echo(u"\r\n\r\n%s EXPERt MOdE? [yn]" % ( 'ENAblE' if expert else 'DiSAblE',)) while True: inp2 = getch() if inp2 in (u'y', u'Y'): user['expert'] = expert break elif inp2 in (u'n', u'N'): break elif inp in (u'q', u'Q',): EXIT = True else: return False return True