Example #1
0
File: top.py Project: gofore/x84
def describe_ssh_availability(term, session):
    from x84.bbs.ini import CFG
    if session.kind == 'ssh':
        # what a good citizen!
        return

    if not (CFG.has_section('ssh') and
            not CFG.has_option('ssh', 'enabled')
            or CFG.getboolean('ssh', 'enabled')):
        # ssh not enabled
        return

    about_key = (u"You may even use an ssh key, which you can configure from "
                 u"your user profile, " if not session.user.get('pubkey')
                 else u'')
    big_msg = term.bold_blue("Big Brother is Watching You")
    description = (
        u"\r\n\r\n"
        u"    {term.red}You are using {session.kind}, but ssh is available "
        u"on port {ssh_port} of this server.  If you want a secure connection "
        u"with shorter latency, we recommend instead to use ssh!  {about_key}"
        u"Remember: {big_msg}!\r\n\r\n".format(
            term=term, session=session, ssh_port=ssh_port,
            about_key=about_key, big_msg=big_msg))

    show_description(term, description, color=None)
    waitprompt(term)
Example #2
0
def describe_ssh_availability(term, session):
    from x84.bbs.ini import CFG
    if session.kind == 'ssh':
        # what a good citizen!
        return

    if not (CFG.has_section('ssh') and not CFG.has_option('ssh', 'enabled')
            or CFG.getboolean('ssh', 'enabled')):
        # ssh not enabled
        return

    about_key = (u"You may even use an ssh key, which you can configure from "
                 u"your user profile, "
                 if not session.user.get('pubkey') else u'')
    big_msg = term.bold_blue("Big Brother is Watching You")
    description = (
        u"\r\n\r\n"
        u"    {term.red}You are using {session.kind}, but ssh is available "
        u"on port {ssh_port} of this server.  If you want a secure connection "
        u"with shorter latency, we recommend instead to use ssh!  {about_key}"
        u"Remember: {big_msg}!\r\n\r\n".format(term=term,
                                               session=session,
                                               ssh_port=ssh_port,
                                               about_key=about_key,
                                               big_msg=big_msg))

    show_description(term, description, color=None)
    waitprompt(term)
