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 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. package irc
  2. import (
  3. "fmt"
  4. "log"
  5. "net"
  6. "sync"
  7. "time"
  8. )
  9. type Client struct {
  10. atime time.Time
  11. away bool
  12. awayMessage string
  13. channels ChannelSet
  14. ctime time.Time
  15. destroyed bool
  16. hostname string
  17. idleTimer *time.Timer
  18. invisible bool
  19. loginTimer *time.Timer
  20. mutex *sync.Mutex
  21. nick string
  22. operator bool
  23. phase Phase
  24. quitTimer *time.Timer
  25. realname string
  26. replies chan Reply
  27. server *Server
  28. socket *Socket
  29. username string
  30. }
  31. func NewClient(server *Server, conn net.Conn) *Client {
  32. now := time.Now()
  33. client := &Client{
  34. atime: now,
  35. channels: make(ChannelSet),
  36. ctime: now,
  37. hostname: AddrLookupHostname(conn.RemoteAddr()),
  38. phase: server.InitPhase(),
  39. replies: make(chan Reply),
  40. server: server,
  41. socket: NewSocket(conn),
  42. mutex: &sync.Mutex{},
  43. }
  44. client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.Destroy)
  45. go client.readCommands()
  46. go client.writeReplies()
  47. return client
  48. }
  49. func (client *Client) Touch() {
  50. if client.IsDestroyed() {
  51. return
  52. }
  53. client.atime = time.Now()
  54. if client.quitTimer != nil {
  55. client.quitTimer.Stop()
  56. }
  57. if client.idleTimer == nil {
  58. client.idleTimer = time.AfterFunc(IDLE_TIMEOUT, client.Idle)
  59. } else {
  60. client.idleTimer.Reset(IDLE_TIMEOUT)
  61. }
  62. }
  63. func (client *Client) Idle() {
  64. if client.quitTimer == nil {
  65. client.quitTimer = time.AfterFunc(QUIT_TIMEOUT, client.ConnectionTimeout)
  66. } else {
  67. client.quitTimer.Reset(QUIT_TIMEOUT)
  68. }
  69. client.Reply(RplPing(client.server, client))
  70. }
  71. func (client *Client) ConnectionTimeout() {
  72. msg := &QuitCommand{
  73. message: "connection timeout",
  74. }
  75. msg.SetClient(client)
  76. client.server.Command(msg)
  77. }
  78. func (client *Client) ConnectionClosed() {
  79. if client.IsDestroyed() {
  80. return
  81. }
  82. msg := &QuitCommand{
  83. message: "connection closed",
  84. }
  85. msg.SetClient(client)
  86. client.server.Command(msg)
  87. }
  88. func (c *Client) readCommands() {
  89. for line := range c.socket.Read() {
  90. m, err := ParseCommand(line)
  91. if err != nil {
  92. switch err {
  93. case NotEnoughArgsError:
  94. c.Reply(ErrNeedMoreParams(c.server, line))
  95. default:
  96. c.Reply(ErrUnknownCommand(c.server, line))
  97. }
  98. continue
  99. }
  100. m.SetClient(c)
  101. c.server.Command(m)
  102. }
  103. if c.phase == Normal {
  104. c.ConnectionClosed()
  105. } else {
  106. c.Destroy()
  107. }
  108. }
  109. func (client *Client) writeReplies() {
  110. for reply := range client.replies {
  111. if client.IsDestroyed() {
  112. if DEBUG_CLIENT {
  113. log.Printf("%s ← %s dropped", client, reply)
  114. }
  115. continue
  116. }
  117. if DEBUG_CLIENT {
  118. log.Printf("%s ← %s", client, reply)
  119. }
  120. if client.socket.Write(reply.Format(client)) != nil {
  121. break
  122. }
  123. }
  124. }
  125. func (client *Client) IsDestroyed() bool {
  126. client.mutex.Lock()
  127. defer client.mutex.Unlock()
  128. return client.destroyed
  129. }
  130. func (client *Client) Destroy() {
  131. if client.IsDestroyed() {
  132. return
  133. }
  134. if DEBUG_CLIENT {
  135. log.Printf("%s destroying", client)
  136. }
  137. client.mutex.Lock()
  138. client.destroyed = true
  139. client.socket.Close()
  140. if client.idleTimer != nil {
  141. client.idleTimer.Stop()
  142. }
  143. if client.quitTimer != nil {
  144. client.quitTimer.Stop()
  145. }
  146. client.channels = make(ChannelSet) // clear channel list
  147. client.server.clients.Remove(client)
  148. client.mutex.Unlock()
  149. if DEBUG_CLIENT {
  150. log.Printf("%s destroyed", client)
  151. }
  152. }
  153. func (client *Client) Reply(replies ...Reply) {
  154. for _, reply := range replies {
  155. if client.replies == nil {
  156. if DEBUG_CLIENT {
  157. log.Printf("%s dropped %s", client, reply)
  158. }
  159. continue
  160. }
  161. client.replies <- reply
  162. }
  163. }
  164. func (client *Client) HasNick() bool {
  165. return client.nick != ""
  166. }
  167. func (client *Client) HasUsername() bool {
  168. return client.username != ""
  169. }
  170. func (client *Client) InterestedClients() ClientSet {
  171. clients := make(ClientSet)
  172. for channel := range client.channels {
  173. for member := range channel.members {
  174. clients.Add(member)
  175. }
  176. }
  177. return clients
  178. }
  179. // <mode>
  180. func (c *Client) ModeString() (str string) {
  181. if c.invisible {
  182. str += Invisible.String()
  183. }
  184. if c.operator {
  185. str += Operator.String()
  186. }
  187. if len(str) > 0 {
  188. str = "+" + str
  189. }
  190. return
  191. }
  192. func (c *Client) UserHost() string {
  193. username := "*"
  194. if c.HasUsername() {
  195. username = c.username
  196. }
  197. return fmt.Sprintf("%s!%s@%s", c.Nick(), username, c.hostname)
  198. }
  199. func (c *Client) Nick() string {
  200. if c.HasNick() {
  201. return c.nick
  202. }
  203. return "*"
  204. }
  205. func (c *Client) Id() string {
  206. return c.UserHost()
  207. }
  208. func (c *Client) String() string {
  209. return c.UserHost()
  210. }