nakedmudv3.3/
nakedmudv3.3/lib/
nakedmudv3.3/lib/logs/
nakedmudv3.3/lib/misc/
nakedmudv3.3/lib/players/
nakedmudv3.3/lib/txt/
nakedmudv3.3/lib/world/
nakedmudv3.3/lib/world/examples/
nakedmudv3.3/lib/world/examples/mproto/
nakedmudv3.3/lib/world/examples/oproto/
nakedmudv3.3/lib/world/examples/reset/
nakedmudv3.3/lib/world/examples/rproto/
nakedmudv3.3/lib/world/examples/trigger/
nakedmudv3.3/lib/world/limbo/
nakedmudv3.3/lib/world/limbo/room/
nakedmudv3.3/lib/world/limbo/rproto/
nakedmudv3.3/src/alias/
nakedmudv3.3/src/char_vars/
nakedmudv3.3/src/editor/
nakedmudv3.3/src/example_module/
nakedmudv3.3/src/help/
nakedmudv3.3/src/set_val/
nakedmudv3.3/src/socials/
nakedmudv3.3/src/time/
'''
account_handler.py

The login and creation of accounts, and handles all account procedures, loading
and deleting, of characters.
'''
import mud, mudsys, char, hooks, account, socket, event, telnetlib

# control sequences for squelching passwords
squelch   = telnetlib.IAC + telnetlib.WILL + telnetlib.ECHO
unsquelch = telnetlib.IAC + telnetlib.WONT + telnetlib.ECHO



def check_acct_name(name):
    '''Makes sure an account name is valid'''
    return (len(name) > 3 and len(name) < 13 and
            name[0].isalpha() and name.isalnum())

def acct_name_prompt(sock):
    sock.send_raw("What is your account name? ")

def acct_new_password_prompt(sock):
    sock.send_raw("What is your new password? " + squelch)

def acct_password_prompt(sock):
    sock.send_raw("What is your password? " + squelch)

def acct_confirm_password_prompt(sock):
    sock.send_raw("\r\nVerify your password? " + squelch)

def acct_wait_dns_prompt(sock):
    sock.send_raw("Resolving your internet address, have patience... ")

def acct_finish_prompt(sock):
    sock.send_raw("{c\r\n*** Press enter to finish account creation:{n ") 

def acct_finish_handler(sock, arg):
    # pop our input handler for finishing account generation
    sock.pop_ih()

    # log that the account created
    mud.log_string("New account '" + sock.account.name + "' has created.")

    # register and save the account to disk
    mudsys.do_register(sock.account)

def acct_wait_dns_handler(sock, arg):
    # do nothing
    return

def acct_name_handler(sock, arg):
    '''the first prompt a socket encounters; enter a name of an account or
       a new name of an account to create one'''
    if mudsys.account_exists(arg):
        # logging on to an already existing account
        mud.log_string("Account '" + arg + "' is trying to connect.")
        acct = mudsys.load_account(arg)

        # attach our account to our socket. Put in mudsys to prevent scripts
        # from messing around with account and socket connections
        mudsys.attach_account_socket(acct, sock)
        sock.pop_ih()
        sock.push_ih(acct_menu_handler, acct_main_menu)
        sock.push_ih(acct_password_handler, acct_password_prompt)

    elif not check_acct_name(arg):
        sock.send(mud.format_string("That is an invalid account name. Your " \
                                    "account name must only consist of " \
                                    "characters and numbers, and it must be " \
                                    "4 and 12 characters in length. The first "\
                                    "character MUST be a letter. Please pick "\
                                    "another.", False))

    elif mudsys.account_creating(arg):
        sock.send("An account with that name is already creating.")

    else:
        # creating a new account
        mud.log_string("Account '" + arg + "' is trying to create.")

        # create our new account
        acct = mudsys.create_account(arg)
        if acct == None:
            sock.send("Could not create an account with that name.")
        else:
            mudsys.attach_account_socket(acct, sock)
            sock.pop_ih()
            sock.push_ih(acct_menu_handler, acct_main_menu)
            sock.push_ih(acct_finish_handler, acct_finish_prompt)
            sock.push_ih(acct_confirm_password_handler, acct_confirm_password_prompt)
            sock.push_ih(acct_new_password_handler, acct_new_password_prompt)


def acct_new_password_handler(sock, arg):
    '''asks a new account for a password'''
    if len(arg) > 0:
        # put in mudsys to prevent scripts from messing with passwords
        mudsys.set_password(sock.account, arg)
        sock.pop_ih()

def acct_confirm_password_handler(sock, arg):
    '''checks to see if our argument matches our password; if not, we re-enter
       our old password'''
    # password functions put in mudsys to prevent scripts from
    # messing with passwords
    if not mudsys.password_matches(sock.account, arg):
        sock.send("Passwords do not match.")
        sock.pop_ih()
        sock.push_ih(acct_confirm_password_handler,acct_confirm_password_prompt)
        sock.push_ih(acct_password_handler, acct_password_prompt)
    else:
        # Password matches. Keep it and go down a level
        sock.send_raw(unsquelch)
        sock.pop_ih()

def acct_password_handler(sock, arg):
    '''asks an account to verify its password'''
    # password functions put in mudsys to prevent scripts from
    # messing with passwords
    sock.send_raw(unsquelch)
    if not mudsys.password_matches(sock.account, arg):
        sock.send("Incorrect password.")
        sock.close()
    else:
        # password matches, pop our handler and go down a level
        sock.pop_ih()

