source file: p10/connection.py
file stats: 872 lines, 794 executed: 91.1% covered
   1. #!/usr/bin/env python
   2. 
   3. # Things that aren't implemented:
   4. # * RPING
   5. # * RPONG
   6. # * ASLL
   7. # * UPING
   8. # * WHOWAS
   9. 
  10. #import threading
  11. import asyncore
  12. import socket
  13. import fnmatch
  14. 
  15. import base64
  16. import parser
  17. import commands.account
  18. import commands.admin
  19. import commands.asll
  20. import commands.away
  21. import commands.burst
  22. import commands.clearmode
  23. import commands.connect
  24. import commands.create
  25. import commands.destruct
  26. import commands.end_of_burst
  27. import commands.eob_ack
  28. import commands.error
  29. import commands.gline
  30. import commands.info
  31. import commands.invite
  32. import commands.join
  33. import commands.jupe
  34. import commands.kick
  35. import commands.kill
  36. import commands.links
  37. import commands.lusers
  38. import commands.mode
  39. import commands.motd
  40. import commands.names
  41. import commands.nick
  42. import commands.notice
  43. import commands.numberrelay
  44. import commands.part
  45. import commands.password
  46. import commands.ping
  47. import commands.pong
  48. import commands.privmsg
  49. import commands.quit
  50. import commands.rping
  51. import commands.rpong
  52. import commands.server
  53. import commands.settime
  54. import commands.silence
  55. import commands.squit
  56. import commands.stats
  57. import commands.svsjoin
  58. import commands.svsnick
  59. import commands.time
  60. import commands.topic
  61. import commands.trace
  62. import commands.uping
  63. import commands.version
  64. import commands.wallchops
  65. import commands.wallops
  66. import commands.wallusers
  67. import commands.wallvoices
  68. import commands.whois
  69. import commands.whowas
  70. 
  71. class connection(asyncore.dispatcher):
  72.     """ Represents a connection upstream """
  73. 
  74.     _state = None
  75.     connstate = None
  76.     _parser = None
  77.     numeric = None
  78.     _endpoint = None
  79.     _password = None
  80.     _upstream_password = None
  81.     _buffer = ""
  82.     _data = ""
  83.     _last_pong = 0
  84. 
  85.     DISCONNECTED = 0
  86.     CONNECTED = 1
  87.     CHALLENGED = 2
  88.     HANDSHAKE = 3
  89.     AUTHENTICATED = 4
  90.     COMPLETE = 5
  91. 
  92.     def __init__(self, state):
  93.         """ Sets up the state that this connection will alter """
  94.         asyncore.dispatcher.__init__(self)
  95.         self._state = state
  96.         self.connstate = self.DISCONNECTED
  97.         self.numeric = None
  98.         self._upstream_password = None
  99.         self._password = None
 100.         self._endpoint = None
 101.         self._parser =  parser.parser(state.maxClientNumerics)
 102.         self._buffer = ""
 103.         self._data = ""
 104.         self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
 105. 
 106.     def start(self, endpoint, password):
 107.         # Create our socket
 108.         self._endpoint = endpoint
 109.         self._password = password
 110.         self._connect()
 111.         return self
 112. 
 113.     def _connect(self):
 114.         self.connect(self._endpoint)
 115.         self.connstate = self.CONNECTED
 116. 
 117.         # Send pass and server - don't use the parser at this point
 118.         self._buffer += "PASS :" + self._password + "\r\n"
 119.         self._buffer += "SERVER " + self._state.getServerName() + " 1 " + str(self._state.ts()) + " " + str(self._state.ts()) + " J10 " + base64.createNumeric((self._state.getServerID(), 262143)) + " +s :" + self._state.getServerDescription() + "\r\n"
 120.         self.connstate = self.CHALLENGED
 121. 
 122.         # Set up stuff for authentication
 123.         self._parser.registerHandler("PASS", commands.password.password(self._state, self))
 124.         self._parser.registerHandler("ERROR", commands.error.error(self._state))
 125.         self._last_pong = self._state.ts()
 126.         self._last_ping = self._state.ts()
 127. 
 128.     def _setupCallbacks(self):
 129.         self._state.registerCallback(self._state.CALLBACK_NEWUSER, self.callbackNewUser)
 130.         self._state.registerCallback(self._state.CALLBACK_CHANGENICK, self.callbackChangeNick)
 131.         self._state.registerCallback(self._state.CALLBACK_NEWSERVER, self.callbackNewServer)
 132.         self._state.registerCallback(self._state.CALLBACK_AUTHENTICATE, self.callbackAuthenticate)
 133.         self._state.registerCallback(self._state.CALLBACK_USERMODECHANGE, self.callbackChangeUserMode)
 134.         self._state.registerCallback(self._state.CALLBACK_AWAY, self.callbackAway)
 135.         self._state.registerCallback(self._state.CALLBACK_BACK, self.callbackBack)
 136.         self._state.registerCallback(self._state.CALLBACK_CHANNELCREATE, self.callbackChannelCreate)
 137.         self._state.registerCallback(self._state.CALLBACK_CHANNELJOIN, self.callbackChannelJoin)
 138.         self._state.registerCallback(self._state.CALLBACK_CHANNELPART, self.callbackChannelPart)
 139.         self._state.registerCallback(self._state.CALLBACK_CHANNELPARTALL, self.callbackPartAll)
 140.         self._state.registerCallback(self._state.CALLBACK_CHANNELMODECHANGE, self.callbackChannelChangeMode)
 141.         self._state.registerCallback(self._state.CALLBACK_CHANNELBANADD, self.callbackChannelAddBan)
 142.         self._state.registerCallback(self._state.CALLBACK_CHANNELBANREMOVE, self.callbackChannelRemoveBan)
 143.         self._state.registerCallback(self._state.CALLBACK_CHANNELBANCLEAR, self.callbackChannelClearBans)
 144.         self._state.registerCallback(self._state.CALLBACK_CHANNELOP, self.callbackChannelOp)
 145.         self._state.registerCallback(self._state.CALLBACK_CHANNELDEOP, self.callbackChannelDeop)
 146.         self._state.registerCallback(self._state.CALLBACK_CHANNELCLEAROPS, self.callbackChannelClearOps)
 147.         self._state.registerCallback(self._state.CALLBACK_CHANNELVOICE, self.callbackChannelVoice)
 148.         self._state.registerCallback(self._state.CALLBACK_CHANNELDEVOICE, self.callbackChannelDevoice)
 149.         self._state.registerCallback(self._state.CALLBACK_CHANNELCLEARVOICES, self.callbackChannelClearVoices)
 150.         self._state.registerCallback(self._state.CALLBACK_GLINEADD, self.callbackGlineAdd)
 151.         self._state.registerCallback(self._state.CALLBACK_GLINEREMOVE, self.callbackGlineRemove)
 152.         self._state.registerCallback(self._state.CALLBACK_INVITE, self.callbackInvite)
 153.         self._state.registerCallback(self._state.CALLBACK_JUPEADD, self.callbackJupeAdd)
 154.         self._state.registerCallback(self._state.CALLBACK_JUPEREMOVE, self.callbackJupeRemove)
 155.         self._state.registerCallback(self._state.CALLBACK_REQUESTADMIN, self.callbackAdminInfo)
 156.         self._state.registerCallback(self._state.CALLBACK_REQUESTINFO, self.callbackInfoRequest)
 157.         self._state.registerCallback(self._state.CALLBACK_CHANNELKICK, self.callbackKick)
 158.         self._state.registerCallback(self._state.CALLBACK_CHANNELPARTZOMBIE, self.callbackZombiePart)
 159.         self._state.registerCallback(self._state.CALLBACK_CHANNELDESTROY, self.callbackChannelDestroy)
 160.         self._state.registerCallback(self._state.CALLBACK_QUIT, self.callbackQuit)
 161.         self._state.registerCallback(self._state.CALLBACK_KILL, self.callbackKill)
 162.         self._state.registerCallback(self._state.CALLBACK_REQUESTLUSERS, self.callbackLusers)
 163.         self._state.registerCallback(self._state.CALLBACK_REQUESTLINKS, self.callbackLinks)
 164.         self._state.registerCallback(self._state.CALLBACK_REQUESTMOTD, self.callbackMOTD)
 165.         self._state.registerCallback(self._state.CALLBACK_REQUESTNAMES, self.callbackNames)
 166.         self._state.registerCallback(self._state.CALLBACK_CHANNELTOPIC, self.callbackTopic)
 167.         self._state.registerCallback(self._state.CALLBACK_SILENCEADD, self.callbackSilenceAdd)
 168.         self._state.registerCallback(self._state.CALLBACK_SILENCEREMOVE, self.callbackSilenceRemove)
 169.         self._state.registerCallback(self._state.CALLBACK_SERVERQUIT, self.callbackSquit)
 170.         self._state.registerCallback(self._state.CALLBACK_REQUESTVERSION, self.callbackRequestVersion)
 171.         self._state.registerCallback(self._state.CALLBACK_REQUESTSTATS, self.callbackRequestStats)
 172.         self._state.registerCallback(self._state.CALLBACK_TRACE, self.callbackTrace)
 173.         self._state.registerCallback(self._state.CALLBACK_PING, self.callbackPing)
 174.         self._state.registerCallback(self._state.CALLBACK_PONG, self.callbackPong)
 175.         self._state.registerCallback(self._state.CALLBACK_REQUESTWHOIS, self.callbackRequestWhois)
 176.         self._state.registerCallback(self._state.CALLBACK_PRIVMSG, self.callbackPrivmsg)
 177.         self._state.registerCallback(self._state.CALLBACK_OOBMSG, self.callbackOobmsg)
 178.         self._state.registerCallback(self._state.CALLBACK_NOTICE, self.callbackNotice)
 179.         self._state.registerCallback(self._state.CALLBACK_WALLOPS, self.callbackWallops)
 180.         self._state.registerCallback(self._state.CALLBACK_WALLUSERS, self.callbackWallusers)
 181.         self._state.registerCallback(self._state.CALLBACK_WALLVOICES, self.callbackWallvoices)
 182.         self._state.registerCallback(self._state.CALLBACK_WALLCHOPS, self.callbackWallchops)
 183. 
 184.     def _teardownCallbacks(self):
 185.         self._state.deregisterCallback(self._state.CALLBACK_NEWUSER, self.callbackNewUser)
 186.         self._state.deregisterCallback(self._state.CALLBACK_CHANGENICK, self.callbackChangeNick)
 187.         self._state.deregisterCallback(self._state.CALLBACK_NEWSERVER, self.callbackNewServer)
 188.         self._state.deregisterCallback(self._state.CALLBACK_AUTHENTICATE, self.callbackAuthenticate)
 189.         self._state.deregisterCallback(self._state.CALLBACK_USERMODECHANGE, self.callbackChangeUserMode)
 190.         self._state.deregisterCallback(self._state.CALLBACK_AWAY, self.callbackAway)
 191.         self._state.deregisterCallback(self._state.CALLBACK_BACK, self.callbackBack)
 192.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELCREATE, self.callbackChannelCreate)
 193.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELJOIN, self.callbackChannelJoin)
 194.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELPART, self.callbackChannelPart)
 195.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELPARTALL, self.callbackPartAll)
 196.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELMODECHANGE, self.callbackChannelChangeMode)
 197.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELBANADD, self.callbackChannelAddBan)
 198.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELBANREMOVE, self.callbackChannelRemoveBan)
 199.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELBANCLEAR, self.callbackChannelClearBans)
 200.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELOP, self.callbackChannelOp)
 201.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELDEOP, self.callbackChannelDeop)
 202.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELCLEAROPS, self.callbackChannelClearOps)
 203.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELVOICE, self.callbackChannelVoice)
 204.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELDEVOICE, self.callbackChannelDevoice)
 205.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELCLEARVOICES, self.callbackChannelClearVoices)
 206.         self._state.deregisterCallback(self._state.CALLBACK_GLINEADD, self.callbackGlineAdd)
 207.         self._state.deregisterCallback(self._state.CALLBACK_GLINEREMOVE, self.callbackGlineRemove)
 208.         self._state.deregisterCallback(self._state.CALLBACK_INVITE, self.callbackInvite)
 209.         self._state.deregisterCallback(self._state.CALLBACK_JUPEADD, self.callbackJupeAdd)
 210.         self._state.deregisterCallback(self._state.CALLBACK_JUPEREMOVE, self.callbackJupeRemove)
 211.         self._state.deregisterCallback(self._state.CALLBACK_REQUESTADMIN, self.callbackAdminInfo)
 212.         self._state.deregisterCallback(self._state.CALLBACK_REQUESTINFO, self.callbackInfoRequest)
 213.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELKICK, self.callbackKick)
 214.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELPARTZOMBIE, self.callbackZombiePart)
 215.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELDESTROY, self.callbackChannelDestroy)
 216.         self._state.deregisterCallback(self._state.CALLBACK_QUIT, self.callbackQuit)
 217.         self._state.deregisterCallback(self._state.CALLBACK_KILL, self.callbackKill)
 218.         self._state.deregisterCallback(self._state.CALLBACK_REQUESTLUSERS, self.callbackLusers)
 219.         self._state.deregisterCallback(self._state.CALLBACK_REQUESTLINKS, self.callbackLinks)
 220.         self._state.deregisterCallback(self._state.CALLBACK_REQUESTMOTD, self.callbackMOTD)
 221.         self._state.deregisterCallback(self._state.CALLBACK_REQUESTNAMES, self.callbackNames)
 222.         self._state.deregisterCallback(self._state.CALLBACK_CHANNELTOPIC, self.callbackTopic)
 223.         self._state.deregisterCallback(self._state.CALLBACK_SILENCEADD, self.callbackSilenceAdd)
 224.         self._state.deregisterCallback(self._state.CALLBACK_SILENCEREMOVE, self.callbackSilenceRemove)
 225.         self._state.deregisterCallback(self._state.CALLBACK_SERVERQUIT, self.callbackSquit)
 226.         self._state.deregisterCallback(self._state.CALLBACK_REQUESTVERSION, self.callbackRequestVersion)
 227.         self._state.deregisterCallback(self._state.CALLBACK_REQUESTSTATS, self.callbackRequestStats)
 228.         self._state.deregisterCallback(self._state.CALLBACK_TRACE, self.callbackTrace)
 229.         self._state.deregisterCallback(self._state.CALLBACK_PING, self.callbackPing)
 230.         self._state.deregisterCallback(self._state.CALLBACK_PONG, self.callbackPong)
 231.         self._state.deregisterCallback(self._state.CALLBACK_REQUESTWHOIS, self.callbackRequestWhois)
 232.         self._state.deregisterCallback(self._state.CALLBACK_PRIVMSG, self.callbackPrivmsg)
 233.         self._state.deregisterCallback(self._state.CALLBACK_OOBMSG, self.callbackOobmsg)
 234.         self._state.deregisterCallback(self._state.CALLBACK_NOTICE, self.callbackNotice)
 235.         self._state.deregisterCallback(self._state.CALLBACK_WALLOPS, self.callbackWallops)
 236.         self._state.deregisterCallback(self._state.CALLBACK_WALLUSERS, self.callbackWallusers)
 237.         self._state.deregisterCallback(self._state.CALLBACK_WALLVOICES, self.callbackWallvoices)
 238.         self._state.deregisterCallback(self._state.CALLBACK_WALLCHOPS, self.callbackWallchops)
 239. 
 240.     def _setupParser(self):
 241.         p = self._parser
 242.         p.registerHandler("AC", commands.account.account(self._state))
 243.         p.registerHandler("AD", commands.admin.admin(self._state))
 244.         p.registerHandler("LL", commands.asll.asll(self._state))
 245.         p.registerHandler("A", commands.away.away(self._state))
 246.         p.registerHandler("B", commands.burst.burst(self._state))
 247.         p.registerHandler("CM", commands.clearmode.clearmode(self._state))
 248.         p.registerHandler("CO", commands.connect.connect(self._state))
 249.         p.registerHandler("C", commands.create.create(self._state))
 250.         p.registerHandler("DE", commands.destruct.destruct(self._state))
 251.         p.registerHandler("DS", commands.wallops.wallops(self._state))
 252.         p.registerHandler("EB", commands.end_of_burst.end_of_burst(self._state, self))
 253.         p.registerHandler("EA", commands.eob_ack.eob_ack(self._state))
 254.         p.registerHandler("Y", commands.error.error(self._state))
 255.         p.registerHandler("GL", commands.gline.gline(self._state))
 256.         p.registerHandler("F", commands.info.info(self._state))
 257.         p.registerHandler("I", commands.invite.invite(self._state))
 258.         p.registerHandler("J", commands.join.join(self._state))
 259.         p.registerHandler("JU", commands.jupe.jupe(self._state))
 260.         p.registerHandler("K", commands.kick.kick(self._state))
 261.         p.registerHandler("D", commands.kill.kill(self._state))
 262.         p.registerHandler("LI", commands.links.links(self._state))
 263.         p.registerHandler("LU", commands.lusers.lusers(self._state))
 264.         p.registerHandler("M", commands.mode.mode(self._state))
 265.         p.registerHandler("MO", commands.motd.motd(self._state))
 266.         p.registerHandler("E", commands.names.names(self._state))
 267.         p.registerHandler("N", commands.nick.nick(self._state))
 268.         p.registerHandler("O", commands.notice.notice(self._state))
 269.         p.registerHandler("OM", commands.mode.mode(self._state)) # opmodes get handled exactly the same as normal modes
 270.         p.registerHandler("L", commands.part.part(self._state))
 271.         p.registerHandler("G", commands.ping.ping(self._state, self))
 272.         p.registerHandler("Z", commands.pong.pong(self._state, self))
 273.         p.registerHandler("P", commands.privmsg.privmsg(self._state))
 274.         p.registerHandler("Q", commands.quit.quit(self._state))
 275.         #p.registerHandler("RI", commands.rping.rping(self._state))
 276.         #p.registerHandler("RO", commands.rpong.rpong(self._state))
 277.         p.registerHandler("S", commands.server.server(self._state, None))
 278.         p.registerHandler("SE", commands.settime.settime(self._state))
 279.         p.registerHandler("U", commands.silence.silence(self._state))
 280.         p.registerHandler("SQ", commands.squit.squit(self._state))
 281.         p.registerHandler("R", commands.stats.stats(self._state))
 282.         p.registerHandler("SJ", commands.svsjoin.svsjoin(self._state))
 283.         p.registerHandler("SN", commands.svsnick.svsnick(self._state))
 284.         p.registerHandler("TI", commands.time.time(self._state))
 285.         p.registerHandler("T", commands.topic.topic(self._state))
 286.         p.registerHandler("TR", commands.trace.trace(self._state))
 287.         p.registerHandler("UP", commands.uping.uping(self._state))
 288.         p.registerHandler("V", commands.version.version(self._state))
 289.         p.registerHandler("WC", commands.wallchops.wallchops(self._state))
 290.         p.registerHandler("WA", commands.wallops.wallops(self._state))
 291.         p.registerHandler("WU", commands.wallusers.wallusers(self._state))
 292.         p.registerHandler("WV", commands.wallvoices.wallvoices(self._state))
 293.         p.registerHandler("W", commands.whois.whois(self._state))
 294.         p.registerHandler("X", commands.whowas.whowas(self._state))
 295.         p.registerHandler("252", commands.numberrelay.numberrelay(self._state, "252"))
 296.         p.registerHandler("254", commands.numberrelay.numberrelay(self._state, "254"))
 297.         p.registerHandler("255", commands.numberrelay.numberrelay(self._state, "255"))
 298.         p.registerHandler("256", commands.numberrelay.numberrelay(self._state, "256"))
 299.         p.registerHandler("257", commands.numberrelay.numberrelay(self._state, "257"))
 300.         p.registerHandler("258", commands.numberrelay.numberrelay(self._state, "258"))
 301.         p.registerHandler("259", commands.numberrelay.numberrelay(self._state, "259"))
 302.         p.registerHandler("351", commands.numberrelay.numberrelay(self._state, "351"))
 303.         p.registerHandler("353", commands.numberrelay.numberrelay(self._state, "353"))
 304.         p.registerHandler("364", commands.numberrelay.numberrelay(self._state, "364"))
 305.         p.registerHandler("365", commands.numberrelay.numberrelay(self._state, "365"))
 306.         p.registerHandler("366", commands.numberrelay.numberrelay(self._state, "366"))
 307.         p.registerHandler("371", commands.numberrelay.numberrelay(self._state, "371"))
 308.         p.registerHandler("374", commands.numberrelay.numberrelay(self._state, "374"))
 309.         p.registerHandler("375", commands.numberrelay.numberrelay(self._state, "375"))
 310.         p.registerHandler("376", commands.numberrelay.numberrelay(self._state, "376"))
 311. 
 312.     def _sendLine(self, source_client, token, args):
 313.         """ Send a line upsteam
 314. 
 315.             source_client: An integer, or None, representing which client is sending this message
 316.             token: The token to be sent.
 317.             args: An array of strings making up the message body """
 318.         self._buffer += self._parser.build(source_client, token, args)
 319. 
 320.     def registerNumeric(self, numeric):
 321.         self.numeric = numeric
 322. 
 323.     def registerUpstreamPassword(self, password):
 324.         self._upstream_password = password
 325. 
 326.     def registerEOB(self):
 327.         self._sendLine((self._state.getServerID(), None), "EA", [])
 328. 
 329.     def registerPing(self, arg):
 330.         self._sendLine((self._state.getServerID(), None), "Z", [base64.createNumeric((self._state.getServerID(), None)), arg])
 331. 
 332.     def registerPong(self):
 333.         self._last_pong = self._state.ts()
 334. 
 335.     def close_connection(self):
 336.         self.close()
 337.         self.connstate = self.COMPLETE
 338. 
 339.     def do_ping(self):
 340.         # Give a 60 second grace between ping being sent and timing out
 341.         if (self._state.ts() - 60) > self._last_ping and self._last_ping > self._last_pong:
 342.             self.error("Ping Timeout")
 343.         elif self._last_ping < (self._state.ts() - 180):
 344.             self._sendLine((self._state.getServerID(), None), "G", [base64.createNumeric((self._state.getServerID(), None))])
 345.             self._last_ping = self._state.ts()
 346. 
 347.     def error(self, reason):
 348.         """ TODO: Handles errors on the connection """
 349.         print "ERROR: " + reason
 350.         self._sendLine((self._state.getServerID(), None), "Y", [reason])
 351. 
 352.     def __recursiveBurstServer(self, server):
 353.         # We don't burst ourselves
 354.         if self._state.servers[server].numeric != self._state.getServerID():
 355.             self.callbackNewServer(((self._state.servers[server].origin, None),
 356.                                      self._state.servers[server].numeric,
 357.                                      self._state.servers[server].name,
 358.                                      self._state.servers[server].maxclient,
 359.                                      self._state.servers[server].boot_ts,
 360.                                      self._state.servers[server].link_ts,
 361.                                      self._state.servers[server].protocol,
 362.                                      self._state.servers[server].hops,
 363.                                      self._state.servers[server].flags,
 364.                                      self._state.servers[server].description))
 365.         for child in self._state.servers[server].children:
 366.             # Don't burst the server back to us
 367.             if child != self.numeric:
 368.                 self.__recursiveBurstServer(child)
 369. 
 370.     def _sendBurst(self):
 371.         # Now we start listening
 372.         self._setupCallbacks()
 373. 
 374.         # Send servers
 375.         self.__recursiveBurstServer(self._state.getServerID())
 376. 
 377.         # Send g-lines
 378.         for (mask, description, expires, active, mod_time) in self._state.glines():
 379.             if active:
 380.                 self.callbackGlineAdd(((self._state.getServerID(), None), mask, None, expires, description))
 381.             else:
 382.                 self.callbackGlineRemove(((self._state.getServerID(), None), mask, None))
 383. 
 384.         # Send jupes
 385.         for (mask, description, expires, active, mod_time) in self._state.jupes():
 386.             if active:
 387.                 self.callbackJupeAdd(((self._state.getServerID(), None), mask, None, expires, description))
 388.             else:
 389.                 self.callbackJupeRemove(((self._state.getServerID(), None), mask, None))
 390. 
 391.         # Send users
 392.         for user in self._state.users:
 393.             self.callbackNewUser((  (self._state.users[user].numeric[0], None),
 394.                                     self._state.users[user].numeric,
 395.                                     self._state.users[user].nickname,
 396.                                     self._state.users[user].username,
 397.                                     self._state.users[user].hostname,
 398.                                     self._state.users[user].modes(),
 399.                                     self._state.users[user].ip,
 400.                                     self._state.users[user].hops,
 401.                                     self._state.users[user].ts,
 402.                                     self._state.users[user].fullname))
 403. 
 404.         # Send channels
 405.         for channel in self._state.channels:
 406. 
 407.             # First part of burst
 408.             burst = [channel, str(self._state.channels[channel].ts)]
 409. 
 410.             # Channel modes
 411.             (modestr, modeargs) = self._buildModeString(self._state.channels[channel].modes())
 412. 
 413.             if modestr != "":
 414.                 burst.append(modestr)
 415.             burst += modeargs
 416. 
 417.             # Get users on channel
 418.             users = self._state.channels[channel].users()
 419.             ovs = []
 420.             os = []
 421.             vs = []
 422.             plains = []
 423.             for user in users:
 424.                 numeric = base64.createNumeric(user)
 425.                 if "o" in users[user] and "v" in users[user]:
 426.                     ovs.append(numeric)
 427.                 elif "o" in users[user]:
 428.                     os.append(numeric)
 429.                 elif "v" in users[user]:
 430.                     vs.append(numeric)
 431.                 else:
 432.                     plains.append(numeric)
 433. 
 434.             bans = self._state.channels[channel].bans
 435. 
 436.             done = False
 437. 
 438.             while not done:
 439.                 # Limit to 510 in size
 440.                 remaining = 510 - 6 # origin and token + spacing
 441.                 for arg in burst:
 442.                     remaining -= len(arg)
 443.                     remaining -= 1 # space
 444. 
 445.                 userstr = ''
 446. 
 447.                 if len(plains) > 0:
 448.                     while len(plains) and remaining > 6:
 449.                         plain = plains.pop()
 450.                         userstr += plain + ","
 451.                         remaining -= len(plain) + 1
 452. 
 453.                 if len(vs) > 0:
 454.                     first = True
 455.                     while len(vs) and remaining > 8:
 456.                         v = vs.pop()
 457.                         if first:
 458.                             userstr += v + ":v,"
 459.                             first = False
 460.                         else:
 461.                             userstr += v + ","
 462.                         remaining -= len(v) + 1
 463. 
 464.                 if len(os) > 0:
 465.                     first = True
 466.                     while len(os) and remaining > 8:
 467.                         o = os.pop()
 468.                         if first:
 469.                             userstr += o + ":o,"
 470.                             first = False
 471.                         else:
 472.                             userstr += o + ","
 473.                         remaining -= len(o) + 1
 474. 
 475.                 if len(ovs) > 0:
 476.                     first = True
 477.                     while len(ovs) and remaining > 9:
 478.                         ov = ovs.pop()
 479.                         if first:
 480.                             userstr += ov + ":ov,"
 481.                             first = False
 482.                         else:
 483.                             userstr += ov + ","
 484.                         remaining -= len(ov) + 1
 485. 
 486.                 if userstr != '':
 487.                     burst.append(userstr[:-1])
 488. 
 489.                 # Bans
 490. 
 491.                 banstr = ''
 492. 
 493.                 if len(bans) > 0:
 494.                     first = True
 495.                     while len(bans) and remaining > len(bans[-1]) + 1:
 496.                         ban = bans.pop()
 497.                         if first:
 498.                             banstr += "%" + ban
 499.                             first = False
 500.                         else:
 501.                             banstr += " " + ban
 502.                         remaining -= len(ban) + 1
 503. 
 504.                 if banstr != '':
 505.                     burst.append(banstr)
 506. 
 507.                 self._sendLine((self._state.getServerID(), None), "B", burst)
 508.                 burst = [channel, str(self._state.channels[channel].ts)]
 509. 
 510.                 # Check we're done
 511.                 if len(plains) == 0 and len(vs) == 0 and len(os) == 0 and len(ovs) == 0 and len(bans) == 0:
 512.                     done = True
 513. 
 514.         self._sendLine((self._state.getServerID(), None), "EB", [])
 515. 
 516.     def writable(self):
 517.         return (len(self._buffer) > 0)
 518. 
 519.     def handle_write(self):
 520.         sent = self.send(self._buffer)
 521.         print "SENT: " + self._buffer[:sent]
 522.         self._buffer = self._buffer[sent:]
 523. 
 524.     def handle_close(self):
 525.         if self.connstate != self.COMPLETE:
 526.             self._state.quitServer((self._state.getServerID(), None), (self.numeric, None), "Connection closed unexpectedly", self._state.ts())
 527.             self.connstate = self.COMPLETE
 528.         self._teardownCallbacks()
 529.         self.close()
 530. 
 531.     def handle_read(self):
 532.         # Get this chunk
 533.         self._data += self.recv(512)
 534. 
 535.         # Get an entire line
 536.         nlb = self._data.find("\n")
 537.         while nlb > -1:
 538.             line = self._data[:nlb+1]
 539.             print "HANDLING: " + line
 540.             # Update state
 541.             if self.connstate == self.CHALLENGED and self._upstream_password != None:
 542.                 # Check password
 543.                 if self._password == self._upstream_password:
 544.                     self.connstate = self.HANDSHAKE
 545.                     self._parser.registerHandler("SERVER", commands.server.server(self._state, self))
 546.                 else:
 547.                     self.error("Password not as expected")
 548.             if self.connstate == self.HANDSHAKE and self.numeric != None:
 549.                 self.connstate = self.AUTHENTICATED
 550.                 self._setupParser()
 551.                 # We're all good, send netburst
 552.                 self._sendBurst()
 553.             if self.connstate < self.AUTHENTICATED:
 554.                 try:
 555.                     self._parser.parsePreAuth(line, (self._state.getServerID(), None))
 556.                 except Exception, e:
 557.                     self.error(str(e))
 558.             else:
 559.                 try:
 560.                     self._parser.parse(line)
 561.                 except Exception, e:
 562.                     self.error(str(e))
 563.             # Get our next complete line if one exists
 564.             self._data = self._data[nlb+1:]
 565.             nlb = self._data.find("\n")
 566.         self.do_ping()
 567. 
 568.     def _buildModeString(self, modes):
 569.         modestr = ""
 570.         curmode = ""
 571.         modeargs = []
 572.         for mode in modes:
 573.             if curmode != mode[0][0]:
 574.                 modestr += mode[0]
 575.                 curmode = mode[0][0]
 576.             else:
 577.                 modestr += mode[0][1]
 578.             if mode[1] != None:
 579.                 modeargs.append(str(mode[1]))
 580.         return (modestr, modeargs)
 581. 
 582.     def callbackNewUser(self, (origin, numeric, nickname, username, hostname, modes, ip, hops, ts, fullname)):
 583.         # Broadcast to all away from origin
 584.         if self._state.getNextHop(origin) != self.numeric:
 585.             line = [nickname, str(hops + 1), str(ts), username, hostname]
 586.             (modestr, modeargs) = self._buildModeString(modes)
 587.             if modestr != "":
 588.                 line.append(modestr)
 589.             line += modeargs
 590.             line.append(base64.toBase64(ip, 6))
 591.             line.append(base64.createNumeric(numeric))
 592.             line.append(fullname)
 593.             self._sendLine(origin, "N", line)
 594. 
 595.     def callbackChangeNick(self, (origin, numeric, newnick, newts)):
 596.         # Broadcast to all away from origin
 597.         if self._state.getNextHop(origin) != self.numeric:
 598.             if origin != numeric:
 599.                 self._sendLine(origin, "SN", [base64.createNumeric(numeric), newnick])
 600.             else:
 601.                 self._sendLine(numeric, "N", [newnick, str(newts)])
 602. 
 603.     def callbackNewServer(self, (origin, numeric, name, maxclient, boot_ts, link_ts, protocol, hops, flags, description)):
 604.         # Broadcast to all away from origin
 605.         if self._state.getNextHop(origin) != self.numeric:
 606.             (modestr, modeargs) = self._buildModeString(flags)
 607.             if modestr == '':
 608.                 modestr = '+'
 609.             self._sendLine(origin, "S", [name, str(hops + 1), str(boot_ts), str(link_ts), protocol, base64.createNumeric((numeric, maxclient)), modestr, description])
 610. 
 611.     def callbackSquit(self, (origin, numeric, reason, ts)):
 612.         # If this uplink is the one being disconnected
 613.         if numeric[0] == self.numeric:
 614.             self.close_connection()
 615.             self._sendLine(origin, "SQ", [self._state.getServerName(), "0", reason])
 616.         # Otherwise, broadcast away from origin
 617.         elif self._state.getNextHop(origin) != self.numeric:
 618.             self._sendLine(origin, "SQ", [self._state.numeric2nick(numeric), str(ts), reason])
 619. 
 620.     def callbackAuthenticate(self, (origin, numeric, acname)):
 621.         # Broadcast to all away from origin
 622.         if self._state.getNextHop(origin) != self.numeric:
 623.             self._sendLine(origin, "AC", [base64.createNumeric(numeric), acname])
 624. 
 625.     def callbackAway(self, (numeric, reason)):
 626.         # Broadcast to all away from origin
 627.         if self._state.getNextHop(numeric) != self.numeric:
 628.             self._sendLine(numeric, "A", [reason])
 629. 
 630.     def callbackBack(self, (numeric)):
 631.         # Broadcast to all away from origin
 632.         if self._state.getNextHop(numeric) != self.numeric:
 633.             self._sendLine(numeric, "A", [])
 634. 
 635.     def callbackChannelCreate(self, (origin, name, ts)):
 636.         # Broadcast to all servers away from origin
 637.         if self._state.getNextHop(origin) != self.numeric:
 638.             self._sendLine(origin, "C", [name, str(ts)])
 639. 
 640.     def callbackChannelJoin(self, (origin, numeric, name, modes, ts)):
 641.         # Broadcast to all servers away from origin
 642.         if self._state.getNextHop(origin) != self.numeric:
 643.             # If it's a forced join, must be a SJ
 644.             if origin != numeric:
 645.                 self._sendLine(origin, "SJ", [base64.createNumeric(numeric), name])
 646.             else:
 647.                 self._sendLine(origin, "J", [name, str(ts)])
 648.             # In theory, joins should never be called if the channel already exists
 649.             # so we must force any modes on
 650.             if "o" in modes:
 651.                 self.callbackChannelOp((origin, name, numeric))
 652.             if "v" in modes:
 653.                 self.callbackChannelVoice((origin, name, numeric))
 654. 
 655.     def callbackChannelPart(self, (numeric, name, reason)):
 656.         if self._state.getNextHop(numeric) != self.numeric:
 657.             self._sendLine(numeric, "P", [name, reason])
 658. 
 659.     def callbackPartAll(self, (numeric)):
 660.         if self._state.getNextHop(numeric) != self.numeric:
 661.             self._sendLine(numeric, "J", ["0"])
 662. 
 663.     def callbackChannelChangeMode(self, (origin, name, modes)):
 664.         if self._state.getNextHop(origin) != self.numeric:
 665.             line = [name]
 666.             (modestr, modeargs) = self._buildModeString(modes)
 667.             line.append(modestr)
 668.             line += modeargs
 669.             line.append(str(self._state.channels[name].ts))
 670.             self._sendLine(origin, "M", line)
 671. 
 672.     def callbackChannelAddBan(self, (origin, name, mask)):
 673.         self.callbackChannelChangeMode((origin, name, [("+b", mask)]))
 674. 
 675.     def callbackChannelRemoveBan(self, (origin, name, ban)):
 676.         self.callbackChannelChangeMode((origin, name, [("-b", ban)]))
 677. 
 678.     def callbackChannelClearBans(self, (origin, name)):
 679.         if self._state.getNextHop(origin) != self.numeric:
 680.             self._sendLine(origin, "CM", [name, "b"])
 681. 
 682.     def callbackChannelOp(self, (origin, channel, user)):
 683.         self.callbackChannelChangeMode((origin, channel, [("+o", base64.createNumeric(user))]))
 684. 
 685.     def callbackChannelDeop(self, (origin, channel, user)):
 686.         self.callbackChannelChangeMode((origin, channel, [("-o", base64.createNumeric(user))]))
 687. 
 688.     def callbackChannelClearOps(self, (origin, name)):
 689.         if self._state.getNextHop(origin) != self.numeric:
 690.             self._sendLine(origin, "CM", [name, "o"])
 691. 
 692.     def callbackChannelVoice(self, (origin, channel, user)):
 693.         self.callbackChannelChangeMode((origin, channel, [("+v", base64.createNumeric(user))]))
 694. 
 695.     def callbackChannelDevoice(self, (origin, channel, user)):
 696.         self.callbackChannelChangeMode((origin, channel, [("-v", base64.createNumeric(user))]))
 697. 
 698.     def callbackChannelClearVoices(self, (origin, name)):
 699.         if self._state.getNextHop(origin) != self.numeric:
 700.             self._sendLine(origin, "CM", [name, "v"])
 701. 
 702.     def _getGline(self, mask):
 703.         for gline in self._state.glines():
 704.             if mask == gline[0]:
 705.                 return gline
 706. 
 707.     def callbackGlineAdd(self, (origin, mask, target, expires, description)):
 708.         gline = self._getGline(mask)
 709.         if self._state.getNextHop(origin) != self.numeric and target == None:
 710.             self._sendLine(origin, "GL", ["*", "+" + mask, str(expires - gline[4]), str(gline[4]), description])
 711.         elif self._state.getNextHop((target, None)) == self.numeric:
 712.             self._sendLine(origin, "GL", [base64.createNumeric((target, None)), "+" + mask, str(expires - gline[4]), str(gline[4]), description])
 713. 
 714.     def callbackGlineRemove(self, (origin, mask, target)):
 715.         gline = self._getGline(mask)
 716.         if self._state.getNextHop(origin) != self.numeric and target == None:
 717.             self._sendLine(origin, "GL", ["*", "-" + mask, str(gline[2] - gline[4]), str(gline[4]), gline[1]])
 718.         elif self._state.getNextHop((target, None)) == self.numeric:
 719.             self._sendLine(origin, "GL", [base64.createNumeric((target, None)), "-" + mask, str(gline[2] - gline[4]), str(gline[4]), gline[1]])
 720. 
 721.     def callbackInvite(self, (origin, target, channel)):
 722.         if self._state.getNextHop(target) == self.numeric:
 723.             self._sendLine(origin, "I", [self._state.numeric2nick(target), channel])
 724. 
 725.     def _getJupe(self, server):
 726.         for jupe in self._state.jupes():
 727.             if server == jupe[0]:
 728.                 return jupe
 729. 
 730.     def callbackJupeAdd(self, (origin, server, target, expire, reason)):
 731.         jupe = self._getJupe(server)
 732.         if self._state.getNextHop(origin) != self.numeric and target == None:
 733.             self._sendLine(origin, "JU", ["*", "+" + server, str(expire - jupe[4]), str(jupe[4]), reason])
 734.         elif self._state.getNextHop((target, None)) == self.numeric:
 735.             self._sendLine(origin, "JU", [base64.createNumeric((target, None)), "+" + server, str(expire - jupe[4]), str(jupe[4]), reason])
 736. 
 737.     def callbackJupeRemove(self, (origin, server, target)):
 738.         jupe = self._getJupe(server)
 739.         if self._state.getNextHop(origin) != self.numeric and target == None:
 740.             self._sendLine(origin, "JU", ["*", "-" + server, str(jupe[2] - jupe[4]), str(jupe[4]), jupe[1]])
 741.         elif self._state.getNextHop((target, None)) == self.numeric:
 742.             self._sendLine(origin, "JU", [base64.createNumeric((target, None)), "-" + server, str(jupe[2] - jupe[4]), str(jupe[4]), jupe[1]])
 743. 
 744.     def callbackAdminInfo(self, (origin, target)):
 745.         if self._state.getNextHop(target) == self.numeric:
 746.             self._sendLine(origin, "AD", [base64.createNumeric(target)])
 747. 
 748.     def callbackInfoRequest(self, (origin, target)):
 749.         if self._state.getNextHop(target) == self.numeric:
 750.             self._sendLine(origin, "F", [base64.createNumeric(target)])
 751. 
 752.     def callbackKick(self, (origin, channel, target, reason)):
 753.         if self._state.getNextHop(origin) != self.numeric:
 754.             self._sendLine(origin, "K", [channel, base64.createNumeric(target), reason])
 755. 
 756.     def callbackZombiePart(self, (origin, channel)):
 757.         self.callbackChannelPart((origin, channel, "Zombie parting channel"))
 758. 
 759.     def callbackChannelDestroy(self, (origin, channel, ts)):
 760.         if self._state.getNextHop(origin) != self.numeric:
 761.             self._sendLine(origin, "DE", [channel, str(ts)])
 762. 
 763.     def callbackQuit(self, (numeric, reason, causedbysquit)):
 764.         if not causedbysquit and self._state.getNextHop(numeric) != self.numeric:
 765.             self._sendLine(numeric, "Q", [reason])
 766. 
 767.     def callbackKill(self, (origin, target, path, reason)):
 768.         if self._state.getNextHop(target) == self.numeric:
 769.             self._sendLine(origin, "D", [base64.createNumeric(target), "!".join(path) + " (" + reason + ")"])
 770. 
 771.     def callbackLusers(self, (origin, target, dummy)):
 772.         if self._state.getNextHop(target) == self.numeric:
 773.             self._sendLine(origin, "LU", [dummy, base64.createNumeric(target)])
 774. 
 775.     def callbackLinks(self, (origin, target, mask)):
 776.         if self._state.getNextHop(target) == self.numeric:
 777.             self._sendLine(origin, "LI", [base64.createNumeric(target), mask])
 778. 
 779.     def callbackChangeUserMode(self, (numeric, modes)):
 780.         if self._state.getNextHop(numeric) != self.numeric:
 781.             line = [self._state.numeric2nick(numeric)]
 782.             (modestr, modeargs) = self._buildModeString(modes)
 783.             line.append(modestr)
 784.             line += modeargs
 785.             self._sendLine(numeric, "M", line)
 786. 
 787.     def callbackMOTD(self, (numeric, target)):
 788.         if self._state.getNextHop(target) == self.numeric:
 789.             self._sendLine(numeric, "MO", [base64.createNumeric(target)])
 790. 
 791.     def callbackNames(self, (origin, target, channels)):
 792.         if self._state.getNextHop(target) == self.numeric:
 793.             self._sendLine(origin, "E", [",".join(channels), base64.createNumeric(target)])
 794. 
 795.     def callbackTopic(self, (origin, channel, topic, topic_ts, channel_ts)):
 796.         if self._state.getNextHop(origin) != self.numeric:
 797.             self._sendLine(origin, "T", [channel, str(channel_ts), str(topic_ts), topic])
 798. 
 799.     def callbackSilenceAdd(self, (numeric, mask)):
 800.         if self._state.getNextHop(numeric) != self.numeric:
 801.             self._sendLine(numeric, "U", ["*", mask])
 802. 
 803.     def callbackSilenceRemove(self, (numeric, mask)):
 804.         if self._state.getNextHop(numeric) != self.numeric:
 805.             self._sendLine(numeric, "U", ["*", "-" + mask])
 806. 
 807.     def callbackRequestVersion(self, (origin, target)):
 808.         if self._state.getNextHop(target) == self.numeric:
 809.             self._sendLine(origin, "V", [base64.createNumeric(target)])
 810. 
 811.     def callbackRequestStats(self, (origin, target, stat, arg)):
 812.         if self._state.getNextHop(target) == self.numeric:
 813.             if arg != None:
 814.                 self._sendLine(origin, "R", [stat, base64.createNumeric(target), arg])
 815.             else:
 816.                 self._sendLine(origin, "R", [stat, base64.createNumeric(target)])
 817. 
 818.     def callbackTrace(self, (origin, search, target)):
 819.         if self._state.getNextHop(target) == self.numeric:
 820.             self._sendLine(origin, "TR", [search, base64.createNumeric(target)])
 821. 
 822.     def callbackPing(self, (origin, source, target)):
 823.         if target[0] == self._state.getServerID() and self._state.getNextHop(origin) == self.numeric:
 824.             self._sendLine((self._state.getServerID(), None), "Z", [base64.createNumeric((self._state.getServerID(), None)), source])
 825.         elif self._state.getNextHop(target) == self.numeric:
 826.             self._sendLine(origin, "G", [source, base64.createNumeric(target)])
 827. 
 828.     def callbackPong(self, (origin, source, target)):
 829.         if self._state.getNextHop(target) == self.numeric:
 830.             self._sendLine(origin, "Z", [base64.createNumeric(source), base64.createNumeric(target)])
 831. 
 832.     def callbackRequestWhois(self, (origin, target, search)):
 833.         if self._state.getNextHop(target) == self.numeric:
 834.             self._sendLine(origin, "W", [base64.createNumeric(target), search])
 835. 
 836.     def _multiTargetMessage(self, origin, target, type, message):
 837.         if self._state.getNextHop(target) == self.numeric:
 838.             self._sendLine(origin, type, [base64.createNumeric(target), message])
 839.         elif target[0] == "#":
 840.             if self._state.getNextHop(origin) != self.numeric:
 841.                 for user in self._state.channels[target].users():
 842.                     if self._state.getNextHop(user) == self.numeric:
 843.                         self._sendLine(origin, type, [target, message])
 844.         elif "@" in target:
 845.             target_parts = target.split("@")
 846.             if self._state.getNextHop(self._state.nick2numeric(target_parts[1])) == self.numeric:
 847.                 self._sendLine(origin, type, [target, message])
 848.         elif "$" in target:
 849.             mask = target[1:]
 850.             for server in self._state.servers:
 851.                 if self._state.getNextHop((server, None)) == self.numeric and fnmatch.fnmatch(self._state.servers[server].name, mask):
 852.                     self._sendLine(origin, type, [target, message])
 853.                     return
 854. 
 855.     def callbackPrivmsg(self, (origin, target, message)):
 856.         self._multiTargetMessage(origin, target, "P", message)
 857. 
 858.     def callbackNotice(self, (origin, target, message)):
 859.         self._multiTargetMessage(origin, target, "O", message)
 860. 
 861.     def callbackOobmsg(self, (origin, target, type, args)):
 862.         if self._state.getNextHop(target) == self.numeric:
 863.             self._sendLine(origin, type, [base64.createNumeric(target)] + args)
 864. 
 865.     def callbackWallops(self, (origin, message)):
 866.         if self._state.getNextHop(origin) != self.numeric:
 867.             self._sendLine(origin, "WA", [message])
 868. 
 869.     def callbackWallusers(self, (origin, message)):
 870.         if self._state.getNextHop(origin) != self.numeric:
 871.             self._sendLine(origin, "WU", [message])
 872. 
 873.     def callbackWallvoices(self, (origin, channel, message)):
 874.         if self._state.getNextHop(origin) != self.numeric:
 875.             for user in self._state.channels[channel].users():
 876.                 if self._state.getNextHop(user) == self.numeric and (self._state.channels[channel].isvoice(user) or self._state.channels[channel].isop(user)):
 877.                     self._sendLine(origin, "WV", [channel, message])
 878.                     return
 879. 
 880.     def callbackWallchops(self, (origin, channel, message)):
 881.         if self._state.getNextHop(origin) != self.numeric:
 882.             for user in self._state.channels[channel].users():
 883.                 if self._state.getNextHop(user) == self.numeric and self._state.channels[channel].isop(user):
 884.                     self._sendLine(origin, "WC", [channel, message])
 885.                     return
 886. 
 887. 
 888. class ConnectionError(Exception):
 889.     """ When an error occurs in a connection """
 890.     pass