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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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 !exiting {
  40. select {
  41. case line = <-cs.receiveLines:
  42. if line != "" {
  43. fmt.Println("<- ", strings.TrimRight(line, "\r\n"))
  44. exiting = cs.processIncomingLine(line)
  45. }
  46. }
  47. }
  48. // empty the receiveLines queue
  49. cs.socket.Close()
  50. select {
  51. case <-cs.receiveLines:
  52. // empty
  53. default:
  54. // empty
  55. }
  56. }
  57. // RunSocketSender sends lines to the IRC socket.
  58. func (cs *ClientSocket) RunSocketSender() {
  59. var err error
  60. var line string
  61. for {
  62. line = <-cs.SendLines
  63. err = cs.socket.Write(line)
  64. fmt.Println(" ->", strings.TrimRight(line, "\r\n"))
  65. if err != nil {
  66. break
  67. }
  68. }
  69. }
  70. // RunSocketListener receives lines from the IRC socket.
  71. func (cs *ClientSocket) RunSocketListener() {
  72. var errConn error
  73. var line string
  74. for {
  75. line, errConn = cs.socket.Read()
  76. cs.receiveLines <- line
  77. if errConn != nil {
  78. break
  79. }
  80. }
  81. if !cs.socket.Closed {
  82. cs.Send(nil, "", "ERROR", "Closing connection")
  83. cs.socket.Close()
  84. }
  85. }
  86. // Send sends an IRC line to the listener.
  87. func (cs *ClientSocket) Send(tags *map[string]ircmsg.TagValue, prefix string, command string, params ...string) error {
  88. ircmsg := ircmsg.MakeMessage(tags, prefix, command, params...)
  89. line, err := ircmsg.Line()
  90. if err != nil {
  91. return err
  92. }
  93. cs.SendLines <- line
  94. return nil
  95. }
  96. // processIncomingLine splits and handles the given command line.
  97. // Returns true if client is exiting (sent a QUIT command, etc).
  98. func (cs *ClientSocket) processIncomingLine(line string) bool {
  99. msg, err := ircmsg.ParseLine(line)
  100. if err != nil {
  101. cs.Send(nil, "", "ERROR", "Your client sent a malformed line")
  102. return true
  103. }
  104. command, canBeParsed := Commands[msg.Command]
  105. if canBeParsed {
  106. return command.Run(cs.client.server, &cs.client, msg)
  107. }
  108. //TODO(dan): This is an error+disconnect purely for reasons of testing.
  109. // Later it may be downgraded to not-that-bad.
  110. cs.Send(nil, "", "ERROR", fmt.Sprintf("Your client sent a command that could not be parsed [%s]", msg.Command))
  111. return true
  112. }