def find_reconnect(name):
    '''searches through the character list for a PC whose name matches the
       supplied name'''
    name = name.lower()
    for ch in char.char_list():
        if ch.is_pc and name == ch.name.lower():
            return ch
    return None

def acct_load_char(sock, arg):
    '''loads a character attached to the account. Argument supplied must be a
       numeric value corresponding to the character'''
    arg = int(arg)
    if arg >= len(sock.account.characters()) or arg < 0:
        sock.send("Invalid choice!")
    else:
        # get the name
        name = sock.account.characters()[arg]

        # first, try a reconnect
        ch = find_reconnect(name)

        # no reconnect? Try loading our character
        if not ch == None:
            # swap the sockets
            old_sock = ch.sock

            # attach and detach put in mudsys to prevent scripts from
            # messing around with the connections between chars and sockets
            mudsys.detach_char_socket(ch)
            mudsys.attach_char_socket(ch, sock)
            
            if old_sock != None:
                old_sock.close()
            mud.log_string(ch.name + " has reconnected.")
            ch.send("You take over a body already in use.")
            sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt)

        else:
            # load our character. Put in mudsys to prevent scripts from using it
            ch = mudsys.load_char(name)
            if ch == None:
                sock.send("The player file for " + name + " is missing.")
            elif (mudsys.sys_getval("lockdown") != '' and
                  not ch.isInGroup(mudsys.sys_getval("lockdown"))):
                sock.send("You are currently locked out of the mud.")
                mud.extract(ch)
            else:
                # attach put in mudsys to prevent scripts from messing with
                # character and socket links
                mudsys.attach_char_socket(ch, sock)
                if mudsys.try_enter_game(ch):
                    mud.log_string(ch.name + " has entered the game.")
                    sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt)
                    ch.page(mud.get_motd())
                    ch.act("look")
                    hooks.run("enter", hooks.build_info("ch rm", (ch, ch.room)))
                else:
                    sock.send("There was a problem entering the game. " + \
                              "try again later.")
                    # detach put in mudsys t prevent scripts from messing with
                    # character and socket links
                    mudsys.detach_char_socket(ch, sock)
                    mud.extract(ch)

def acct_menu_handler(sock, arg):
    '''parses account commands (new character, enter game, quit, etc)'''
    if len(arg) == 0:
        return
    arg = arg[0].upper()
    if arg.isdigit():
        acct_load_char(sock, arg)
    elif arg == 'Q':
        sock.send("Come back soon!")
        mudsys.do_save(sock.account)
        sock.close()
    elif arg == 'P':
        sock.push_ih(acct_confirm_password_handler,acct_confirm_password_prompt)
        sock.push_ih(acct_new_password_handler, acct_new_password_prompt)
        sock.push_ih(acct_password_handler, acct_password_prompt)
    elif arg == 'N':
        hooks.run("create_character", hooks.build_info("sk", (sock,)))
    else:
        sock.send("Invalid choice!")

def display_acct_chars(sock):
    '''shows the socket a prettied list of characters tied to the account it
       has attached. Prints three names per line.'''
    num_cols   = 3
    print_room = (80 - 10*num_cols)/num_cols
    fmt        = "  {c%2d{g) %-" + str(print_room) + "s"

    sock.send("{w\r\nPlay a Character:")
    i = 0
    for ch in sock.account.characters():
        sock.send_raw(fmt % (i, ch))
        if i % num_cols == num_cols - 1:
            sock.send_raw("\r\n")
        i = i + 1

    if not i % num_cols == 0:
        sock.send_raw("\r\n")

def acct_main_menu(sock):
    '''displays the main menu for the account and asks for a command'''
    if len(sock.account.characters()) > 0:
        display_acct_chars(sock)

    sock.send("\r\n{wAdditional Options:")
    sock.send("  {g[{cP{g]assword change")
    sock.send("  {g[{cN{g]new character\r\n")
    sock.send_raw("Enter choice, or Q to quit:{n ")



################################################################################
# events for blocking action when dns lookup is in progress
################################################################################
def dns_check_event(no_owner, unused, info):
    sock, = hooks.parse_info(info)
    if sock.can_use:
        sock.send("Lookup complete.")
        sock.pop_ih()
        sock.bust_prompt()
    else:
        event.start_event(None, 0.1, dns_check_event, None, info)



################################################################################
# account handler hooks
################################################################################
def account_handler_hook(info):
    # put a nonfunctional prompt up while waiting for the DNS to resolve
    sock, = hooks.parse_info(info)
    sock.push_ih(acct_name_handler, acct_name_prompt)
    sock.push_ih(acct_wait_dns_handler, acct_wait_dns_prompt)
    event.start_event(None, 0.1, dns_check_event, None, info)

def copyover_complete_hook(info):
    sock, = hooks.parse_info(info)
    sock.push_ih(acct_menu_handler, acct_main_menu)
    sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt)



################################################################################
# loading and unloading the module
################################################################################
hooks.add("receive_connection", account_handler_hook)
hooks.add("copyover_complete",  copyover_complete_hook)

def __unload__():
    '''removes the hooks for account handling'''
    hooks.remove("receive_connection", account_handler_hook)
    hooks.remove("copyover_complete",  copyover_complete_hook)