-
Notifications
You must be signed in to change notification settings - Fork 0
/
conn.go
157 lines (130 loc) · 3.85 KB
/
conn.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package irc
import (
"bufio"
"crypto/tls"
"io"
"net"
"strings"
)
/****The SSL implementation is currently insecure. ***
Conn represents a connection to an IRC server. It provides
methods to read, write and close a connection.
MessageHandlers can be added to the Conn, and will be called anytime
a message is sent or recieved if it matches the specified criteria.
A NewConnectionWrapper method is provided to allow you to provide
your own implementation of net.Conn (e.g. for a websocket)*/
type Conn interface {
Read() (Message, error)
Write(Message) error
Close()
AddHandler(dir handlerDirection, mh MessageHandler, cmds ...string)
}
//MessageHandler are functions that will be called by a client upon
//recieving a message that matches the supplied criteria.
type MessageHandler func(Message)
//handlerDirection represents the direction a handler should be triggered on
type handlerDirection int
const (
msgHandlerKey = "*" //key for general handler (triggered on all messages)
)
//NewConnection returns a new IRC Conn object
//TODO: The SSL implementation is currently insecure.
func NewConnection(serverAddress string, useSSL bool) (Conn, error) {
var c net.Conn
var err error
if useSSL {
conf := tls.Config{ //TODO: Fix insecure SSL connection
InsecureSkipVerify: true,
}
c, err = tls.Dial("tcp", serverAddress, &conf)
} else {
c, err = net.Dial("tcp", serverAddress)
}
if err != nil {
return nil, err
}
return NewConnectionWrapper(c), nil
}
//NewConnectionWrapper provides a new IRC Conn object using
//the specified input stream. Useful for websockets or other
//connectivity methods
func NewConnectionWrapper(c io.ReadWriteCloser) Conn {
return &conn{conn: c,
scanner: bufio.NewScanner(c),
incomingHandlers: make(map[string][]MessageHandler),
outgoingHandlers: make(map[string][]MessageHandler),
}
}
//A very simple implementation of an IRC client
type conn struct {
conn io.ReadWriteCloser
scanner *bufio.Scanner
incomingHandlers map[string][]MessageHandler
outgoingHandlers map[string][]MessageHandler
}
//Read blocks until a new line is available from the server,
//It returns a new Message or returns an error
func (c *conn) Read() (msg Message, err error) {
ok := c.scanner.Scan()
if !ok {
err = c.scanner.Err()
if err == nil { //Scanner doesn't return EOF
err = io.EOF
}
return
}
line := c.scanner.Text()
msg = NewMessage(line)
for _, h := range c.incomingHandlers[msgHandlerKey] {
h(msg)
}
for _, h := range c.incomingHandlers[msg.Command()] {
h(msg)
}
return
}
//Writes the message to the server.
//Returns an error if one occurs
func (c *conn) Write(msg Message) error {
_, err := c.conn.Write([]byte(msg.String() + "\r\n"))
if err == nil {
for _, h := range c.outgoingHandlers[msgHandlerKey] {
h(msg)
}
for _, h := range c.outgoingHandlers[msg.Command()] {
h(msg)
}
}
return err
}
//Closes the connection to the server. It does not send
//a quit command.
func (c *conn) Close() {
if c != nil {
c.conn.Close()
}
}
//Adds a MessageHandler function to the client. The supplied handler
//will be called for all messages that are going in the specified direction
//(inbound, outbound or both). If commands are specified, the handler will be
//called only on those commands. If no commands are specified, the handler will
//be called for all messages, regardless of the command.
func (c *conn) AddHandler(dir handlerDirection, h MessageHandler, cmds ...string) {
if len(cmds) < 1 {
cmds = []string{msgHandlerKey}
}
if dir == Incoming || dir == Both {
for _, cmd := range cmds {
cmd = strings.ToUpper(cmd)
handlers := c.incomingHandlers[cmd]
c.incomingHandlers[cmd] = append(handlers, h)
}
}
if dir == Outgoing || dir == Both {
for _, cmd := range cmds {
cmd = strings.ToUpper(cmd)
handlers := c.outgoingHandlers[cmd]
c.outgoingHandlers[cmd] = append(handlers, h)
}
}
}