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.

clientsocket.go 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. // Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "fmt"
  6. "net"
  7. "strings"
  8. "github.com/DanielOaks/girc-go/ircmsg"
  9. )
  10. // ClientSocket listens to a socket using the IRC protocol, processes events,
  11. // and also sends IRC lines out of that socket.
  12. type ClientSocket struct {
  13. receiveLines chan string
  14. ReceiveEvents chan Message
  15. SendLines chan string
  16. socket Socket
  17. client Client
  18. }
  19. // NewClientSocket returns a new ClientSocket.
  20. func NewClientSocket(conn net.Conn, client Client) ClientSocket {
  21. return ClientSocket{
  22. receiveLines: make(chan string),
  23. ReceiveEvents: make(chan Message),
  24. SendLines: make(chan string),
  25. socket: NewSocket(conn),
  26. client: client,
  27. }
  28. }
  29. // Start creates and starts running the necessary event loops.
  30. func (cs *ClientSocket) Start() {
  31. go cs.RunEvents()
  32. go cs.RunSocketSender()
  33. go cs.RunSocketListener()
  34. }
  35. // RunEvents handles received IRC lines and processes incoming commands.
  36. func (cs *ClientSocket) RunEvents() {
  37. var exiting bool
  38. var line string
  39. for {
  40. select {
  41. case line = <-cs.receiveLines:
  42. if line != "" {
  43. fmt.Println("<- ", strings.TrimRight(line, "\r\n"))
  44. exiting = cs.processIncomingLine(line)
  45. if exiting {
  46. cs.socket.Close()
  47. break
  48. }
  49. }
  50. }
  51. }
  52. // empty the receiveLines queue
  53. select {
  54. case <-cs.receiveLines:
  55. // empty
  56. default:
  57. // empty
  58. }
  59. }
  60. // RunSocketSender sends lines to the IRC socket.
  61. func (cs *ClientSocket) RunSocketSender() {
  62. var err error
  63. var line string
  64. for {
  65. line = <-cs.SendLines
  66. err = cs.socket.Write(line)
  67. fmt.Println(" ->", strings.TrimRight(line, "\r\n"))
  68. if err != nil {
  69. break
  70. }
  71. }
  72. }
  73. // RunSocketListener receives lines from the IRC socket.
  74. func (cs *ClientSocket) RunSocketListener() {
  75. var errConn error
  76. var line string
  77. for {
  78. line, errConn = cs.socket.Read()
  79. cs.receiveLines <- line
  80. if errConn != nil {
  81. break
  82. }
  83. }
  84. if !cs.socket.Closed {
  85. cs.Send(nil, "", "ERROR", "Closing connection")
  86. cs.socket.Close()
  87. }
  88. }
  89. // Send sends an IRC line to the listener.
  90. func (cs *ClientSocket) Send(tags *map[string]ircmsg.TagValue, prefix string, command string, params ...string) error {
  91. ircmsg := ircmsg.MakeMessage(tags, prefix, command, params...)
  92. line, err := ircmsg.Line()
  93. if err != nil {
  94. return err
  95. }
  96. cs.SendLines <- line
  97. return nil
  98. }
  99. // processIncomingLine splits and handles the given command line.
  100. // Returns true if client is exiting (sent a QUIT command, etc).
  101. func (cs *ClientSocket) processIncomingLine(line string) bool {
  102. msg, err := ircmsg.ParseLine(line)
  103. if err != nil {
  104. cs.Send(nil, "", "ERROR", "Your client sent a malformed line")
  105. return true
  106. }
  107. command, canBeParsed := Commands[msg.Command]
  108. if canBeParsed {
  109. return command.Run(cs, msg)
  110. }
  111. //TODO(dan): This is an error+disconnect purely for reasons of testing.
  112. // Later it may be downgraded to not-that-bad.
  113. cs.Send(nil, "", "ERROR", fmt.Sprintf("Your client sent a command that could not be parsed [%s]", msg.Command))
  114. return true
  115. }