Example #3
0
File: top.py Project: tehmaze/x84
def describe_ssh_availability(term, session):
    from x84.bbs.ini import CFG
    if session.kind == 'ssh':
        # what a good citizen!
        return

    if not (CFG.has_section('ssh') and
            not CFG.has_option('ssh', 'enabled')
            or CFG.getboolean('ssh', 'enabled')):
        # ssh not enabled
        return

    about_key = (u"You may even use an ssh key, which you can configure from "
                 u"your user profile, " if not session.user.get('pubkey')
                 else u'')
    big_msg = term.bold_blue("Big Brother is Watching You")
    description = (
        "    {term.red}You are using {session.kind}, but ssh is available "
        "on port {ssh_port} of this server.  If you want a secure connection "
        "with shorter latency, we recommend instead to use ssh!  {about_key}"
        "Remember: {big_msg}!"
        .format(term=term,
                session=session,
                ssh_port=ssh_port,
                about_key=about_key,
                big_msg=big_msg)
    )

    echo(u'\r\n\r\n')
    for txt in term.wrap(description, width=min(80, term.width)):
        echo(term.move_x(max(0, (term.width // 2) - 40)))
        echo(term.red(txt.rstrip() + '\r\n'))
    echo(u'\r\n\r\n')
    echo(term.center(term.bold_black('Press any key to continue: ')).rstrip())
    term.inkey()
Example #4
0
File: engine.py Project: hick/x84
def get_servers(CFG):
    """
    Given a configuration file, instantiate and return a list of enabled
    servers.
    """
    log = logging.getLogger('x84.engine')

    servers = []

    if CFG.has_section('telnet'):
        # start telnet server instance
        from x84.telnet import TelnetServer
        servers.append(TelnetServer(config=CFG))

    if CFG.has_section('ssh'):
        # start ssh server instance
        #
        # may raise an ImportError for systems where pyOpenSSL and etc. could
        # not be installed (due to any issues with missing python-dev, libffi,
        # cc, etc.).  Allow it to raise naturally, the curious user should
        # either discover and resolve the root issue, or disable ssh if it
        # cannot be resolved.
        from x84.ssh import SshServer
        servers.append(SshServer(config=CFG))

    if CFG.has_section('rlogin'):
        # start rlogin server instance
        from x84.rlogin import RLoginServer
        servers.append(RLoginServer(config=CFG))

    return servers
Example #5
0
def main():
    """
    x84 main entry point. The system begins and ends here.

    Command line arguments to engine.py:

    - ``--config=`` location of alternate configuration file
    - ``--logger=`` location of alternate logging.ini file
    """
    import x84.bbs.ini

    # load existing .ini files or create default ones.
    x84.bbs.ini.init(*parse_args())
    from x84.bbs import get_ini
    from x84.bbs.ini import CFG

    if sys.maxunicode == 65535:
        # apple is the only known bastardized variant that does this;
        # presumably for memory/speed savings (UCS-2 strings are faster
        # than UCS-4).  Python 3 dynamically allocates string types by
        # their widest content, so such things aren't necessary ...
        import warnings
        warnings.warn('This python is built without wide unicode support. '
                      'some internationalized languages will not be possible.')

    # retrieve list of managed servers
    servers = get_servers(CFG)

    # begin unmanaged servers
    if (CFG.has_section('web') and
            (not CFG.has_option('web', 'enabled')
             or CFG.getboolean('web', 'enabled'))):
        # start https server for one or more web modules.
        from x84 import webserve
        webserve.main()

    if get_ini(section='msg', key='network_tags'):
        # start background timer to poll for new messages
        # of message networks we may be a member of.
        from x84 import msgpoll
        msgpoll.main()

    try:
        # begin main event loop
        _loop(servers)
    except KeyboardInterrupt:
        # exit on ^C, killing any client sessions.
        from x84.terminal import kill_session
        for server in servers:
            for idx, thread in enumerate(server.threads[:]):
                if not thread.stopped:
                    thread.stopped = True
                server.threads.remove(thread)
            for key, client in server.clients.items()[:]:
                kill_session(client, 'server shutdown')
                del server.clients[key]
    return 0
Example #6
0
def main():
    """
    x84 main entry point. The system begins and ends here.

    Command line arguments to engine.py:

    - ``--config=`` location of alternate configuration file
    - ``--logger=`` location of alternate logging.ini file
    """
    # load existing .ini files or create default ones.
    import x84.bbs.ini
    x84.bbs.ini.init(*cmdline.parse_args())

    from x84.bbs import get_ini
    from x84.bbs.ini import CFG

    if sys.maxunicode == 65535:
        # apple is the only known bastardized variant that does this;
        # presumably for memory/speed savings (UCS-2 strings are faster
        # than UCS-4).  Python 3 dynamically allocates string types by
        # their widest content, so such things aren't necessary, there.
        import warnings
        warnings.warn('This python is built without wide unicode support. '
                      'some internationalized languages will not be possible.')

    # retrieve list of managed servers
    servers = get_servers(CFG)

    # begin unmanaged servers
    if (CFG.has_section('web') and
            (not CFG.has_option('web', 'enabled')
             or CFG.getboolean('web', 'enabled'))):
        # start https server for one or more web modules.
        from x84 import webserve
        webserve.main()

    if get_ini(section='msg', key='network_tags'):
        # start background timer to poll for new messages
        # of message networks we may be a member of.
        from x84 import msgpoll
        msgpoll.main()

    try:
        # begin main event loop
        _loop(servers)
    except KeyboardInterrupt:
        # exit on ^C, killing any client sessions.
        for server in servers:
            for thread in server.threads[:]:
                if not thread.stopped:
                    thread.stopped = True
                server.threads.remove(thread)
            for key, client in server.clients.items()[:]:
                kill_session(client, 'server shutdown')
                del server.clients[key]
    return 0
Example #7
0
def get_servers(CFG):
    """ Instantiate and return enabled servers by configuration ``CFG``. """
    servers = []

    if (CFG.has_section('telnet') and
            (not CFG.has_option('telnet', 'enabled')
             or CFG.getboolean('telnet', 'enabled'))):
        # start telnet server instance
        from x84.telnet import TelnetServer
        servers.append(TelnetServer(config=CFG))

    if (CFG.has_section('ssh') and
            not CFG.has_option('ssh', 'enabled')
            or CFG.getboolean('ssh', 'enabled')):
        # start ssh server instance
        #
        # may raise an ImportError for systems where pyOpenSSL and etc. could
        # not be installed (due to any issues with missing python-dev, libffi,
        # cc, etc.).  Allow it to raise naturally, the curious user should
        # either discover and resolve the root issue, or disable ssh if it
        # cannot be resolved.
        from x84.ssh import SshServer
        servers.append(SshServer(config=CFG))

    if (CFG.has_section('rlogin') and
            (not CFG.has_option('rlogin', 'enabled')
             or CFG.getboolean('rlogin', 'enabled'))):
        # start rlogin server instance
        from x84.rlogin import RLoginServer
        servers.append(RLoginServer(config=CFG))

    return servers
Example #8
0
File: __init__.py Project: hick/x84
def get_ini(section=None, key=None, getter='get', split=False, splitsep=None):
    """
    Get an ini configuration of ``section`` and ``key``.

    If the option does not exist, an empty list, string, or False
    is returned -- return type decided by the given arguments.

    The ``getter`` method is 'get' by default, returning a string.
    For booleans, use ``getter='get_boolean'``.

    To return a list, use ``split=True``.
    """
    from x84.bbs.ini import CFG
    assert section is not None, section
    assert key is not None, key
    if CFG.has_option(section, key):
        getter = getattr(CFG, getter)
        value = getter(section, key)
        if split and hasattr(value, 'split'):
            return map(str.strip, value.split(splitsep))
        return value
    if getter == 'getboolean':
        return False
    if split:
        return []
    return u''
Example #9
0
 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')
Example #10
0
 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')
Example #11
0
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))
Example #12
0
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()
Example #13
0
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)
Example #14
0
def translate_ttype(ttype):
    from x84.bbs.ini import CFG
    log = logging.getLogger(__name__)

    termcap_unknown = CFG.get('system', 'termcap-unknown')
    termcap_ansi = CFG.get('system', 'termcap-ansi')

    if termcap_unknown != 'no' and ttype == 'unknown':
        log.debug("terminal-type {0!r} => {1!r}".format(
            ttype, termcap_unknown))
        return termcap_unknown

    elif termcap_ansi != 'no' and ttype.lower().startswith('ansi'):
        log.debug("terminal-type {0!r} => {1!r}".format(ttype, termcap_ansi))
        return termcap_ansi

    log.info("terminal type is {0!r}".format(ttype))
    return ttype
Example #15
0
    def queue_for_network(self):
        " Queue message for networks, hosting or sending. "
        from x84.bbs import get_ini

        log = logging.getLogger(__name__)

        # server networks this server is a member of,
        member_networks = get_ini(section='msg',
                                  key='network_tags',
                                  split=True,
                                  splitsep=',')

        # server networks offered by this server,
        my_networks = get_ini(section='msg',
                              key='server_tags',
                              split=True,
                              splitsep=',')

        # check all tags of message; if they match a message network,
        # either record for hosting servers, or schedule for delivery.
        for tag in self.tags:
            section = 'msgnet_{tag}'.format(tag=tag)

            # message is for a network we host
            if tag in my_networks:
                section = 'msgnet_{tag}'.format(tag=tag)
                transdb_name = CFG.get(section, 'trans_db_name')
                transdb = DBProxy(transdb_name)
                with transdb:
                    self.body = u''.join((self.body, format_origin_line()))
                    self.save()
                    transdb[self.idx] = self.idx
                log.info('[{tag}] Stored for network (msgid {self.idx}).'
                         .format(tag=tag, self=self))

            # message is for a another network, queue for delivery
            elif tag in member_networks:
                queuedb_name = CFG.get(section, 'queue_db_name')
                queuedb = DBProxy(queuedb_name)
                with queuedb:
                    queuedb[self.idx] = tag
                log.info('[{tag}] Message (msgid {self.idx}) queued '
                         'for delivery'.format(tag=tag, self=self))
Example #16
0
File: terminal.py Project: hick/x84
def translate_ttype(ttype):
    from x84.bbs.ini import CFG
    log = logging.getLogger(__name__)

    termcap_unknown = CFG.get('system', 'termcap-unknown')
    termcap_ansi = CFG.get('system', 'termcap-ansi')

    if termcap_unknown != 'no' and ttype == 'unknown':
        log.debug("terminal-type {0!r} => {1!r}"
                  .format(ttype, termcap_unknown))
        return termcap_unknown

    elif termcap_ansi != 'no' and ttype.lower().startswith('ansi'):
        log.debug("terminal-type {0!r} => {1!r}"
                  .format(ttype, termcap_ansi))
        return termcap_ansi

    log.info("terminal type is {0!r}".format(ttype))
    return ttype
Example #17
0
File: userbase.py Project: hick/x84
def get_digestpw():
    """
    Returns singleton to password digest routine.
    """
    global FN_PASSWORD_DIGEST
    if FN_PASSWORD_DIGEST is not None:
        return FN_PASSWORD_DIGEST

    from x84.bbs.ini import CFG
    FN_PASSWORD_DIGEST = {
        'bcrypt': _digestpw_bcrypt,
        'internal': _digestpw_internal,
        'plaintext': _digestpw_plaintext,
    }.get(CFG.get('system', 'password_digest'))
    return FN_PASSWORD_DIGEST
Example #18
0
File: userbase.py Project: hick/x84
def get_digestpw():
    """
    Returns singleton to password digest routine.
    """
    global FN_PASSWORD_DIGEST
    if FN_PASSWORD_DIGEST is not None:
        return FN_PASSWORD_DIGEST

    from x84.bbs.ini import CFG
    FN_PASSWORD_DIGEST = {
        'bcrypt': _digestpw_bcrypt,
        'internal': _digestpw_internal,
        'plaintext': _digestpw_plaintext,
    }.get(CFG.get('system', 'password_digest'))
    return FN_PASSWORD_DIGEST
Example #19
0
    def GET(self, num=10):
        """ Return last x callers """

        num = int(num)
        callers = DBProxy('lastcalls', use_session=False).items()
        last = sorted(callers[-num:], reverse=True,
                      key=lambda caller: caller[1][0])

        # output JSON instead?
        if 'json' in web.input(_method='get'):
            return json.dumps(last)

        callers_html = ''
        board = CFG.get('system', 'bbsname', 'x/84')
        page_title = 'Last {num} Callers to {board}'.format(
            num=num, board=board)

        for caller in last:
            callers_html += ''.join(('<li><b>{who}</b> {affil} ',
                                     '<small>at {when}</small></li>')).format(
                who=caller[0],
                affil='(%s)' % caller[1][2] if caller[1][2] else '',
                when=datetime.fromtimestamp(caller[1][0]))

        web.header('Content-Type', 'text/html; charset=utf-8', unique=True)
        output = """
            <!DOCTYPE html>
            <html lang="en-US">
            <head>
                <meta charset="utf-8" />
                <title>{page_title}</title>
            </head>
            <body>
                <h1>{page_title}</h1>
                <ul>
                    {callers_html}
                </ul>
            </body>
            </html>
            """.format(page_title=page_title, callers_html=callers_html)
        return output
Example #20
0
    def GET(self, num=10):
        """ Return last x oneliners """

        num = int(num)
        oneliners = DBProxy('oneliner', use_session=False).items()
        oneliners = [(int(k), v)
                     for (k,
                          v) in DBProxy('oneliner', use_session=False).items()]
        last = oneliners[-num:]

        # output JSON instead?
        if 'json' in web.input(_method='get'):
            return json.dumps(last)

        board = CFG.get('system', 'bbsname', 'x/84')
        page_title = 'Last {num} Oneliners on {board}'.format(num=num,
                                                              board=board)
        oneliners_html = ''

        for line in last:
            val = line[1]
            oneliners_html += '<li><b>{alias}:</b> {oneliner}</li>'.format(
                alias=val['alias'], oneliner=val['oneliner'])

        web.header('Content-Type', 'text/html; charset=utf-8', unique=True)
        output = """
            <!DOCTYPE html>
            <html lang="en-US">
            <head>
                <meta charset="utf-8" />
                <title>{page_title}</title>
            </head>
            <body>
                <h1>{page_title}</h1>
                <ul>
                    {oneliners_html}
                </ul>
            </body>
            </html>
            """.format(page_title=page_title, oneliners_html=oneliners_html)
        return output
Example #21
0
    def GET(self, num=10):
        """ Return last x callers """

        num = int(num)
        callers = DBProxy('lastcalls', use_session=False).items()
        last = sorted(callers[-num:], reverse=True,
                      key=lambda caller: caller[1][0])

        # output JSON instead?
        if 'json' in web.input(_method='get'):
            return json.dumps(last)

        callers_html = ''
        board = CFG.get('system', 'bbsname', 'x/84')
        page_title = 'Last {num} Callers to {board}'.format(
            num=num, board=board)

        for caller in last:
            callers_html += ''.join(('<li><b>{who}</b> {affil} ',
                                     '<small>at {when}</small></li>')).format(
                who=caller[0],
                affil='(%s)' % caller[1][2] if caller[1][2] else '',
                when=datetime.fromtimestamp(caller[1][0]))

        web.header('Content-Type', 'text/html; charset=utf-8', unique=True)
        output = """
            <!DOCTYPE html>
            <html lang="en-US">
            <head>
                <meta charset="utf-8" />
                <title>{page_title}</title>
            </head>
            <body>
                <h1>{page_title}</h1>
                <ul>
                    {callers_html}
                </ul>
            </body>
            </html>
            """.format(page_title=page_title, callers_html=callers_html)
        return output
Example #22
0
    def GET(self, num=10):
        """ Return last x oneliners """

        num = int(num)
        oneliners = DBProxy('oneliner', use_session=False).items()
        oneliners = [(int(k), v) for (k, v) in
                     DBProxy('oneliner', use_session=False).items()]
        last = oneliners[-num:]

        # output JSON instead?
        if 'json' in web.input(_method='get'):
            return json.dumps(last)

        board = CFG.get('system', 'bbsname', 'x/84')
        page_title = 'Last {num} Oneliners on {board}'.format(
            num=num, board=board)
        oneliners_html = ''

        for line in last:
            val = line[1]
            oneliners_html += '<li><b>{alias}:</b> {oneliner}</li>'.format(
                alias=val['alias'], oneliner=val['oneliner'])

        web.header('Content-Type', 'text/html; charset=utf-8', unique=True)
        output = """
            <!DOCTYPE html>
            <html lang="en-US">
            <head>
                <meta charset="utf-8" />
                <title>{page_title}</title>
            </head>
            <body>
                <h1>{page_title}</h1>
                <ul>
                    {oneliners_html}
                </ul>
            </body>
            </html>
            """.format(page_title=page_title, oneliners_html=oneliners_html)
        return output
Example #23
0
def start(web_modules):
    """ fire up a web server with the given modules as endpoints """
    from threading import Thread
    import logging
    import sys
    import os
    from x84.bbs.ini import CFG

    logger = logging.getLogger()
    sys.path.insert(0, os.path.expanduser(CFG.get('system', 'scriptpath')))
    urls = ('/favicon.ico', 'favicon')
    funcs = globals()
    funcs['favicon'] = Favicon

    for mod in web_modules:
        module = None

        # first check for it in the scripttpath's webmodules dir
        try:
            module = __import__('webmodules.%s' % mod,
                                fromlist=('webmodules',))
        except ImportError:
            pass

        # fallback to the engine's webmodules dir
        if module is None:
            module = __import__('x84.webmodules.%s' % mod,
                                fromlist=('x84.webmodules',))

        api = module.web_module()
        urls += api['urls']

        for key in api['funcs']:
            funcs[key] = api['funcs'][key]

    t = Thread(target=server_thread, args=(urls, funcs,))
    t.daemon = True
    t.start()
    logger.info(u'Web modules: %s' % u', '.join(web_modules))
Example #24
0
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
Example #25
0
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()
Example #26
0
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
Example #27
0
File: __init__.py Project: hick/x84
def showart(filepattern, encoding=None, auto_mode=True, center=False,
            poll_cancel=False, msg_cancel=None):
    """
    Yield unicode sequences for any given ANSI Art (of art_encoding). Effort
    is made to parse SAUCE data, translate input to unicode, and trim artwork
    too large to display.  If ``poll_cancel`` is not ``False``, represents
    time as float for each line to block for keypress -- if any is received,
    then iteration ends and ``msg_cancel`` is displayed as last line of art.

    If you provide no ``encoding``, the piece encoding will be based on either
    the encoding in the SAUCE record, the configured default or the default
    fallback ``CP437`` encoding.

    Alternate codecs are available if you provide the ``encoding`` argument.
    For example, if you want to show an Amiga style ASCII art file::

        >>> from x84.bbs import echo, showart
        >>> for line in showart('test.asc', 'topaz'):
        ...     echo(line)

    The ``auto_mode`` flag will, if set, only respect the selected encoding if
    the active session is UTF-8 capable.

    If ``center`` is set to ``True``, the piece will be centered respecting the
    current terminal's width.

    """
    import random
    import glob
    import os
    from sauce import SAUCE
    from x84.bbs.ini import CFG

    session, term = getsession(), getterminal()

    # Open the piece
    try:
        filename = os.path.relpath(random.choice(glob.glob(filepattern)))
    except IndexError:
        filename = None

    if filename is None:
        yield u''.join((
            term.bold_red(u'-- '),
            u'no files matching {0}'.format(filepattern),
            term.bold_red(u' --'),
        ))
        return

    # Parse the piece
    parsed = SAUCE(filename)

    # If no explicit encoding is given, we go through a couple of steps to
    # resolve the possible file encoding:
    if encoding is None:
        # 1. See if the SAUCE record has a font we know about, it's in the
        #    filler
        if parsed.record and parsed.filler_str in SAUCE_FONT_MAP:
            encoding = SAUCE_FONT_MAP[parsed.filler_str]

        # 2. Get the system default art encoding
        elif CFG.has_option('system', 'art_utf8_codec'):
            encoding = CFG.get('system', 'art_utf8_codec')

        # 3. Fall back to CP437
        else:
            encoding = 'cp437'

    # If auto_mode is enabled, we'll only use the input encoding on UTF-8
    # capable terminals, because our codecs do not know how to "transcode"
    # between the various encodings.
    if auto_mode:
        def _decode(what):
            session = getsession()
            if session.encoding == 'utf8':
                return what.decode(encoding)
            elif session.encoding == 'cp437':
                return what.decode('cp437')
            else:
                return what

    # If auto_mode is disabled, we'll just respect whatever input encoding was
    # selected before
    else:
        _decode = lambda what: what.decode(encoding)

    # For wide terminals, center the piece on the screen using cursor movement,
    # if requested
    padding = u''
    if center and term.width > 81:
        padding = term.move_x((term.width / 2) - 40)

    msg_cancel = msg_cancel or u''.join(
        (term.normal,
         term.bold_black(u'-- '),
         u'cancelled {0} by input'.format(filename),
         term.bold_black(u' --'),
         ))

    msg_too_wide = u''.join(
        (term.normal,
         term.bold_black(u'-- '),
         u'cancelled {0}, too wide:: {{0}}'.format(filename),
         term.bold_black(u' --'),
         ))

    lines = _decode(parsed.data).splitlines()
    for idx, line in enumerate(lines):
        # Allow slow terminals to cancel by pressing a keystroke
        if poll_cancel is not False and term.inkey(poll_cancel):
            yield u'\r\n' + term.center(msg_cancel) + u'\r\n'
            return
        line_length = term.length(line.rstrip())
        if not padding and term.width < line_length:
            yield (u'\r\n' +
                   term.center(msg_too_wide.format(line_length)) +
                   u'\r\n')
            return
        if idx == len(lines) - 1:
            # strip DOS end of file (^Z)
            line = line.rstrip('\x1a')
            if not line.strip():
                break
        yield padding + line + u'\r\n'
    yield term.normal
Example #28
0
File: terminal.py Project: hick/x84
 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')
Example #29
0
    def save(self, send_net=True, ctime=None):
        """
        Save message in 'Msgs' sqlite db, and record index in 'tags' db.
        """
        from x84.bbs.ini import CFG
        from x84.bbs import getsession

        session = getsession()
        use_session = bool(session is not None)
        log = logging.getLogger(__name__)
        new = self.idx is None or self._stime is None

        # persist message record to MSGDB
        db_msg = DBProxy(MSGDB, use_session=use_session)
        with db_msg:
            if new:
                self.idx = max([int(key) for key in db_msg.keys()] or [-1]) + 1
                if ctime is not None:
                    self._ctime = self._stime = ctime
                else:
                    self._stime = datetime.datetime.now()
                new = True
            db_msg['%d' % (self.idx,)] = self

        # persist message idx to TAGDB
        db_tag = DBProxy(TAGDB, use_session=use_session)
        with db_tag:
            for tag in db_tag.keys():
                msgs = db_tag[tag]
                if tag in self.tags and self.idx not in msgs:
                    msgs.add(self.idx)
                    db_tag[tag] = msgs
                    log.debug("msg {self.idx} tagged '{tag}'"
                              .format(self=self, tag=tag))
                elif tag not in self.tags and self.idx in msgs:
                    msgs.remove(self.idx)
                    db_tag[tag] = msgs
                    log.info("msg {self.idx} removed tag '{tag}'"
                             .format(self=self, tag=tag))
            for tag in [_tag for _tag in self.tags if _tag not in db_tag]:
                db_tag[tag] = set([self.idx])

        # persist message as child to parent;
        if not hasattr(self, 'parent'):
            self.parent = None
        assert self.parent not in self.children
        if self.parent is not None:
            parent_msg = get_msg(self.parent)
            if self.idx != parent_msg.idx:
                parent_msg.children.add(self.idx)
                parent_msg.save()
            else:
                log.error('Parent idx same as message idx; stripping')
                self.parent = None
                with db_msg:
                    db_msg['%d' % (self.idx)] = self

        if send_net and new and CFG.has_option('msg', 'network_tags'):
            self.queue_for_network()

        log.info(
            u"saved {new}{public}msg {post}, addressed to '{self.recipient}'."
            .format(new='new ' if new else '',
                    public='public ' if 'public' in self.tags else '',
                    post='post' if self.parent is None else 'reply',
                    self=self))
Example #30
0
    def save(self, send_net=True, ctime=None):
        """
        Save message in 'Msgs' sqlite db, and record index in 'tags' db.
        """
        from x84.bbs.ini import CFG
        from x84.bbs import getsession

        session = getsession()
        use_session = bool(session is not None)
        log = logging.getLogger(__name__)
        new = self.idx is None or self._stime is None

        # persist message record to MSGDB
        db_msg = DBProxy(MSGDB, use_session=use_session)
        with db_msg:
            if new:
                self.idx = max(map(int, db_msg.keys()) or [-1]) + 1
                if ctime is not None:
                    self._ctime = self._stime = ctime
                else:
                    self._stime = datetime.datetime.now()
                new = True
            db_msg['%d' % (self.idx, )] = self

        # persist message idx to TAGDB
        db_tag = DBProxy(TAGDB, use_session=use_session)
        with db_tag:
            for tag in db_tag.keys():
                msgs = db_tag[tag]
                if tag in self.tags and self.idx not in msgs:
                    msgs.add(self.idx)
                    db_tag[tag] = msgs
                    log.debug("msg {self.idx} tagged '{tag}'".format(self=self,
                                                                     tag=tag))
                elif tag not in self.tags and self.idx in msgs:
                    msgs.remove(self.idx)
                    db_tag[tag] = msgs
                    log.info("msg {self.idx} removed tag '{tag}'".format(
                        self=self, tag=tag))
            for tag in [_tag for _tag in self.tags if _tag not in db_tag]:
                db_tag[tag] = set([self.idx])

        # persist message as child to parent;
        if not hasattr(self, 'parent'):
            self.parent = None
        assert self.parent not in self.children
        if self.parent is not None:
            parent_msg = get_msg(self.parent)
            if self.idx != parent_msg.idx:
                parent_msg.children.add(self.idx)
                parent_msg.save()
            else:
                log.error('Parent idx same as message idx; stripping')
                self.parent = None
                with db_msg:
                    db_msg['%d' % (self.idx)] = self

        if send_net and new and CFG.has_option('msg', 'network_tags'):
            self.queue_for_network()

        log.info(
            u"saved {new}{public}msg {post}, addressed to '{self.recipient}'.".
            format(new='new ' if new else '',
                   public='public ' if 'public' in self.tags else '',
                   post='post' if self.parent is None else 'reply',
                   self=self))
Example #31
0
def main(msg=None):
    """ Main procedure. """
    from x84.bbs import Msg, getsession, echo, getterminal
    from x84.bbs.ini import CFG
    session, term = getsession(), getterminal()
    new_message = True if msg is None else False
    network_tags = None
    is_network_msg = False

    if CFG.has_option('msg', 'network_tags'):
        network_tags = map(
            str.strip, CFG.get('msg', 'network_tags').split(','))

    if new_message:
        msg = Msg()
        msg.tags = ('public',)

    elif network_tags is not None:
        net_tags = [tag for tag in network_tags if tag in msg.tags]
        is_network_msg = True if len(net_tags) > 0 else False

    banner()
    while True:
        session.activity = 'Constructing a %s message' % (
            u'public' if u'public' in msg.tags else u'private',)
        if network_tags and new_message:
            is_network_msg = prompt_network(msg, network_tags)
        if not prompt_recipient(msg, is_network_msg):
            break
        if not prompt_subject(msg):
            break
        if not prompt_public(msg):
            break
        if not prompt_body(msg):
            break
        # XXX
        if not prompt_tags(msg):
            break
        if is_network_msg:
            # XX move to sub-func
            how_many = len([tag for tag in network_tags if tag in msg.tags])
            if how_many == 0:
                echo(u''.join((u'\r\n',
                               term.bold_yellow_on_red(
                                   u' YOU tOld ME thiS WAS A NEtWORk MESSAGE. '
                                   u'WhY did YOU liE?! '),
                               u'\r\n')))
                term.inkey(timeout=7)
                continue
            if how_many > 1:
                echo(u''.join((
                    u'\r\n', term.bold_yellow_on_red(
                        u' ONlY ONE NEtWORk CAN bE POStEd tO At A tiME, '
                        u'SORRY '),
                    u'\r\n')))
                continue
            if u'public' not in msg.tags:
                echo(u''.join((u'\r\n',
                               term.bold_yellow_on_red(
                                   u" YOU ShOUldN't SENd PRiVAtE "
                                   u"MESSAGES OVER tHE NEtWORk... "),
                               u'\r\n')))
                term.inkey(timeout=7)
                continue
        display_msg(msg)
        if not prompt_send():
            break

        msg.save()
        session.user['msgs_sent'] = session.user.get('msgs_sent', 0) + 1
        return True
    return False
Example #32
0
File: engine.py Project: hick/x84
def _loop(servers):
    """
    Main event loop. Never returns.
    """
    # pylint: disable=R0912,R0914,R0915
    #         Too many local variables (24/15)
    import logging
    import select
    import sys
    from x84.terminal import get_terminals, kill_session
    from x84.bbs.ini import CFG
    from x84.fail2ban import get_fail2ban_function

    SELECT_POLL = 0.02 # polling time is 20ms

    # WIN32 has no session_fds (multiprocess queues are not polled using
    # select), use a persistently empty set; for WIN32, sessions are always
    # polled for data at every loop.
    WIN32 = sys.platform.lower().startswith('win32')
    session_fds = set()

    log = logging.getLogger('x84.engine')

    if not len(servers):
        raise ValueError("No servers configured for event loop! (ssh, telnet)")

    tap_events = CFG.getboolean('session', 'tap_events')
    check_ban = get_fail2ban_function()
    locks = dict()

    while True:
        # shutdown, close & delete inactive clients,
        for server in servers:
            # bbs sessions that are no longer active on the socket
            # level -- send them a 'kill signal'
            for key, client in server.clients.items()[:]:
                if not client.is_active():
                    kill_session(client, 'socket shutdown')
                    del server.clients[key]
            # on-connect negotiations that have completed or failed.
            # delete their thread instance from further evaluation
            for thread in [_thread for _thread in server.threads
                           if _thread.stopped][:]:
                server.threads.remove(thread)

        server_fds = [server.server_socket.fileno() for server in servers]
        client_fds = [fd for fd in server.client_fds() for server in servers]
        check_r = server_fds + client_fds
        if not WIN32:
            session_fds = get_session_output_fds(servers)
            check_r += session_fds

        # We'd like to use timeout 'None', but the registration of
        # a new client in terminal.start_process surprises us with new
        # file descriptors for the session i/o. unless we loop for
        # additional `session_fds', a connecting client would block.
        ready_r, _, _ = select.select(check_r, [], [], SELECT_POLL)

        for fd in ready_r:
            # see if any new tcp connections were made
            server = find_server(servers, fd)
            if server is not None:
                accept(log, server, check_ban)

        # receive new data from tcp clients.
        client_recv(servers, log)
        terms = get_terminals()

        # receive new data from session terminals
        if WIN32 or set(session_fds) & set(ready_r):
            try:
                session_recv(locks, terms, log, tap_events)
            except IOError, err:
                # if the ipc closes while we poll, warn and continue
                log.warn(err)

        # send tcp data to clients
        client_send(terms, log)

        # send session data, poll for user-timeout and disconnect them
        session_send(terms)
Example #33
0
def main(msg=None):
    """ Main procedure. """
    from x84.bbs import Msg, getsession, echo, getterminal
    from x84.bbs.ini import CFG
    session, term = getsession(), getterminal()
    new_message = True if msg is None else False
    network_tags = None
    is_network_msg = False

    if CFG.has_option('msg', 'network_tags'):
        network_tags = map(
            str.strip, CFG.get('msg', 'network_tags').split(','))

    if CFG.has_option('msg', 'server_tags'):
        if network_tags is None:
            network_tags = list()
        network_tags += map(
            str.strip, CFG.get('msg', 'server_tags').split(','))

    if new_message:
        msg = Msg()
        msg.tags = ('public',)

    elif network_tags is not None:
        net_tags = [tag for tag in network_tags if tag in msg.tags]
        is_network_msg = True if len(net_tags) > 0 else False

    banner()
    while True:
        session.activity = 'Constructing a %s message' % (
            u'public' if u'public' in msg.tags else u'private',)
        if network_tags and new_message:
            is_network_msg = prompt_network(msg, network_tags)
        if not prompt_recipient(msg, is_network_msg):
            break
        if not prompt_subject(msg):
            break
        if not prompt_public(msg):
            break
        if not prompt_body(msg):
            break
        # XXX
        if not prompt_tags(msg):
            break
        if is_network_msg:
            # XX move to sub-func
            how_many = len([tag for tag in network_tags if tag in msg.tags])
            if how_many == 0:
                echo(u''.join((u'\r\n',
                               term.bold_yellow_on_red(
                                   u' YOU tOld ME thiS WAS A NEtWORk MESSAGE. '
                                   u'WhY did YOU liE?! '),
                               u'\r\n')))
                term.inkey(timeout=7)
                continue
            if how_many > 1:
                echo(u''.join((
                    u'\r\n', term.bold_yellow_on_red(
                        u' ONlY ONE NEtWORk CAN bE POStEd tO At A tiME, '
                        u'SORRY '),
                    u'\r\n')))
                continue
            if u'public' not in msg.tags:
                echo(u''.join((u'\r\n',
                               term.bold_yellow_on_red(
                                   u" YOU ShOUldN't SENd PRiVAtE "
                                   u"MESSAGES OVER tHE NEtWORk... "),
                               u'\r\n')))
                term.inkey(timeout=7)
                continue
        display_msg(msg)
        if not prompt_send():
            break

        msg.save()
        session.user['msgs_sent'] = session.user.get('msgs_sent', 0) + 1
        return True
    return False
Example #34
0
 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')