2015-06-05 22:34:13 +00:00
|
|
|
package irc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2019-01-11 03:53:50 +00:00
|
|
|
"bytes"
|
2015-06-05 22:34:13 +00:00
|
|
|
"crypto/tls"
|
2017-07-04 09:28:56 +00:00
|
|
|
"crypto/x509"
|
2017-07-03 05:35:38 +00:00
|
|
|
"errors"
|
2015-06-05 22:34:13 +00:00
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2017-07-03 05:35:38 +00:00
|
|
|
var (
|
|
|
|
ErrBadProtocol = errors.New("This server does not speak IRC")
|
|
|
|
)
|
|
|
|
|
2015-06-05 22:34:13 +00:00
|
|
|
func (c *Client) Connect(address string) {
|
|
|
|
if idx := strings.Index(address, ":"); idx < 0 {
|
|
|
|
c.Host = address
|
|
|
|
|
|
|
|
if c.TLS {
|
|
|
|
address += ":6697"
|
|
|
|
} else {
|
|
|
|
address += ":6667"
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
c.Host = address[:idx]
|
|
|
|
}
|
|
|
|
c.Server = address
|
|
|
|
c.dialer = &net.Dialer{Timeout: 10 * time.Second}
|
|
|
|
|
2017-07-02 01:31:00 +00:00
|
|
|
c.connChange(false, nil)
|
2015-06-05 22:34:13 +00:00
|
|
|
go c.run()
|
|
|
|
}
|
|
|
|
|
2017-07-04 09:28:56 +00:00
|
|
|
func (c *Client) Reconnect() {
|
|
|
|
close(c.reconnect)
|
|
|
|
}
|
|
|
|
|
2015-06-05 22:34:13 +00:00
|
|
|
func (c *Client) Write(data string) {
|
|
|
|
c.out <- data + "\r\n"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) Writef(format string, a ...interface{}) {
|
|
|
|
c.out <- fmt.Sprintf(format+"\r\n", a...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) write(data string) {
|
|
|
|
c.conn.Write([]byte(data + "\r\n"))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) writef(format string, a ...interface{}) {
|
|
|
|
fmt.Fprintf(c.conn, format+"\r\n", a...)
|
|
|
|
}
|
|
|
|
|
2016-01-15 07:26:06 +00:00
|
|
|
func (c *Client) run() {
|
|
|
|
c.tryConnect()
|
2015-06-11 02:57:52 +00:00
|
|
|
|
2016-01-15 07:26:06 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-c.quit:
|
2018-06-17 20:53:22 +00:00
|
|
|
c.setRegistered(false)
|
2016-01-15 07:26:06 +00:00
|
|
|
if c.Connected() {
|
|
|
|
c.disconnect()
|
|
|
|
}
|
2016-01-11 20:04:57 +00:00
|
|
|
|
2016-01-15 07:26:06 +00:00
|
|
|
c.sendRecv.Wait()
|
|
|
|
close(c.Messages)
|
|
|
|
return
|
2016-01-11 20:04:57 +00:00
|
|
|
|
2016-01-15 07:26:06 +00:00
|
|
|
case <-c.reconnect:
|
2018-06-17 20:53:22 +00:00
|
|
|
c.setRegistered(false)
|
2017-07-04 09:28:56 +00:00
|
|
|
if c.Connected() {
|
|
|
|
c.disconnect()
|
|
|
|
}
|
2015-06-05 22:34:13 +00:00
|
|
|
|
2016-01-15 07:26:06 +00:00
|
|
|
c.sendRecv.Wait()
|
|
|
|
c.reconnect = make(chan struct{})
|
2015-06-05 22:34:13 +00:00
|
|
|
|
2016-01-15 07:26:06 +00:00
|
|
|
c.tryConnect()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-06-05 22:34:13 +00:00
|
|
|
|
2017-07-02 01:31:00 +00:00
|
|
|
type ConnectionState struct {
|
|
|
|
Connected bool
|
|
|
|
Error error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) connChange(connected bool, err error) {
|
|
|
|
c.ConnectionChanged <- ConnectionState{
|
|
|
|
Connected: connected,
|
|
|
|
Error: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-15 07:26:06 +00:00
|
|
|
func (c *Client) disconnect() {
|
|
|
|
c.lock.Lock()
|
|
|
|
c.connected = false
|
|
|
|
c.lock.Unlock()
|
2015-06-05 22:34:13 +00:00
|
|
|
|
2016-01-15 07:26:06 +00:00
|
|
|
c.conn.Close()
|
2015-06-05 22:34:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) tryConnect() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-c.quit:
|
|
|
|
return
|
2016-01-12 23:12:51 +00:00
|
|
|
|
2015-06-05 22:34:13 +00:00
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
err := c.connect()
|
2017-07-02 01:31:00 +00:00
|
|
|
if err != nil {
|
|
|
|
c.connChange(false, err)
|
2017-07-04 09:28:56 +00:00
|
|
|
if _, ok := err.(x509.UnknownAuthorityError); ok {
|
|
|
|
return
|
|
|
|
}
|
2017-07-02 01:31:00 +00:00
|
|
|
} else {
|
2015-06-05 22:34:13 +00:00
|
|
|
return
|
|
|
|
}
|
2016-01-12 23:12:51 +00:00
|
|
|
|
2016-01-13 00:00:57 +00:00
|
|
|
time.Sleep(c.backoff.Duration())
|
2015-06-05 22:34:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-15 07:26:06 +00:00
|
|
|
func (c *Client) connect() error {
|
|
|
|
c.lock.Lock()
|
|
|
|
defer c.lock.Unlock()
|
2015-06-05 22:34:13 +00:00
|
|
|
|
2016-01-15 07:26:06 +00:00
|
|
|
if c.TLS {
|
|
|
|
conn, err := tls.DialWithDialer(c.dialer, "tcp", c.Server, c.TLSConfig)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-01-12 23:12:51 +00:00
|
|
|
|
2016-01-15 07:26:06 +00:00
|
|
|
c.conn = conn
|
|
|
|
} else {
|
|
|
|
conn, err := c.dialer.Dial("tcp", c.Server)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2015-06-05 22:34:13 +00:00
|
|
|
}
|
2016-01-15 07:26:06 +00:00
|
|
|
|
|
|
|
c.conn = conn
|
2015-06-05 22:34:13 +00:00
|
|
|
}
|
2016-01-15 07:26:06 +00:00
|
|
|
|
|
|
|
c.connected = true
|
2017-07-02 01:31:00 +00:00
|
|
|
c.connChange(true, nil)
|
2019-01-11 03:53:50 +00:00
|
|
|
c.scan = bufio.NewScanner(c.conn)
|
|
|
|
c.scan.Buffer(c.recvBuf, cap(c.recvBuf))
|
2016-01-15 07:26:06 +00:00
|
|
|
|
|
|
|
c.register()
|
|
|
|
|
2017-07-03 05:35:38 +00:00
|
|
|
c.sendRecv.Add(1)
|
2016-01-15 07:26:06 +00:00
|
|
|
go c.recv()
|
|
|
|
|
|
|
|
return nil
|
2015-06-05 22:34:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) send() {
|
2016-01-12 23:12:51 +00:00
|
|
|
defer c.sendRecv.Done()
|
|
|
|
|
2015-06-05 22:34:13 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-c.quit:
|
|
|
|
return
|
|
|
|
|
|
|
|
case <-c.reconnect:
|
|
|
|
return
|
|
|
|
|
|
|
|
case msg := <-c.out:
|
|
|
|
_, err := c.conn.Write([]byte(msg))
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) recv() {
|
2016-01-12 23:12:51 +00:00
|
|
|
defer c.sendRecv.Done()
|
|
|
|
|
2015-06-05 22:34:13 +00:00
|
|
|
for {
|
2019-01-11 03:53:50 +00:00
|
|
|
if !c.scan.Scan() {
|
2015-06-11 00:04:51 +00:00
|
|
|
select {
|
|
|
|
case <-c.quit:
|
|
|
|
return
|
2016-01-12 23:12:51 +00:00
|
|
|
|
2015-06-11 00:04:51 +00:00
|
|
|
default:
|
2017-07-04 09:28:56 +00:00
|
|
|
c.connChange(false, nil)
|
|
|
|
c.Reconnect()
|
2015-06-11 00:04:51 +00:00
|
|
|
return
|
|
|
|
}
|
2015-06-05 22:34:13 +00:00
|
|
|
}
|
|
|
|
|
2019-01-11 03:53:50 +00:00
|
|
|
b := bytes.Trim(c.scan.Bytes(), " ")
|
|
|
|
if len(b) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
msg := ParseMessage(string(b))
|
2017-07-03 05:35:38 +00:00
|
|
|
if msg == nil {
|
|
|
|
close(c.quit)
|
|
|
|
c.connChange(false, ErrBadProtocol)
|
|
|
|
return
|
|
|
|
}
|
2015-06-05 22:34:13 +00:00
|
|
|
|
|
|
|
switch msg.Command {
|
|
|
|
case Ping:
|
2016-12-16 23:11:44 +00:00
|
|
|
go c.write("PONG :" + msg.LastParam())
|
2015-06-05 22:34:13 +00:00
|
|
|
|
2016-02-03 20:12:32 +00:00
|
|
|
case Join:
|
2019-01-13 04:10:11 +00:00
|
|
|
if c.EqualFold(msg.Nick, c.GetNick()) {
|
2016-02-03 20:12:32 +00:00
|
|
|
c.addChannel(msg.Params[0])
|
|
|
|
}
|
|
|
|
|
2017-04-11 04:04:59 +00:00
|
|
|
case Nick:
|
2019-01-13 04:10:11 +00:00
|
|
|
if c.EqualFold(msg.Nick, c.GetNick()) {
|
2017-04-11 04:04:59 +00:00
|
|
|
c.setNick(msg.LastParam())
|
|
|
|
}
|
|
|
|
|
2015-06-05 22:34:13 +00:00
|
|
|
case ReplyWelcome:
|
2017-04-12 23:50:58 +00:00
|
|
|
c.setNick(msg.Params[0])
|
2018-06-17 20:53:22 +00:00
|
|
|
c.setRegistered(true)
|
2019-01-11 04:00:15 +00:00
|
|
|
c.flushChannels()
|
|
|
|
|
|
|
|
c.backoff.Reset()
|
2017-07-03 05:35:38 +00:00
|
|
|
c.sendRecv.Add(1)
|
|
|
|
go c.send()
|
2017-04-06 21:26:58 +00:00
|
|
|
|
2018-04-28 18:31:44 +00:00
|
|
|
case ReplyISupport:
|
|
|
|
c.Support.parse(msg.Params)
|
|
|
|
|
2017-04-06 21:31:11 +00:00
|
|
|
case ErrNicknameInUse:
|
2017-04-06 21:26:58 +00:00
|
|
|
if c.HandleNickInUse != nil {
|
2017-04-12 23:50:58 +00:00
|
|
|
go c.writeNick(c.HandleNickInUse(msg.Params[1]))
|
2017-04-06 21:26:58 +00:00
|
|
|
}
|
2015-06-05 22:34:13 +00:00
|
|
|
}
|
2017-04-11 04:04:59 +00:00
|
|
|
|
|
|
|
c.Messages <- msg
|
2015-06-05 22:34:13 +00:00
|
|
|
}
|
|
|
|
}
|