Parse ident and host, rename irc.Message.Nick to Sender

This commit is contained in:
Ken-Håvard Lieng 2020-05-24 11:09:59 +02:00
parent 8829793290
commit e76beca4a0
10 changed files with 95 additions and 83 deletions

View File

@ -30,7 +30,7 @@ type Config struct {
} }
type Client struct { type Client struct {
Config Config Config *Config
Messages chan *Message Messages chan *Message
ConnectionChanged chan ConnectionState ConnectionChanged chan ConnectionState
@ -57,7 +57,7 @@ type Client struct {
lock sync.Mutex lock sync.Mutex
} }
func NewClient(config Config) *Client { func NewClient(config *Config) *Client {
if config.Port == "" { if config.Port == "" {
if config.TLS { if config.TLS {
config.Port = "6697" config.Port = "6697"
@ -74,11 +74,18 @@ func NewClient(config Config) *Client {
config.Realname = config.Nick config.Realname = config.Nick
} }
c := Client{ wantedCapabilities := append([]string{}, clientWantedCaps...)
if config.SASL != nil {
wantedCapabilities = append(wantedCapabilities, "sasl")
}
return &Client{
Config: config, Config: config,
nick: config.Nick, nick: config.Nick,
Features: NewFeatures(), Features: NewFeatures(),
Messages: make(chan *Message, 32), Messages: make(chan *Message, 32),
wantedCapabilities: wantedCapabilities,
requestedCapabilities: map[string][]string{}, requestedCapabilities: map[string][]string{},
enabledCapabilities: map[string][]string{}, enabledCapabilities: map[string][]string{},
ConnectionChanged: make(chan ConnectionState, 4), ConnectionChanged: make(chan ConnectionState, 4),
@ -93,14 +100,6 @@ func NewClient(config Config) *Client {
Jitter: true, Jitter: true,
}, },
} }
c.wantedCapabilities = append(c.wantedCapabilities, clientWantedCaps...)
if config.SASL != nil {
c.wantedCapabilities = append(c.wantedCapabilities, "sasl")
}
return &c
} }
func (c *Client) GetNick() string { func (c *Client) GetNick() string {

View File

@ -8,7 +8,7 @@ import (
) )
func testClientSend() (*Client, chan string) { func testClientSend() (*Client, chan string) {
c := NewClient(Config{}) c := NewClient(&Config{})
conn := &mockConn{hook: make(chan string, 16)} conn := &mockConn{hook: make(chan string, 16)}
c.conn = conn c.conn = conn
c.sendRecv.Add(1) c.sendRecv.Add(1)
@ -147,7 +147,6 @@ func TestRegister(t *testing.T) {
c.Config.Nick = "nick" c.Config.Nick = "nick"
c.Config.Username = "user" c.Config.Username = "user"
c.Config.Realname = "rn" c.Config.Realname = "rn"
t.Log(c.Config)
c.register() c.register()
assert.Equal(t, "CAP LS 302\r\n", <-out) assert.Equal(t, "CAP LS 302\r\n", <-out)
assert.Equal(t, "NICK nick\r\n", <-out) assert.Equal(t, "NICK nick\r\n", <-out)

View File

@ -200,12 +200,12 @@ func (c *Client) recv() {
go c.write("PONG :" + msg.LastParam()) go c.write("PONG :" + msg.LastParam())
case JOIN: case JOIN:
if c.EqualFold(msg.Nick, c.GetNick()) { if c.Is(msg.Sender) {
c.addChannel(msg.Params[0]) c.addChannel(msg.Params[0])
} }
case NICK: case NICK:
if c.EqualFold(msg.Nick, c.GetNick()) { if c.Is(msg.Sender) {
c.setNick(msg.LastParam()) c.setNick(msg.LastParam())
} }

View File

@ -78,7 +78,7 @@ func (i *mockIrcd) handle(conn net.Conn) {
} }
func TestConnect(t *testing.T) { func TestConnect(t *testing.T) {
c := NewClient(Config{ c := NewClient(&Config{
Host: "127.0.0.1", Host: "127.0.0.1",
Port: "45678", Port: "45678",
}) })
@ -87,7 +87,7 @@ func TestConnect(t *testing.T) {
} }
func TestConnectTLS(t *testing.T) { func TestConnectTLS(t *testing.T) {
c := NewClient(Config{ c := NewClient(&Config{
Host: "127.0.0.1", Host: "127.0.0.1",
Port: "45679", Port: "45679",
TLS: true, TLS: true,
@ -100,12 +100,12 @@ func TestConnectTLS(t *testing.T) {
} }
func TestConnectDefaultPorts(t *testing.T) { func TestConnectDefaultPorts(t *testing.T) {
c := NewClient(Config{ c := NewClient(&Config{
Host: "127.0.0.1", Host: "127.0.0.1",
}) })
assert.Equal(t, "6667", c.Config.Port) assert.Equal(t, "6667", c.Config.Port)
c = NewClient(Config{ c = NewClient(&Config{
Host: "127.0.0.1", Host: "127.0.0.1",
TLS: true, TLS: true,
}) })
@ -125,7 +125,7 @@ func TestWrite(t *testing.T) {
} }
func TestRecv(t *testing.T) { func TestRecv(t *testing.T) {
c := NewClient(Config{}) c := NewClient(&Config{})
conn := &mockConn{hook: make(chan string, 16)} conn := &mockConn{hook: make(chan string, 16)}
c.conn = conn c.conn = conn
@ -145,7 +145,7 @@ func TestRecv(t *testing.T) {
} }
func TestRecvTriggersReconnect(t *testing.T) { func TestRecvTriggersReconnect(t *testing.T) {
c := NewClient(Config{}) c := NewClient(&Config{})
c.conn = &mockConn{} c.conn = &mockConn{}
c.scan = bufio.NewScanner(bytes.NewBufferString("001 bob\r\n")) c.scan = bufio.NewScanner(bytes.NewBufferString("001 bob\r\n"))
done := make(chan struct{}) done := make(chan struct{})
@ -168,7 +168,7 @@ func TestRecvTriggersReconnect(t *testing.T) {
} }
func TestClose(t *testing.T) { func TestClose(t *testing.T) {
c := NewClient(Config{}) c := NewClient(&Config{})
close(c.quit) close(c.quit)
ok := false ok := false
done := make(chan struct{}) done := make(chan struct{})

View File

@ -45,25 +45,25 @@ func EncodeCTCP(ctcp *CTCP) string {
func (c *Client) handleCTCP(ctcp *CTCP, msg *Message) { func (c *Client) handleCTCP(ctcp *CTCP, msg *Message) {
switch ctcp.Command { switch ctcp.Command {
case "CLIENTINFO": case "CLIENTINFO":
c.ReplyCTCP(msg.Nick, ctcp.Command, ClientInfo) c.ReplyCTCP(msg.Sender, ctcp.Command, ClientInfo)
case "FINGER", "VERSION": case "FINGER", "VERSION":
if c.Config.Version != "" { if c.Config.Version != "" {
c.ReplyCTCP(msg.Nick, ctcp.Command, c.Config.Version) c.ReplyCTCP(msg.Sender, ctcp.Command, c.Config.Version)
} }
case "PING": case "PING":
c.ReplyCTCP(msg.Nick, ctcp.Command, ctcp.Params) c.ReplyCTCP(msg.Sender, ctcp.Command, ctcp.Params)
case "SOURCE": case "SOURCE":
if c.Config.Source != "" { if c.Config.Source != "" {
c.ReplyCTCP(msg.Nick, ctcp.Command, c.Config.Source) c.ReplyCTCP(msg.Sender, ctcp.Command, c.Config.Source)
} }
case "TIME": case "TIME":
c.ReplyCTCP(msg.Nick, ctcp.Command, time.Now().UTC().Format(time.RFC3339)) c.ReplyCTCP(msg.Sender, ctcp.Command, time.Now().UTC().Format(time.RFC3339))
case "USERINFO": case "USERINFO":
c.ReplyCTCP(msg.Nick, ctcp.Command, fmt.Sprintf("%s (%s)", c.GetNick(), c.Config.Realname)) c.ReplyCTCP(msg.Sender, ctcp.Command, fmt.Sprintf("%s (%s)", c.GetNick(), c.Config.Realname))
} }
} }

View File

@ -6,8 +6,9 @@ import (
type Message struct { type Message struct {
Tags map[string]string Tags map[string]string
Prefix string Sender string
Nick string Ident string
Host string
Command string Command string
Params []string Params []string
} }
@ -20,7 +21,7 @@ func (m *Message) LastParam() string {
} }
func (m *Message) IsFromServer() bool { func (m *Message) IsFromServer() bool {
return m.Nick == "" || strings.Contains(m.Nick, ".") return m.Sender == "" || strings.Contains(m.Sender, ".")
} }
func (m *Message) ToCTCP() *CTCP { func (m *Message) ToCTCP() *CTCP {
@ -65,14 +66,23 @@ func ParseMessage(line string) *Message {
if next == -1 { if next == -1 {
return nil return nil
} }
msg.Prefix = line[1:next] prefix := line[1:next]
if i := strings.Index(msg.Prefix, "!"); i > 0 { if i := strings.Index(prefix, "!"); i > 0 {
msg.Nick = msg.Prefix[:i] msg.Sender = prefix[:i]
} else if i := strings.Index(msg.Prefix, "@"); i > 0 { prefix = prefix[i+1:]
msg.Nick = msg.Prefix[:i]
if i = strings.Index(prefix, "@"); i > 0 {
msg.Ident = prefix[:i]
msg.Host = prefix[i+1:]
} else { } else {
msg.Nick = msg.Prefix msg.Ident = prefix
}
} else if i = strings.Index(prefix, "@"); i > 0 {
msg.Sender = prefix[:i]
msg.Host = prefix[i+1:]
} else {
msg.Sender = prefix
} }
line = line[next+1:] line = line[next+1:]

View File

@ -14,16 +14,16 @@ func TestParseMessage(t *testing.T) {
{ {
":user CMD #chan :some message", ":user CMD #chan :some message",
&Message{ &Message{
Prefix: "user", Sender: "user",
Nick: "user",
Command: "CMD", Command: "CMD",
Params: []string{"#chan", "some message"}, Params: []string{"#chan", "some message"},
}, },
}, { }, {
":nick!user@host.com CMD a b", ":nick!user@host.com CMD a b",
&Message{ &Message{
Prefix: "nick!user@host.com", Sender: "nick",
Nick: "nick", Ident: "user",
Host: "host.com",
Command: "CMD", Command: "CMD",
Params: []string{"a", "b"}, Params: []string{"a", "b"},
}, },
@ -53,15 +53,16 @@ func TestParseMessage(t *testing.T) {
}, { }, {
":nick@host.com CMD", ":nick@host.com CMD",
&Message{ &Message{
Prefix: "nick@host.com", Sender: "nick",
Nick: "nick", Host: "host.com",
Command: "CMD", Command: "CMD",
}, },
}, { }, {
":ni@ck!user!name@host!.com CMD", ":ni@ck!user!name@host!.com CMD",
&Message{ &Message{
Prefix: "ni@ck!user!name@host!.com", Sender: "ni@ck",
Nick: "ni@ck", Ident: "user!name",
Host: "host!.com",
Command: "CMD", Command: "CMD",
}, },
}, { }, {
@ -114,8 +115,9 @@ func TestParseMessage(t *testing.T) {
Tags: map[string]string{ Tags: map[string]string{
"x": "y", "x": "y",
}, },
Prefix: "nick!user@host.com", Sender: "nick",
Nick: "nick", Ident: "user",
Host: "host.com",
Command: "CMD", Command: "CMD",
}, },
}, { }, {
@ -124,8 +126,9 @@ func TestParseMessage(t *testing.T) {
Tags: map[string]string{ Tags: map[string]string{
"x": "y", "x": "y",
}, },
Prefix: "nick!user@host.com", Sender: "nick",
Nick: "nick", Ident: "user",
Host: "host.com",
Command: "CMD", Command: "CMD",
Params: []string{"pie and cake"}, Params: []string{"pie and cake"},
}, },
@ -135,8 +138,9 @@ func TestParseMessage(t *testing.T) {
Tags: map[string]string{ Tags: map[string]string{
"x": "y", "x": "y",
}, },
Prefix: "nick!user@host.com", Sender: "nick",
Nick: "nick", Ident: "user",
Host: "host.com",
Command: "CMD", Command: "CMD",
Params: []string{"beans", "rainbows", "pie and cake"}, Params: []string{"beans", "rainbows", "pie and cake"},
}, },

View File

@ -72,7 +72,7 @@ func connectIRC(server *storage.Server, state *State, srcIP []byte) *irc.Client
ircCfg.Password = server.ServerPassword ircCfg.Password = server.ServerPassword
} }
i := irc.NewClient(ircCfg) i := irc.NewClient(&ircCfg)
i.Config.HandleNickInUse = createNickInUseHandler(i, state) i.Config.HandleNickInUse = createNickInUseHandler(i, state)
state.setIRC(server.Host, i) state.setIRC(server.Host, i)

View File

@ -113,11 +113,11 @@ func (i *ircHandler) dispatchMessage(msg *irc.Message) {
func (i *ircHandler) nick(msg *irc.Message) { func (i *ircHandler) nick(msg *irc.Message) {
i.state.sendJSON("nick", Nick{ i.state.sendJSON("nick", Nick{
Server: i.client.Host(), Server: i.client.Host(),
Old: msg.Nick, Old: msg.Sender,
New: msg.LastParam(), New: msg.LastParam(),
}) })
channelStore.RenameUser(msg.Nick, msg.LastParam(), i.client.Host()) channelStore.RenameUser(msg.Sender, msg.LastParam(), i.client.Host())
if i.client.Is(msg.LastParam()) { if i.client.Is(msg.LastParam()) {
go i.state.user.SetNick(msg.LastParam(), i.client.Host()) go i.state.user.SetNick(msg.LastParam(), i.client.Host())
@ -127,14 +127,14 @@ func (i *ircHandler) nick(msg *irc.Message) {
func (i *ircHandler) join(msg *irc.Message) { func (i *ircHandler) join(msg *irc.Message) {
i.state.sendJSON("join", Join{ i.state.sendJSON("join", Join{
Server: i.client.Host(), Server: i.client.Host(),
User: msg.Nick, User: msg.Sender,
Channels: msg.Params, Channels: msg.Params,
}) })
channel := msg.Params[0] channel := msg.Params[0]
channelStore.AddUser(msg.Nick, i.client.Host(), channel) channelStore.AddUser(msg.Sender, i.client.Host(), channel)
if i.client.Is(msg.Nick) { if i.client.Is(msg.Sender) {
// In case no topic is set and there's a cached one that needs to be cleared // In case no topic is set and there's a cached one that needs to be cleared
i.client.Topic(channel) i.client.Topic(channel)
@ -150,7 +150,7 @@ func (i *ircHandler) join(msg *irc.Message) {
func (i *ircHandler) part(msg *irc.Message) { func (i *ircHandler) part(msg *irc.Message) {
part := Part{ part := Part{
Server: i.client.Host(), Server: i.client.Host(),
User: msg.Nick, User: msg.Sender,
Channel: msg.Params[0], Channel: msg.Params[0],
} }
@ -160,9 +160,9 @@ func (i *ircHandler) part(msg *irc.Message) {
i.state.sendJSON("part", part) i.state.sendJSON("part", part)
channelStore.RemoveUser(msg.Nick, i.client.Host(), part.Channel) channelStore.RemoveUser(msg.Sender, i.client.Host(), part.Channel)
if i.client.Is(msg.Nick) { if i.client.Is(msg.Sender) {
go i.state.user.RemoveChannel(i.client.Host(), part.Channel) go i.state.user.RemoveChannel(i.client.Host(), part.Channel)
} }
} }
@ -196,7 +196,7 @@ func (i *ircHandler) message(msg *irc.Message) {
message := Message{ message := Message{
ID: betterguid.New(), ID: betterguid.New(),
Server: i.client.Host(), Server: i.client.Host(),
From: msg.Nick, From: msg.Sender,
Content: msg.LastParam(), Content: msg.LastParam(),
} }
target := msg.Params[0] target := msg.Params[0]
@ -216,18 +216,18 @@ func (i *ircHandler) message(msg *irc.Message) {
if target != "*" && !msg.IsFromServer() { if target != "*" && !msg.IsFromServer() {
go i.state.user.LogMessage(message.ID, go i.state.user.LogMessage(message.ID,
i.client.Host(), msg.Nick, target, msg.LastParam()) i.client.Host(), msg.Sender, target, msg.LastParam())
} }
} }
func (i *ircHandler) quit(msg *irc.Message) { func (i *ircHandler) quit(msg *irc.Message) {
i.state.sendJSON("quit", Quit{ i.state.sendJSON("quit", Quit{
Server: i.client.Host(), Server: i.client.Host(),
User: msg.Nick, User: msg.Sender,
Reason: msg.LastParam(), Reason: msg.LastParam(),
}) })
channelStore.RemoveUserAll(msg.Nick, i.client.Host()) channelStore.RemoveUserAll(msg.Sender, i.client.Host())
} }
func (i *ircHandler) info(msg *irc.Message) { func (i *ircHandler) info(msg *irc.Message) {
@ -248,7 +248,7 @@ func (i *ircHandler) info(msg *irc.Message) {
i.state.sendJSON("pm", Message{ i.state.sendJSON("pm", Message{
Server: i.client.Host(), Server: i.client.Host(),
From: msg.Nick, From: msg.Sender,
Content: strings.Join(msg.Params[1:], " "), Content: strings.Join(msg.Params[1:], " "),
}) })
} }
@ -297,7 +297,7 @@ func (i *ircHandler) topic(msg *irc.Message) {
if msg.Command == irc.TOPIC { if msg.Command == irc.TOPIC {
channel = msg.Params[0] channel = msg.Params[0]
nick = msg.Nick nick = msg.Sender
} else { } else {
channel = msg.Params[1] channel = msg.Params[1]
} }
@ -425,7 +425,7 @@ func (i *ircHandler) receiveDCCSend(pack *irc.DCCSend, msg *irc.Message) {
i.state.sendJSON("dcc_send", DCCSend{ i.state.sendJSON("dcc_send", DCCSend{
Server: i.client.Host(), Server: i.client.Host(),
From: msg.Nick, From: msg.Sender,
Filename: pack.File, Filename: pack.File,
URL: fmt.Sprintf("%s://%s/downloads/%s/%s", URL: fmt.Sprintf("%s://%s/downloads/%s/%s",
i.state.String("scheme"), i.state.String("host"), i.state.user.Username, pack.File), i.state.String("scheme"), i.state.String("host"), i.state.user.Username, pack.File),
@ -539,5 +539,5 @@ func formatIRCError(msg *irc.Message) string {
} }
func printMessage(msg *irc.Message, i *irc.Client) { func printMessage(msg *irc.Message, i *irc.Client) {
log.Println(i.GetNick()+":", msg.Prefix, msg.Command, msg.Params) log.Println(i.GetNick()+":", msg.Sender, msg.Command, msg.Params)
} }

View File

@ -54,7 +54,7 @@ func dispatchMessage(msg *irc.Message) WSResponse {
} }
func dispatchMessageMulti(msg *irc.Message) chan WSResponse { func dispatchMessageMulti(msg *irc.Message) chan WSResponse {
c := irc.NewClient(irc.Config{ c := irc.NewClient(&irc.Config{
Nick: "nick", Nick: "nick",
Username: "user", Username: "user",
Host: "host.com", Host: "host.com",
@ -74,7 +74,7 @@ func checkResponse(t *testing.T, expectedType string, expectedData interface{},
func TestHandleIRCNick(t *testing.T) { func TestHandleIRCNick(t *testing.T) {
res := dispatchMessage(&irc.Message{ res := dispatchMessage(&irc.Message{
Command: irc.NICK, Command: irc.NICK,
Nick: "old", Sender: "old",
Params: []string{"new"}, Params: []string{"new"},
}) })
@ -88,7 +88,7 @@ func TestHandleIRCNick(t *testing.T) {
func TestHandleIRCJoin(t *testing.T) { func TestHandleIRCJoin(t *testing.T) {
res := dispatchMessage(&irc.Message{ res := dispatchMessage(&irc.Message{
Command: irc.JOIN, Command: irc.JOIN,
Nick: "joining", Sender: "joining",
Params: []string{"#chan"}, Params: []string{"#chan"},
}) })
@ -102,7 +102,7 @@ func TestHandleIRCJoin(t *testing.T) {
func TestHandleIRCPart(t *testing.T) { func TestHandleIRCPart(t *testing.T) {
res := dispatchMessage(&irc.Message{ res := dispatchMessage(&irc.Message{
Command: irc.PART, Command: irc.PART,
Nick: "parting", Sender: "parting",
Params: []string{"#chan", "the reason"}, Params: []string{"#chan", "the reason"},
}) })
@ -115,7 +115,7 @@ func TestHandleIRCPart(t *testing.T) {
res = dispatchMessage(&irc.Message{ res = dispatchMessage(&irc.Message{
Command: irc.PART, Command: irc.PART,
Nick: "parting", Sender: "parting",
Params: []string{"#chan"}, Params: []string{"#chan"},
}) })
@ -144,7 +144,7 @@ func TestHandleIRCMode(t *testing.T) {
func TestHandleIRCMessage(t *testing.T) { func TestHandleIRCMessage(t *testing.T) {
res := dispatchMessage(&irc.Message{ res := dispatchMessage(&irc.Message{
Command: irc.PRIVMSG, Command: irc.PRIVMSG,
Nick: "nick", Sender: "nick",
Params: []string{"#chan", "the message"}, Params: []string{"#chan", "the message"},
}) })
@ -157,7 +157,7 @@ func TestHandleIRCMessage(t *testing.T) {
res = dispatchMessage(&irc.Message{ res = dispatchMessage(&irc.Message{
Command: irc.PRIVMSG, Command: irc.PRIVMSG,
Nick: "someone", Sender: "someone",
Params: []string{"nick", "the message"}, Params: []string{"nick", "the message"},
}) })
@ -172,7 +172,7 @@ func TestHandleIRCMessage(t *testing.T) {
func TestHandleIRCQuit(t *testing.T) { func TestHandleIRCQuit(t *testing.T) {
res := dispatchMessage(&irc.Message{ res := dispatchMessage(&irc.Message{
Command: irc.QUIT, Command: irc.QUIT,
Nick: "nick", Sender: "nick",
Params: []string{"the reason"}, Params: []string{"the reason"},
}) })
@ -186,7 +186,7 @@ func TestHandleIRCQuit(t *testing.T) {
func TestHandleIRCWelcome(t *testing.T) { func TestHandleIRCWelcome(t *testing.T) {
res := dispatchMessageMulti(&irc.Message{ res := dispatchMessageMulti(&irc.Message{
Command: irc.RPL_WELCOME, Command: irc.RPL_WELCOME,
Nick: "nick", Sender: "nick",
Params: []string{"nick", "some", "text"}, Params: []string{"nick", "some", "text"},
}) })
@ -203,7 +203,7 @@ func TestHandleIRCWelcome(t *testing.T) {
} }
func TestHandleIRCWhois(t *testing.T) { func TestHandleIRCWhois(t *testing.T) {
c := irc.NewClient(irc.Config{ c := irc.NewClient(&irc.Config{
Nick: "nick", Nick: "nick",
Username: "user", Username: "user",
Host: "host.com", Host: "host.com",
@ -250,7 +250,7 @@ func TestHandleIRCTopic(t *testing.T) {
res = dispatchMessage(&irc.Message{ res = dispatchMessage(&irc.Message{
Command: irc.TOPIC, Command: irc.TOPIC,
Params: []string{"#chan", "the topic"}, Params: []string{"#chan", "the topic"},
Nick: "bob", Sender: "bob",
}) })
checkResponse(t, "topic", Topic{ checkResponse(t, "topic", Topic{
@ -274,7 +274,7 @@ func TestHandleIRCNoTopic(t *testing.T) {
} }
func TestHandleIRCNames(t *testing.T) { func TestHandleIRCNames(t *testing.T) {
c := irc.NewClient(irc.Config{ c := irc.NewClient(&irc.Config{
Nick: "nick", Nick: "nick",
Username: "user", Username: "user",
Host: "host.com", Host: "host.com",
@ -303,7 +303,7 @@ func TestHandleIRCNames(t *testing.T) {
} }
func TestHandleIRCMotd(t *testing.T) { func TestHandleIRCMotd(t *testing.T) {
c := irc.NewClient(irc.Config{ c := irc.NewClient(&irc.Config{
Nick: "nick", Nick: "nick",
Username: "user", Username: "user",
Host: "host.com", Host: "host.com",
@ -333,7 +333,7 @@ func TestHandleIRCMotd(t *testing.T) {
} }
func TestHandleIRCBadNick(t *testing.T) { func TestHandleIRCBadNick(t *testing.T) {
c := irc.NewClient(irc.Config{ c := irc.NewClient(&irc.Config{
Nick: "nick", Nick: "nick",
Username: "user", Username: "user",
Host: "host.com", Host: "host.com",