account_handler.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. '''
  2. account_handler.py
  3. The login and creation of accounts, and handles all account procedures, loading
  4. and deleting, of characters.
  5. '''
  6. import mud, mudsys, char, hooks, account, socket, event, telnetlib, utils
  7. # control sequences for squelching passwords
  8. squelch = telnetlib.IAC + telnetlib.WILL + telnetlib.ECHO
  9. unsquelch = telnetlib.IAC + telnetlib.WONT + telnetlib.ECHO
  10. def check_acct_name(name):
  11. '''Makes sure an account name is valid'''
  12. return (len(name) > 3 and len(name) < 13 and
  13. name[0].isalpha() and name.isalnum())
  14. def login_method_prompt(sock):
  15. sock.send_raw("Choose an option: ")
  16. def acct_new_password_prompt(sock):
  17. sock.send_raw("\r\nWhat is your new password? " + squelch)
  18. def acct_password_prompt(sock):
  19. sock.send_raw("What is your password? " + squelch)
  20. def acct_wait_dns_prompt(sock):
  21. sock.send_raw(" Resolving your internet address, please have patience... ")
  22. def try_create_account(sock, name, psswd):
  23. if mudsys.account_exists(name):
  24. return False
  25. elif mudsys.account_creating(name):
  26. return False
  27. elif not check_acct_name(name):
  28. return False
  29. else:
  30. # creating a new account
  31. mud.log_string("Account '" + name + "' is trying to create.")
  32. # create our new account
  33. acct = mudsys.create_account(name)
  34. if acct == None:
  35. return False
  36. else:
  37. mudsys.attach_account_socket(acct, sock)
  38. mudsys.set_password(acct, psswd)
  39. sock.pop_ih()
  40. sock.push_ih(acct_menu_handler, acct_main_menu)
  41. #sock.push_ih(acct_finish_handler, acct_finish_prompt)
  42. # log that the account created
  43. mud.log_string("New account '" + acct.name + "' has created.")
  44. # register and save the account to disk
  45. mudsys.do_register(acct)
  46. return True
  47. return False
  48. def try_load_account(sock, name, psswd):
  49. '''Attempt to load an account with the given name and password.'''
  50. if not mudsys.account_exists(name):
  51. return False
  52. else:
  53. acct = mudsys.load_account(name)
  54. if not mudsys.password_matches(acct, psswd):
  55. return False
  56. # successful load
  57. mudsys.attach_account_socket(acct, sock)
  58. sock.pop_ih()
  59. sock.push_ih(acct_menu_handler, acct_main_menu)
  60. return True
  61. return False
  62. def login_method_handler(sock, arg):
  63. args = arg.split()
  64. if len(args) == 0:
  65. return
  66. args[0] = args[0].lower()
  67. if "create".startswith(args[0]):
  68. if len(args) != 3:
  69. return
  70. elif mudsys.account_exists(args[1]):
  71. sock.send("{cAn account by that name already exists.{n\r\n")
  72. elif not check_acct_name(args[1]):
  73. txt = "{cThat is an invalid account name. Your account name must "\
  74. "only consist of characters and numbers, and it must be " \
  75. "4 and 12 characters in length. The first character MUST be "\
  76. "a letter. Please pick another.{n"
  77. sock.send(mud.format_string(txt, False))
  78. elif not try_create_account(sock, args[1], args[2]):
  79. sock.send("Your account was unable to be created.")
  80. elif "load".startswith(args[0]):
  81. if len(args) != 3:
  82. return
  83. elif not try_load_account(sock, args[1], args[2]):
  84. sock.send("{cInvalid account name or password.{n\r\n")
  85. elif "guest".startswith(args[0]):
  86. if (mudsys.sys_getval("lockdown") != '' and
  87. not utils.is_keyword(mudsys.sys_getval("lockdown"), "player")):
  88. sock.send("{cThe mud is currently locked out to new players.{n\r\n")
  89. else:
  90. sock.pop_ih()
  91. hooks.run("create_guest", hooks.build_info("sk", (sock,)))
  92. def acct_wait_dns_handler(sock, arg):
  93. # do nothing
  94. return
  95. def acct_new_password_handler(sock, arg):
  96. '''asks a new account for a password'''
  97. sock.send_raw(unsquelch)
  98. if len(arg) > 0:
  99. # put in mudsys to prevent scripts from messing with passwords
  100. mudsys.set_password(sock.account, arg)
  101. sock.pop_ih()
  102. def acct_password_handler(sock, arg):
  103. '''asks an account to verify its password'''
  104. # password functions put in mudsys to prevent scripts from
  105. # messing with passwords
  106. sock.send_raw(unsquelch)
  107. if not mudsys.password_matches(sock.account, arg):
  108. sock.send("Incorrect password.")
  109. sock.close()
  110. else:
  111. # password matches, pop our handler and go down a level
  112. sock.pop_ih()
  113. def find_reconnect(name):
  114. '''searches through the character list for a PC whose name matches the
  115. supplied name'''
  116. name = name.lower()
  117. for ch in char.char_list():
  118. if ch.is_pc and name == ch.name.lower():
  119. return ch
  120. return None
  121. def acct_load_char(sock, name):
  122. '''loads a character attached to the account. Argument supplied must be a
  123. name of the corresponding character'''
  124. chars = sock.account.characters()
  125. if not name.lower() in [n.lower() for n in sock.account.characters()]:
  126. sock.send("A character by that name does not exist on your account.")
  127. else:
  128. # first, try a reconnect
  129. ch = find_reconnect(name)
  130. # no reconnect? Try loading our character
  131. if not ch == None:
  132. # swap the sockets
  133. old_sock = ch.sock
  134. # attach and detach put in mudsys to prevent scripts from
  135. # messing around with the connections between chars and sockets
  136. mudsys.detach_char_socket(ch)
  137. mudsys.attach_char_socket(ch, sock)
  138. if old_sock != None:
  139. old_sock.close()
  140. mud.log_string(ch.name + " has reconnected.")
  141. # ch.act("clear")
  142. ch.send("You take over a body already in use.")
  143. ch.act("look")
  144. hooks.run("reconnect", hooks.build_info("ch", (ch,)))
  145. sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt, "playing")
  146. else:
  147. # load our character. Put in mudsys to prevent scripts from using it
  148. ch = mudsys.load_char(name)
  149. if ch == None:
  150. sock.send("The player file for " + name + " is missing.")
  151. elif (mudsys.sys_getval("lockdown") != '' and
  152. not ch.isInGroup(mudsys.sys_getval("lockdown"))):
  153. sock.send("You are currently locked out of the mud.")
  154. mud.extract(ch)
  155. else:
  156. # attach put in mudsys to prevent scripts from messing with
  157. # character and socket links
  158. mudsys.attach_char_socket(ch, sock)
  159. if mudsys.try_enter_game(ch):
  160. mud.log_string(ch.name + " has entered the game.")
  161. sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt,
  162. "playing")
  163. # ch.act("clear")
  164. ch.page(mud.get_motd())
  165. ch.act("look")
  166. hooks.run("enter", hooks.build_info("ch rm", (ch, ch.room)))
  167. else:
  168. sock.send("There was a problem entering the game. " + \
  169. "try again later.")
  170. # detach put in mudsys t prevent scripts from messing with
  171. # character and socket links
  172. mudsys.detach_char_socket(ch, sock)
  173. mud.extract(ch)
  174. def acct_menu_handler(sock, arg):
  175. '''parses account commands (new character, enter game, quit, etc)'''
  176. if len(arg) == 0:
  177. return
  178. args = arg.split()
  179. args[0] = args[0].upper()
  180. if args[0].isdigit():
  181. opts = sock.account.characters()
  182. arg = int(args[0])
  183. if arg < 0 or arg >= len(opts):
  184. sock.send("Invalid choice!")
  185. else:
  186. acct_load_char(sock, opts[arg])
  187. elif args[0] == 'L':
  188. if len(args) == 1:
  189. sock.send("Which character would you like to load?")
  190. else:
  191. acct_load_char(sock, args[1])
  192. elif args[0] == 'Q' or "QUIT".startswith(args[0]):
  193. sock.send("Come back soon!")
  194. mudsys.do_save(sock.account)
  195. sock.close()
  196. elif args[0] == 'P':
  197. # sock.push_ih(acct_confirm_password_handler,acct_confirm_password_prompt)
  198. sock.push_ih(acct_new_password_handler, acct_new_password_prompt)
  199. sock.push_ih(acct_password_handler, acct_password_prompt)
  200. elif args[0] == 'N':
  201. if "player" in [x.strip() for x in mudsys.sys_getval("lockdown").split(",")]:
  202. sock.send("New characters are not allowed to be created at this time.")
  203. else:
  204. hooks.run("create_character", hooks.build_info("sk", (sock,)))
  205. else:
  206. sock.send("Invalid choice!")
  207. def display_acct_chars(sock):
  208. '''shows the socket a prettied list of characters tied to the account it
  209. has attached. Prints three names per line.'''
  210. num_cols = 3
  211. print_room = (80 - 10*num_cols)/num_cols
  212. fmt = " {c%2d{n) %-" + str(print_room) + "s"
  213. sock.send("{w\r\nPlay a Character:")
  214. i = 0
  215. for ch in sock.account.characters():
  216. sock.send_raw(fmt % (i, ch))
  217. if i % num_cols == num_cols - 1:
  218. sock.send_raw("\r\n")
  219. i = i + 1
  220. if not i % num_cols == 0:
  221. sock.send_raw("\r\n")
  222. def acct_main_menu(sock):
  223. '''displays the main menu for the account and asks for a command'''
  224. line_buf = "%-38s" % " "
  225. # make the account menu look pretty
  226. img = ["+--------------------------------------+",
  227. "| |",
  228. "| |",
  229. "| |",
  230. "| |",
  231. "| |",
  232. "| |",
  233. "| |",
  234. "| |",
  235. "| |",
  236. "| I M A G E |",
  237. "| H E R E |",
  238. "| |",
  239. "| |",
  240. "| |",
  241. "| |",
  242. "| |",
  243. "| |",
  244. "| |",
  245. "| |",
  246. "| |",
  247. "| |",
  248. "+--------------------------------------+"]
  249. # the lines for displaying our account options
  250. opts = [ ]
  251. chars = sock.account.characters()
  252. chars.sort()
  253. if len(chars) > 0:
  254. opts.append(" {wPlay A Character: ")
  255. for i in range(len(chars)):
  256. opts.append(" {n[{c%1d{n] %-30s" % (i,chars[i]))
  257. opts.append(line_buf)
  258. # append our other displays
  259. opts.append(" {wAccount Management: ")
  260. opts.append(" {n[{cP{n]assword change ")
  261. opts.append(" {n[{cN{n]ew character ")
  262. opts.append(" {n[{cL{n]oad character <name> ")
  263. opts.append(line_buf)
  264. opts.append(line_buf)
  265. opts.append(line_buf)
  266. # fill up our height to be in line with the image
  267. while len(opts) < len(img) - 2:
  268. opts.insert(0, line_buf)
  269. # append our title
  270. opts.insert(1, " {nW E L C O M E T O ")
  271. opts.insert(2, " {nN A K E D M U D ")
  272. # display all of our info
  273. sock.send("")
  274. for i in range(max(len(opts), len(img))):
  275. if i < len(opts):
  276. sock.send_raw(opts[i])
  277. else:
  278. sock.send_raw(line_buf)
  279. if i < len(img):
  280. sock.send_raw("{n%s" % img[i])
  281. sock.send("")
  282. sock.send_raw("{nEnter choice, or Q to quit: ")
  283. ################################################################################
  284. # events for blocking action when dns lookup is in progress
  285. ################################################################################
  286. def dns_check_event(owner, void, info):
  287. '''After a socket connects, monitor their hostname until dns lookup is
  288. complete. Then, put the socket into the account handler.
  289. '''
  290. sock, = hooks.parse_info(info)
  291. if sock != None and sock.can_use:
  292. sock.send(" Lookup complete.")
  293. sock.send("================================================================================")
  294. sock.send(mud.get_greeting())
  295. sock.pop_ih()
  296. sock.bust_prompt()
  297. # mud.log_string("new connection from " + sock.hostname)
  298. else:
  299. event.start_event(None, 0.2, dns_check_event, None, info)
  300. ################################################################################
  301. # account handler hooks
  302. ################################################################################
  303. def account_handler_hook(info):
  304. '''When a socket connects, put them into the account handler menu.'''
  305. # put a nonfunctional prompt up while waiting for the DNS to resolve
  306. sock, = hooks.parse_info(info)
  307. sock.push_ih(login_method_handler, login_method_prompt)
  308. mud.log_string("new socket, %d, attempting to connect" % sock.uid)
  309. sock.send(mud.get_greeting())
  310. sock.send("== Options Are ================================================================")
  311. sock.send(" Load account : load <account> <password>")
  312. sock.send(" Create account : create <account> <password>")
  313. sock.send(" Play as guest : guest")
  314. sock.send("===============================================================================")
  315. sock.send("")
  316. '''
  317. sock.push_ih(acct_wait_dns_handler, acct_wait_dns_prompt)
  318. sock.send("================================================================================")
  319. event.start_event(None, 0.2, dns_check_event, None, info)
  320. '''
  321. def copyover_complete_hook(info):
  322. sock, = hooks.parse_info(info)
  323. sock.push_ih(acct_menu_handler, acct_main_menu)
  324. sock.push_ih(mudsys.handle_cmd_input, mudsys.show_prompt, "playing")
  325. ################################################################################
  326. # loading and unloading the module
  327. ################################################################################
  328. hooks.add("receive_connection", account_handler_hook)
  329. hooks.add("copyover_complete", copyover_complete_hook)
  330. def __unload__():
  331. '''removes the hooks for account handling'''
  332. hooks.remove("receive_connection", account_handler_hook)
  333. hooks.remove("copyover_complete", copyover_complete_hook)