You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

client.go 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. package irc
  2. import (
  3. "fmt"
  4. "net"
  5. "time"
  6. )
  7. const (
  8. IDLE_TIMEOUT = time.Minute // how long before a client is considered idle
  9. QUIT_TIMEOUT = time.Minute // how long after idle before a client is kicked
  10. )
  11. type Client struct {
  12. atime time.Time
  13. authorized bool
  14. awayMessage Text
  15. capabilities CapabilitySet
  16. capState CapState
  17. channels ChannelSet
  18. ctime time.Time
  19. flags map[UserMode]bool
  20. hasQuit bool
  21. hops uint
  22. hostname Name
  23. idleTimer *time.Timer
  24. nick Name
  25. quitTimer *time.Timer
  26. realname Text
  27. registered bool
  28. server *Server
  29. socket *Socket
  30. username Name
  31. }
  32. func NewClient(server *Server, conn net.Conn) *Client {
  33. now := time.Now()
  34. client := &Client{
  35. atime: now,
  36. authorized: server.password == nil,
  37. capState: CapNone,
  38. capabilities: make(CapabilitySet),
  39. channels: make(ChannelSet),
  40. ctime: now,
  41. flags: make(map[UserMode]bool),
  42. server: server,
  43. socket: NewSocket(conn),
  44. }
  45. client.Touch()
  46. go client.run()
  47. return client
  48. }
  49. //
  50. // command goroutine
  51. //
  52. func (client *Client) run() {
  53. var command Command
  54. var err error
  55. var line string
  56. // Set the hostname for this client. The client may later send a PROXY
  57. // command from stunnel that sets the hostname to something more accurate.
  58. client.send(NewProxyCommand(AddrLookupHostname(
  59. client.socket.conn.RemoteAddr())))
  60. for err == nil {
  61. if line, err = client.socket.Read(); err != nil {
  62. command = NewQuitCommand("connection closed")
  63. } else if command, err = ParseCommand(line); err != nil {
  64. switch err {
  65. case ErrParseCommand:
  66. client.Reply(RplNotice(client.server, client,
  67. NewText("failed to parse command")))
  68. case NotEnoughArgsError:
  69. // TODO
  70. }
  71. continue
  72. } else if checkPass, ok := command.(checkPasswordCommand); ok {
  73. checkPass.LoadPassword(client.server)
  74. // Block the client thread while handling a potentially expensive
  75. // password bcrypt operation. Since the server is single-threaded
  76. // for commands, we don't want the server to perform the bcrypt,
  77. // blocking anyone else from sending commands until it
  78. // completes. This could be a form of DoS if handled naively.
  79. checkPass.CheckPassword()
  80. }
  81. client.send(command)
  82. }
  83. }
  84. func (client *Client) send(command Command) {
  85. command.SetClient(client)
  86. client.server.commands <- command
  87. }
  88. // quit timer goroutine
  89. func (client *Client) connectionTimeout() {
  90. client.send(NewQuitCommand("connection timeout"))
  91. }
  92. //
  93. // idle timer goroutine
  94. //
  95. func (client *Client) connectionIdle() {
  96. client.server.idle <- client
  97. }
  98. //
  99. // server goroutine
  100. //
  101. func (client *Client) Active() {
  102. client.atime = time.Now()
  103. }
  104. func (client *Client) Touch() {
  105. if client.quitTimer != nil {
  106. client.quitTimer.Stop()
  107. }
  108. if client.idleTimer == nil {
  109. client.idleTimer = time.AfterFunc(IDLE_TIMEOUT, client.connectionIdle)
  110. } else {
  111. client.idleTimer.Reset(IDLE_TIMEOUT)
  112. }
  113. }
  114. func (client *Client) Idle() {
  115. client.Reply(RplPing(client.server))
  116. if client.quitTimer == nil {
  117. client.quitTimer = time.AfterFunc(QUIT_TIMEOUT, client.connectionTimeout)
  118. } else {
  119. client.quitTimer.Reset(QUIT_TIMEOUT)
  120. }
  121. }
  122. func (client *Client) Register() {
  123. if client.registered {
  124. return
  125. }
  126. client.registered = true
  127. client.Touch()
  128. }
  129. func (client *Client) destroy() {
  130. // clean up channels
  131. for channel := range client.channels {
  132. channel.Quit(client)
  133. }
  134. // clean up server
  135. client.server.clients.Remove(client)
  136. // clean up self
  137. if client.idleTimer != nil {
  138. client.idleTimer.Stop()
  139. }
  140. if client.quitTimer != nil {
  141. client.quitTimer.Stop()
  142. }
  143. client.socket.Close()
  144. Log.debug.Printf("%s: destroyed", client)
  145. }
  146. func (client *Client) IdleTime() time.Duration {
  147. return time.Since(client.atime)
  148. }
  149. func (client *Client) SignonTime() int64 {
  150. return client.ctime.Unix()
  151. }
  152. func (client *Client) IdleSeconds() uint64 {
  153. return uint64(client.IdleTime().Seconds())
  154. }
  155. func (client *Client) HasNick() bool {
  156. return client.nick != ""
  157. }
  158. func (client *Client) HasUsername() bool {
  159. return client.username != ""
  160. }
  161. // <mode>
  162. func (c *Client) ModeString() (str string) {
  163. for flag := range c.flags {
  164. str += flag.String()
  165. }
  166. if len(str) > 0 {
  167. str = "+" + str
  168. }
  169. return
  170. }
  171. func (c *Client) UserHost() Name {
  172. username := "*"
  173. if c.HasUsername() {
  174. username = c.username.String()
  175. }
  176. return Name(fmt.Sprintf("%s!%s@%s", c.Nick(), username, c.hostname))
  177. }
  178. func (c *Client) Nick() Name {
  179. if c.HasNick() {
  180. return c.nick
  181. }
  182. return Name("*")
  183. }
  184. func (c *Client) Id() Name {
  185. return c.UserHost()
  186. }
  187. func (c *Client) String() string {
  188. return c.Id().String()
  189. }
  190. func (client *Client) Friends() ClientSet {
  191. friends := make(ClientSet)
  192. friends.Add(client)
  193. for channel := range client.channels {
  194. for member := range channel.members {
  195. friends.Add(member)
  196. }
  197. }
  198. return friends
  199. }
  200. func (client *Client) SetNickname(nickname Name) {
  201. if client.HasNick() {
  202. Log.error.Printf("%s nickname already set!", client)
  203. return
  204. }
  205. client.nick = nickname
  206. client.server.clients.Add(client)
  207. }
  208. func (client *Client) ChangeNickname(nickname Name) {
  209. // Make reply before changing nick to capture original source id.
  210. reply := RplNick(client, nickname)
  211. client.server.clients.Remove(client)
  212. client.server.whoWas.Append(client)
  213. client.nick = nickname
  214. client.server.clients.Add(client)
  215. for friend := range client.Friends() {
  216. friend.Reply(reply)
  217. }
  218. }
  219. func (client *Client) Reply(reply string) error {
  220. return client.socket.Write(reply)
  221. }
  222. func (client *Client) Quit(message Text) {
  223. if client.hasQuit {
  224. return
  225. }
  226. client.hasQuit = true
  227. client.Reply(RplError("quit"))
  228. client.server.whoWas.Append(client)
  229. friends := client.Friends()
  230. friends.Remove(client)
  231. client.destroy()
  232. if len(friends) > 0 {
  233. reply := RplQuit(client, message)
  234. for friend := range friends {
  235. friend.Reply(reply)
  236. }
  237. }
  238. }