From b62f72a74619511f90c4a1ea1432075e5f91cbcb Mon Sep 17 00:00:00 2001 From: lye Date: Sun, 18 Mar 2012 16:51:13 -0500 Subject: [PATCH 01/11] Hack in SSL support --- irc.go | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/irc.go b/irc.go index 2dd0cad..44a777c 100644 --- a/irc.go +++ b/irc.go @@ -10,6 +10,7 @@ import ( "net" "strings" "time" + "crypto/tls" ) const ( @@ -166,15 +167,7 @@ func (i *IRCConnection) Loop() { <-i.syncwriter } -func (i *IRCConnection) Connect(server string) error { - i.server = server - fmt.Printf("Connecting to %s\n", i.server) - var err error - i.socket, err = net.Dial("tcp", i.server) - if err != nil { - return err - } - fmt.Printf("Connected to %s (%s)\n", i.server, i.socket.RemoteAddr()) +func (i *IRCConnection) postConnect() error { i.pread = make(chan string, 100) i.pwrite = make(chan string, 100) i.Error = make(chan error, 10) @@ -191,6 +184,30 @@ func (i *IRCConnection) Connect(server string) error { return nil } +func (i *IRCConnection) Connect(server string) error { + i.server = server + fmt.Printf("Connecting to %s\n", i.server) + var err error + i.socket, err = net.Dial("tcp", i.server) + if err != nil { + return err + } + fmt.Printf("Connected to %s (%s)\n", i.server, i.socket.RemoteAddr()) + return i.postConnect() +} + +func (i *IRCConnection) ConnectSSL(server string) error { + i.server = server + fmt.Printf("Connecting to %s over SSL\n", i.server) + var err error + i.socket, err = tls.Dial("tcp", i.server, nil) + if err != nil { + return err + } + fmt.Printf("Connected to %s (%s) over SSL\n", i.server, i.socket.RemoteAddr()) + return i.postConnect() +} + func IRC(nick, user string) *IRCConnection { irc := new(IRCConnection) irc.registered = false From 52c17040d52806a01b41e83e9f2424445b3c7a56 Mon Sep 17 00:00:00 2001 From: lye Date: Sun, 18 Mar 2012 17:08:05 -0500 Subject: [PATCH 02/11] Make the SSL config app-configurable --- irc.go | 2 +- irc_struct.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/irc.go b/irc.go index 44a777c..b629a2b 100644 --- a/irc.go +++ b/irc.go @@ -200,7 +200,7 @@ func (i *IRCConnection) ConnectSSL(server string) error { i.server = server fmt.Printf("Connecting to %s over SSL\n", i.server) var err error - i.socket, err = tls.Dial("tcp", i.server, nil) + i.socket, err = tls.Dial("tcp", i.server, i.SSLConfig) if err != nil { return err } diff --git a/irc_struct.go b/irc_struct.go index fb1b788..6ad78ad 100644 --- a/irc_struct.go +++ b/irc_struct.go @@ -7,6 +7,7 @@ package irc import ( "net" "time" + "crypto/tls" ) type IRCConnection struct { @@ -29,6 +30,8 @@ type IRCConnection struct { VerboseCallbackHandler bool quitting bool + + SSLConfig *tls.Config } type IRCEvent struct { From 07b619e8f213664b5d7f37c15f287fc176bf2a1d Mon Sep 17 00:00:00 2001 From: lye Date: Tue, 20 Mar 2012 23:08:05 -0500 Subject: [PATCH 03/11] Temporarily use my tls fixes until they're merged --- irc.go | 2 +- irc_struct.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/irc.go b/irc.go index b629a2b..e3f1a11 100644 --- a/irc.go +++ b/irc.go @@ -10,7 +10,7 @@ import ( "net" "strings" "time" - "crypto/tls" + "github.com/lye/tls" ) const ( diff --git a/irc_struct.go b/irc_struct.go index 6ad78ad..44ea0de 100644 --- a/irc_struct.go +++ b/irc_struct.go @@ -7,7 +7,7 @@ package irc import ( "net" "time" - "crypto/tls" + "github.com/lye/tls" ) type IRCConnection struct { From d544994641de67c816babeb000c486fa1c02b052 Mon Sep 17 00:00:00 2001 From: lye Date: Tue, 20 Mar 2012 23:11:35 -0500 Subject: [PATCH 04/11] rfc1459 compliance: PASS before NICK/USER --- irc.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/irc.go b/irc.go index e3f1a11..0455e06 100644 --- a/irc.go +++ b/irc.go @@ -173,14 +173,17 @@ func (i *IRCConnection) postConnect() error { i.Error = make(chan error, 10) i.syncreader = make(chan bool) i.syncwriter = make(chan bool) + go reader(i) go writer(i) go pinger(i) - i.pwrite <- fmt.Sprintf("NICK %s\r\n", i.nick) - i.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", i.user, i.user) + if len(i.Password) > 0 { i.pwrite <- fmt.Sprintf("PASS %s\r\n", i.Password) } + + i.pwrite <- fmt.Sprintf("NICK %s\r\n", i.nick) + i.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", i.user, i.user) return nil } From 5fe3fea8c0528378641872abc82dfe52b6109d76 Mon Sep 17 00:00:00 2001 From: lye Date: Wed, 21 Mar 2012 22:38:22 -0500 Subject: [PATCH 05/11] Hurrah, starting a real fork. The old codebase is too messy for my taste and doesn't provide a clean enough API. I don't like the name either, so I'm also rebranding it. I don't care. --- Makefile | 7 +++++-- irc.go => src/irc.go | 0 irc_callback.go => src/irc_callback.go | 0 irc_struct.go => src/irc_struct.go | 0 4 files changed, 5 insertions(+), 2 deletions(-) rename irc.go => src/irc.go (100%) rename irc_callback.go => src/irc_callback.go (100%) rename irc_struct.go => src/irc_struct.go (100%) diff --git a/Makefile b/Makefile index 393ab31..870adf1 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ include $(GOROOT)/src/Make.inc -TARG=irc -GOFILES=irc.go irc_struct.go irc_callback.go +TARG=github.com/lye/cleanirc +GOFILES=\ + src/irc.go \ + src/irc_struct.go \ + src/irc_callback.go include $(GOROOT)/src/Make.pkg diff --git a/irc.go b/src/irc.go similarity index 100% rename from irc.go rename to src/irc.go diff --git a/irc_callback.go b/src/irc_callback.go similarity index 100% rename from irc_callback.go rename to src/irc_callback.go diff --git a/irc_struct.go b/src/irc_struct.go similarity index 100% rename from irc_struct.go rename to src/irc_struct.go From a008509e204d93782c3f813d8bb2be14881d0519 Mon Sep 17 00:00:00 2001 From: lye Date: Wed, 21 Mar 2012 22:39:33 -0500 Subject: [PATCH 06/11] Kill the example, will replace with tests later --- example/Makefile | 13 ------------- example/test.go | 31 ------------------------------- 2 files changed, 44 deletions(-) delete mode 100644 example/Makefile delete mode 100644 example/test.go diff --git a/example/Makefile b/example/Makefile deleted file mode 100644 index 05650bc..0000000 --- a/example/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -include $(GOROOT)/src/Make.inc - -OBJS := $(patsubst %.go,%.$O,$(wildcard *.go)) -OUT := $(patsubst %.$O,%,$(OBJS)) - -all: $(OBJS) - -%.$O: %.go - $(GC) $< - $(LD) -o $(patsubst %.$O,%,$@) $@ - -clean: - rm -f *.$O $(OBJS) $(OUT) diff --git a/example/test.go b/example/test.go deleted file mode 100644 index 9f7e81d..0000000 --- a/example/test.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - // irc "github.com/thoj/Go-IRC-Client-Library" - "fmt" - "os" - "irc" -) - -func main() { - irccon := irc.IRC("testgo", "testgo") - err := irccon.Connect("irc.efnet.net:6667") - if err != nil { - fmt.Printf("%s\n", err) - fmt.Printf("%#v\n", irccon) - os.Exit(1) - } - irccon.AddCallback("001", func(e *irc.IRCEvent) { irccon.Join("#testgo1") }) - irccon.AddCallback("001", func(e *irc.IRCEvent) { irccon.Join("#testgo2") }) - irccon.AddCallback("001", func(e *irc.IRCEvent) { irccon.Join("#testgo3") }) - irccon.AddCallback("001", func(e *irc.IRCEvent) { irccon.Join("#testgo4") }) - irccon.AddCallback("001", func(e *irc.IRCEvent) { irccon.Join("#testgo5") }) - irccon.AddCallback("001", func(e *irc.IRCEvent) { irccon.Join("#testgo6") }) - irccon.ReplaceCallback("001", 0, func(e *irc.IRCEvent) { irccon.Join("#testgo01") }) - irccon.ReplaceCallback("001", 1, func(e *irc.IRCEvent) { irccon.Join("#testgo02") }) - irccon.ReplaceCallback("001", 2, func(e *irc.IRCEvent) { irccon.Join("#testgo03") }) - irccon.ReplaceCallback("001", 3, func(e *irc.IRCEvent) { irccon.Join("#testgo04") }) - irccon.ReplaceCallback("001", 4, func(e *irc.IRCEvent) { irccon.Join("#testgo05") }) - irccon.ReplaceCallback("001", 6, func(e *irc.IRCEvent) { irccon.Join("#testgo06") }) - irccon.Loop() -} From a910f6d47dcc842633ddc85e84c2d9a0e09eadcf Mon Sep 17 00:00:00 2001 From: lye Date: Wed, 21 Mar 2012 22:50:21 -0500 Subject: [PATCH 07/11] Minor formatting changes --- src/irc.go | 27 +++++++++++++++++++++++++-- src/irc_callback.go | 20 +++++++++++++++++++- src/irc_struct.go | 2 +- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/irc.go b/src/irc.go index 0455e06..ea438c7 100644 --- a/src/irc.go +++ b/src/irc.go @@ -10,26 +10,29 @@ import ( "net" "strings" "time" - "github.com/lye/tls" + "tls" ) const ( - VERSION = "GolangBOT v1.0" + VERSION = "cleanirc v1.0" ) var error_ bool func reader(irc *IRCConnection) { br := bufio.NewReader(irc.socket) + for !error_ { msg, err := br.ReadString('\n') if err != nil { irc.Error <- err break } + irc.lastMessage = time.Now() msg = msg[0 : len(msg)-2] //Remove \r\n event := &IRCEvent{Raw: msg} + if msg[0] == ':' { if i := strings.Index(msg, " "); i > -1 { event.Source = msg[1:i] @@ -43,32 +46,39 @@ func reader(irc *IRCConnection) { event.Host = event.Source[j+1 : len(event.Source)] } } + args := strings.SplitN(msg, " :", 2) if len(args) > 1 { event.Message = args[1] } + args = strings.Split(args[0], " ") event.Code = strings.ToUpper(args[0]) if len(args) > 1 { event.Arguments = args[1:len(args)] } + irc.RunCallbacks(event) } + irc.syncreader <- true } func writer(irc *IRCConnection) { b, ok := <-irc.pwrite + for !error_ && ok { if b == "" || irc.socket == nil { break } + _, err := irc.socket.Write([]byte(b)) if err != nil { fmt.Printf("%s\n", err) irc.Error <- err break } + b, ok = <-irc.pwrite } irc.syncwriter <- true @@ -78,6 +88,7 @@ func writer(irc *IRCConnection) { func pinger(i *IRCConnection) { i.ticker = time.Tick(1 * time.Minute) //Tick every minute. i.ticker2 = time.Tick(15 * time.Minute) //Tick every 15 minutes. + for { select { case <-i.ticker: @@ -85,6 +96,7 @@ func pinger(i *IRCConnection) { if time.Since(i.lastMessage) >= (4 * time.Minute) { i.SendRaw(fmt.Sprintf("PING %d", time.Now().UnixNano())) } + case <-i.ticker2: //Ping every 15 minutes. i.SendRaw(fmt.Sprintf("PING %d", time.Now().UnixNano())) @@ -131,8 +143,10 @@ func (irc *IRCConnection) SendRaw(message string) { func (i *IRCConnection) Reconnect() error { close(i.pwrite) close(i.pread) + <-i.syncreader <-i.syncwriter + for { fmt.Printf("Reconnecting to %s\n", i.server) var err error @@ -142,27 +156,36 @@ func (i *IRCConnection) Reconnect() error { } fmt.Printf("Error: %s\n", err) } + error_ = false + fmt.Printf("Connected to %s (%s)\n", i.server, i.socket.RemoteAddr()) + go reader(i) go writer(i) + i.pwrite <- fmt.Sprintf("NICK %s\r\n", i.nick) i.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", i.user, i.user) + return nil } func (i *IRCConnection) Loop() { for !i.quitting { e := <-i.Error + if i.quitting { break } + fmt.Printf("Error: %s\n", e) error_ = true i.Reconnect() } + close(i.pwrite) close(i.pread) + <-i.syncreader <-i.syncwriter } diff --git a/src/irc_callback.go b/src/irc_callback.go index b6a922b..6acc685 100644 --- a/src/irc_callback.go +++ b/src/irc_callback.go @@ -9,8 +9,10 @@ import ( func (irc *IRCConnection) AddCallback(eventcode string, callback func(*IRCEvent)) { eventcode = strings.ToUpper(eventcode) + if _, ok := irc.events[eventcode]; ok { irc.events[eventcode] = append(irc.events[eventcode], callback) + } else { irc.events[eventcode] = make([]func(*IRCEvent), 1) irc.events[eventcode][0] = callback @@ -19,42 +21,54 @@ func (irc *IRCConnection) AddCallback(eventcode string, callback func(*IRCEvent) func (irc *IRCConnection) ReplaceCallback(eventcode string, i int, callback func(*IRCEvent)) { eventcode = strings.ToUpper(eventcode) + if event, ok := irc.events[eventcode]; ok { if i < len(event) { event[i] = callback return } + fmt.Printf("Event found, but no callback found at index %d. Use AddCallback\n", i) return } + fmt.Printf("Event not found. Use AddCallBack\n") } func (irc *IRCConnection) RunCallbacks(event *IRCEvent) { if event.Code == "PRIVMSG" && len(event.Message) > 0 && event.Message[0] == '\x01' { event.Code = "CTCP" //Unknown CTCP + if i := strings.LastIndex(event.Message, "\x01"); i > -1 { event.Message = event.Message[1:i] } + if event.Message == "VERSION" { event.Code = "CTCP_VERSION" + } else if event.Message == "TIME" { event.Code = "CTCP_TIME" + } else if event.Message[0:4] == "PING" { event.Code = "CTCP_PING" + } else if event.Message == "USERINFO" { event.Code = "CTCP_USERINFO" + } else if event.Message == "CLIENTINFO" { event.Code = "CTCP_CLIENTINFO" } } + if callbacks, ok := irc.events[event.Code]; ok { if irc.VerboseCallbackHandler { fmt.Printf("%v (%v) >> %#v\n", event.Code, len(callbacks), event) } + for _, callback := range callbacks { go callback(event) } + } else if irc.VerboseCallbackHandler { fmt.Printf("%v (0) >> %#v\n", event.Code, event) } @@ -84,7 +98,9 @@ func (irc *IRCConnection) setupCallbacks() { irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01TIME %s\x01", e.Nick, ltime.String())) }) - irc.AddCallback("CTCP_PING", func(e *IRCEvent) { irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01%s\x01", e.Nick, e.Message)) }) + irc.AddCallback("CTCP_PING", func(e *IRCEvent) { + irc.SendRaw(fmt.Sprintf("NOTICE %s :\x01%s\x01", e.Nick, e.Message)) + }) irc.AddCallback("437", func(e *IRCEvent) { irc.nickcurrent = irc.nickcurrent + "_" @@ -94,9 +110,11 @@ func (irc *IRCConnection) setupCallbacks() { irc.AddCallback("433", func(e *IRCEvent) { if len(irc.nickcurrent) > 8 { irc.nickcurrent = "_" + irc.nickcurrent + } else { irc.nickcurrent = irc.nickcurrent + "_" } + irc.SendRaw(fmt.Sprintf("NICK %s", irc.nickcurrent)) }) diff --git a/src/irc_struct.go b/src/irc_struct.go index 44ea0de..9a7f538 100644 --- a/src/irc_struct.go +++ b/src/irc_struct.go @@ -7,7 +7,7 @@ package irc import ( "net" "time" - "github.com/lye/tls" + "tls" ) type IRCConnection struct { From 31036ff926bed33b3c22a58007f11d9f0bff0d14 Mon Sep 17 00:00:00 2001 From: lye Date: Wed, 21 Mar 2012 22:57:35 -0500 Subject: [PATCH 08/11] Remove calls to fmt.Printf; send to IRCConn.Log chan if it is non-nil --- src/irc.go | 63 +++++++++++++++++++++++++++------------------ src/irc_callback.go | 10 +++---- src/irc_struct.go | 41 +++++++++++++++-------------- 3 files changed, 64 insertions(+), 50 deletions(-) diff --git a/src/irc.go b/src/irc.go index ea438c7..ad3e9d4 100644 --- a/src/irc.go +++ b/src/irc.go @@ -25,7 +25,7 @@ func reader(irc *IRCConnection) { for !error_ { msg, err := br.ReadString('\n') if err != nil { - irc.Error <- err + irc.Error <-err break } @@ -38,7 +38,7 @@ func reader(irc *IRCConnection) { event.Source = msg[1:i] msg = msg[i+1 : len(msg)] } else { - fmt.Printf("Misformed msg from server: %#s\n", msg) + irc.log(fmt.Sprintf("Misformed msg from server: %#s\n", msg)) } if i, j := strings.Index(event.Source, "!"), strings.Index(event.Source, "@"); i > -1 && j > -1 { event.Nick = event.Source[0:i] @@ -61,7 +61,7 @@ func reader(irc *IRCConnection) { irc.RunCallbacks(event) } - irc.syncreader <- true + irc.syncreader <-true } func writer(irc *IRCConnection) { @@ -74,14 +74,13 @@ func writer(irc *IRCConnection) { _, err := irc.socket.Write([]byte(b)) if err != nil { - fmt.Printf("%s\n", err) - irc.Error <- err + irc.Error <-err break } b, ok = <-irc.pwrite } - irc.syncwriter <- true + irc.syncwriter <-true } //Pings the server if we have not recived any messages for 5 minutes @@ -120,24 +119,24 @@ func (irc *IRCConnection) Quit() { } func (irc *IRCConnection) Join(channel string) { - irc.pwrite <- fmt.Sprintf("JOIN %s\r\n", channel) + irc.pwrite <-fmt.Sprintf("JOIN %s\r\n", channel) } func (irc *IRCConnection) Part(channel string) { - irc.pwrite <- fmt.Sprintf("PART %s\r\n", channel) + irc.pwrite <-fmt.Sprintf("PART %s\r\n", channel) } func (irc *IRCConnection) Notice(target, message string) { - irc.pwrite <- fmt.Sprintf("NOTICE %s :%s\r\n", target, message) + irc.pwrite <-fmt.Sprintf("NOTICE %s :%s\r\n", target, message) } func (irc *IRCConnection) Privmsg(target, message string) { - irc.pwrite <- fmt.Sprintf("PRIVMSG %s :%s\r\n", target, message) + irc.pwrite <-fmt.Sprintf("PRIVMSG %s :%s\r\n", target, message) } func (irc *IRCConnection) SendRaw(message string) { - fmt.Printf("--> %s\n", message) - irc.pwrite <- fmt.Sprintf("%s\r\n", message) + irc.log(fmt.Sprintf("--> %s\n", message)) + irc.pwrite <-fmt.Sprintf("%s\r\n", message) } func (i *IRCConnection) Reconnect() error { @@ -148,24 +147,26 @@ func (i *IRCConnection) Reconnect() error { <-i.syncwriter for { - fmt.Printf("Reconnecting to %s\n", i.server) + i.log(fmt.Sprintf("Reconnecting to %s\n", i.server)) + var err error i.socket, err = net.Dial("tcp", i.server) if err == nil { break } - fmt.Printf("Error: %s\n", err) + + i.log(fmt.Sprintf("Error: %s\n", err)) } error_ = false - fmt.Printf("Connected to %s (%s)\n", i.server, i.socket.RemoteAddr()) + i.log(fmt.Sprintf("Connected to %s (%s)\n", i.server, i.socket.RemoteAddr())) go reader(i) go writer(i) - i.pwrite <- fmt.Sprintf("NICK %s\r\n", i.nick) - i.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", i.user, i.user) + i.pwrite <-fmt.Sprintf("NICK %s\r\n", i.nick) + i.pwrite <-fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", i.user, i.user) return nil } @@ -178,7 +179,7 @@ func (i *IRCConnection) Loop() { break } - fmt.Printf("Error: %s\n", e) + i.log(fmt.Sprintf("Error: %s\n", e)) error_ = true i.Reconnect() } @@ -202,38 +203,50 @@ func (i *IRCConnection) postConnect() error { go pinger(i) if len(i.Password) > 0 { - i.pwrite <- fmt.Sprintf("PASS %s\r\n", i.Password) + i.pwrite <-fmt.Sprintf("PASS %s\r\n", i.Password) } - i.pwrite <- fmt.Sprintf("NICK %s\r\n", i.nick) - i.pwrite <- fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", i.user, i.user) + i.pwrite <-fmt.Sprintf("NICK %s\r\n", i.nick) + i.pwrite <-fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", i.user, i.user) return nil } func (i *IRCConnection) Connect(server string) error { i.server = server - fmt.Printf("Connecting to %s\n", i.server) + i.log(fmt.Sprintf("Connecting to %s\n", i.server)) + var err error i.socket, err = net.Dial("tcp", i.server) if err != nil { return err } - fmt.Printf("Connected to %s (%s)\n", i.server, i.socket.RemoteAddr()) + + i.log(fmt.Sprintf("Connected to %s (%s)\n", i.server, i.socket.RemoteAddr())) return i.postConnect() } func (i *IRCConnection) ConnectSSL(server string) error { i.server = server - fmt.Printf("Connecting to %s over SSL\n", i.server) + i.log(fmt.Sprintf("Connecting to %s over SSL\n", i.server)) + var err error i.socket, err = tls.Dial("tcp", i.server, i.SSLConfig) + if err != nil { return err } - fmt.Printf("Connected to %s (%s) over SSL\n", i.server, i.socket.RemoteAddr()) + + i.log(fmt.Sprintf("Connected to %s (%s) over SSL\n", i.server, i.socket.RemoteAddr())) + return i.postConnect() } +func (i *IRCConnection) log(msg string) { + if i.Log != nil { + i.Log <-msg + } +} + func IRC(nick, user string) *IRCConnection { irc := new(IRCConnection) irc.registered = false diff --git a/src/irc_callback.go b/src/irc_callback.go index 6acc685..f23356f 100644 --- a/src/irc_callback.go +++ b/src/irc_callback.go @@ -28,11 +28,11 @@ func (irc *IRCConnection) ReplaceCallback(eventcode string, i int, callback func return } - fmt.Printf("Event found, but no callback found at index %d. Use AddCallback\n", i) + irc.log(fmt.Sprintf("Event found, but no callback found at index %d. Use AddCallback\n", i)) return } - fmt.Printf("Event not found. Use AddCallBack\n") + irc.log(fmt.Sprintf("Event not found. Use AddCallBack\n")) } func (irc *IRCConnection) RunCallbacks(event *IRCEvent) { @@ -62,7 +62,7 @@ func (irc *IRCConnection) RunCallbacks(event *IRCEvent) { if callbacks, ok := irc.events[event.Code]; ok { if irc.VerboseCallbackHandler { - fmt.Printf("%v (%v) >> %#v\n", event.Code, len(callbacks), event) + irc.log(fmt.Sprintf("%v (%v) >> %#v\n", event.Code, len(callbacks), event)) } for _, callback := range callbacks { @@ -70,7 +70,7 @@ func (irc *IRCConnection) RunCallbacks(event *IRCEvent) { } } else if irc.VerboseCallbackHandler { - fmt.Printf("%v (0) >> %#v\n", event.Code, event) + irc.log(fmt.Sprintf("%v (0) >> %#v\n", event.Code, event)) } } @@ -121,7 +121,7 @@ func (irc *IRCConnection) setupCallbacks() { irc.AddCallback("PONG", func(e *IRCEvent) { ns, _ := strconv.ParseInt(e.Message, 10, 64) delta := time.Duration(time.Now().UnixNano() - ns) - fmt.Printf("Lag: %vs\n", delta) + irc.log(fmt.Sprintf("Lag: %vs\n", delta)) }) irc.AddCallback("NICK", func(e *IRCEvent) { diff --git a/src/irc_struct.go b/src/irc_struct.go index 9a7f538..7bf4022 100644 --- a/src/irc_struct.go +++ b/src/irc_struct.go @@ -11,37 +11,38 @@ import ( ) type IRCConnection struct { - socket net.Conn - pread, pwrite chan string - Error chan error + Error chan error + Log chan string + Password string + SSLConfig *tls.Config + + socket net.Conn + pread, pwrite chan string syncreader, syncwriter chan bool - nick string //The nickname we want. - nickcurrent string //The nickname we currently have. - user string - registered bool - server string - Password string - events map[string][]func(*IRCEvent) + nick string //The nickname we want. + nickcurrent string //The nickname we currently have. + user string + registered bool + server string + events map[string][]func(*IRCEvent) lastMessage time.Time - ticker <-chan time.Time - ticker2 <-chan time.Time + ticker <-chan time.Time + ticker2 <-chan time.Time VerboseCallbackHandler bool quitting bool - - SSLConfig *tls.Config } type IRCEvent struct { - Code string + Code string Message string - Raw string - Nick string // - Host string //!@ - Source string // - User string // + Raw string + Nick string // + Host string //!@ + Source string // + User string // Arguments []string } From b06c2dfa4a3e774d3d8e566228eda89e89684e29 Mon Sep 17 00:00:00 2001 From: lye Date: Wed, 21 Mar 2012 23:02:29 -0500 Subject: [PATCH 09/11] Standardized on 'irc' as the method receiver name for IRCConn methods --- src/irc.go | 125 +++++++++++++++++++++++++++-------------------------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/src/irc.go b/src/irc.go index ad3e9d4..f0c4fa7 100644 --- a/src/irc.go +++ b/src/irc.go @@ -84,25 +84,26 @@ func writer(irc *IRCConnection) { } //Pings the server if we have not recived any messages for 5 minutes -func pinger(i *IRCConnection) { - i.ticker = time.Tick(1 * time.Minute) //Tick every minute. - i.ticker2 = time.Tick(15 * time.Minute) //Tick every 15 minutes. +func pinger(irc *IRCConnection) { + irc.ticker = time.Tick(1 * time.Minute) //Tick every minute. + irc.ticker2 = time.Tick(15 * time.Minute) //Tick every 15 minutes. for { select { - case <-i.ticker: + case <-irc.ticker: //Ping if we haven't recived anything from the server within 4 minutes - if time.Since(i.lastMessage) >= (4 * time.Minute) { - i.SendRaw(fmt.Sprintf("PING %d", time.Now().UnixNano())) + if time.Since(irc.lastMessage) >= (4 * time.Minute) { + irc.SendRaw(fmt.Sprintf("PING %d", time.Now().UnixNano())) } - case <-i.ticker2: + case <-irc.ticker2: //Ping every 15 minutes. - i.SendRaw(fmt.Sprintf("PING %d", time.Now().UnixNano())) + irc.SendRaw(fmt.Sprintf("PING %d", time.Now().UnixNano())) + //Try to recapture nickname if it's not as configured. - if i.nick != i.nickcurrent { - i.nickcurrent = i.nick - i.SendRaw(fmt.Sprintf("NICK %s", i.nick)) + if irc.nick != irc.nickcurrent { + irc.nickcurrent = irc.nick + irc.SendRaw(fmt.Sprintf("NICK %s", irc.nick)) } } } @@ -139,111 +140,111 @@ func (irc *IRCConnection) SendRaw(message string) { irc.pwrite <-fmt.Sprintf("%s\r\n", message) } -func (i *IRCConnection) Reconnect() error { - close(i.pwrite) - close(i.pread) +func (irc *IRCConnection) Reconnect() error { + close(irc.pwrite) + close(irc.pread) - <-i.syncreader - <-i.syncwriter + <-irc.syncreader + <-irc.syncwriter for { - i.log(fmt.Sprintf("Reconnecting to %s\n", i.server)) + irc.log(fmt.Sprintf("Reconnecting to %s\n", irc.server)) var err error - i.socket, err = net.Dial("tcp", i.server) + irc.socket, err = net.Dial("tcp", irc.server) if err == nil { break } - i.log(fmt.Sprintf("Error: %s\n", err)) + irc.log(fmt.Sprintf("Error: %s\n", err)) } error_ = false - i.log(fmt.Sprintf("Connected to %s (%s)\n", i.server, i.socket.RemoteAddr())) + irc.log(fmt.Sprintf("Connected to %s (%s)\n", irc.server, irc.socket.RemoteAddr())) - go reader(i) - go writer(i) + go reader(irc) + go writer(irc) - i.pwrite <-fmt.Sprintf("NICK %s\r\n", i.nick) - i.pwrite <-fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", i.user, i.user) + irc.pwrite <-fmt.Sprintf("NICK %s\r\n", irc.nick) + irc.pwrite <-fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", irc.user, irc.user) return nil } -func (i *IRCConnection) Loop() { - for !i.quitting { - e := <-i.Error +func (irc *IRCConnection) Loop() { + for !irc.quitting { + e := <-irc.Error - if i.quitting { + if irc.quitting { break } - i.log(fmt.Sprintf("Error: %s\n", e)) + irc.log(fmt.Sprintf("Error: %s\n", e)) error_ = true - i.Reconnect() + irc.Reconnect() } - close(i.pwrite) - close(i.pread) + close(irc.pwrite) + close(irc.pread) - <-i.syncreader - <-i.syncwriter + <-irc.syncreader + <-irc.syncwriter } -func (i *IRCConnection) postConnect() error { - i.pread = make(chan string, 100) - i.pwrite = make(chan string, 100) - i.Error = make(chan error, 10) - i.syncreader = make(chan bool) - i.syncwriter = make(chan bool) +func (irc *IRCConnection) postConnect() error { + irc.pread = make(chan string, 100) + irc.pwrite = make(chan string, 100) + irc.Error = make(chan error, 10) + irc.syncreader = make(chan bool) + irc.syncwriter = make(chan bool) - go reader(i) - go writer(i) - go pinger(i) + go reader(irc) + go writer(irc) + go pinger(irc) - if len(i.Password) > 0 { - i.pwrite <-fmt.Sprintf("PASS %s\r\n", i.Password) + if len(irc.Password) > 0 { + irc.pwrite <-fmt.Sprintf("PASS %s\r\n", irc.Password) } - i.pwrite <-fmt.Sprintf("NICK %s\r\n", i.nick) - i.pwrite <-fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", i.user, i.user) + irc.pwrite <-fmt.Sprintf("NICK %s\r\n", irc.nick) + irc.pwrite <-fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", irc.user, irc.user) return nil } -func (i *IRCConnection) Connect(server string) error { - i.server = server - i.log(fmt.Sprintf("Connecting to %s\n", i.server)) +func (irc *IRCConnection) Connect(server string) error { + irc.server = server + irc.log(fmt.Sprintf("Connecting to %s\n", irc.server)) var err error - i.socket, err = net.Dial("tcp", i.server) + irc.socket, err = net.Dial("tcp", irc.server) if err != nil { return err } - i.log(fmt.Sprintf("Connected to %s (%s)\n", i.server, i.socket.RemoteAddr())) - return i.postConnect() + irc.log(fmt.Sprintf("Connected to %s (%s)\n", irc.server, irc.socket.RemoteAddr())) + return irc.postConnect() } -func (i *IRCConnection) ConnectSSL(server string) error { - i.server = server - i.log(fmt.Sprintf("Connecting to %s over SSL\n", i.server)) +func (irc *IRCConnection) ConnectSSL(server string) error { + irc.server = server + irc.log(fmt.Sprintf("Connecting to %s over SSL\n", irc.server)) var err error - i.socket, err = tls.Dial("tcp", i.server, i.SSLConfig) + irc.socket, err = tls.Dial("tcp", irc.server, irc.SSLConfig) if err != nil { return err } - i.log(fmt.Sprintf("Connected to %s (%s) over SSL\n", i.server, i.socket.RemoteAddr())) + irc.log(fmt.Sprintf("Connected to %s (%s) over SSL\n", irc.server, irc.socket.RemoteAddr())) - return i.postConnect() + return irc.postConnect() } -func (i *IRCConnection) log(msg string) { - if i.Log != nil { - i.Log <-msg +func (irc *IRCConnection) log(msg string) { + if irc.Log != nil { + irc.Log <-msg } } From f2faa10846a4916d778abe80b699ba14d7e0aaaf Mon Sep 17 00:00:00 2001 From: lye Date: Wed, 21 Mar 2012 23:08:21 -0500 Subject: [PATCH 10/11] Refactor reader/writer/pinger The old code relied on a global bool for reconnect synchronization; this would cause the reader/writer to stop (and not restart) on every other `IRCConnection` when one was `Reconnect`d. This patch renames the methods to indicate they loop, makes them methods on `IRCConnection`, and uses a member of `IRCConnection` for synchronization rather than a global. --- src/irc.go | 34 +++++++++++++++++++--------------- src/irc_struct.go | 2 ++ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/irc.go b/src/irc.go index f0c4fa7..14e5cfe 100644 --- a/src/irc.go +++ b/src/irc.go @@ -17,12 +17,10 @@ const ( VERSION = "cleanirc v1.0" ) -var error_ bool - -func reader(irc *IRCConnection) { +func (irc *IRCConnection) readLoop() { br := bufio.NewReader(irc.socket) - for !error_ { + for !irc.reconnecting { msg, err := br.ReadString('\n') if err != nil { irc.Error <-err @@ -37,9 +35,11 @@ func reader(irc *IRCConnection) { if i := strings.Index(msg, " "); i > -1 { event.Source = msg[1:i] msg = msg[i+1 : len(msg)] + } else { irc.log(fmt.Sprintf("Misformed msg from server: %#s\n", msg)) } + if i, j := strings.Index(event.Source, "!"), strings.Index(event.Source, "@"); i > -1 && j > -1 { event.Nick = event.Source[0:i] event.User = event.Source[i+1 : j] @@ -54,9 +54,11 @@ func reader(irc *IRCConnection) { args = strings.Split(args[0], " ") event.Code = strings.ToUpper(args[0]) + if len(args) > 1 { event.Arguments = args[1:len(args)] } + /* XXX: len(args) == 0: args should be empty */ irc.RunCallbacks(event) } @@ -64,10 +66,10 @@ func reader(irc *IRCConnection) { irc.syncreader <-true } -func writer(irc *IRCConnection) { +func (irc *IRCConnection) writeLoop() { b, ok := <-irc.pwrite - for !error_ && ok { + for !irc.reconnecting && ok { if b == "" || irc.socket == nil { break } @@ -84,14 +86,14 @@ func writer(irc *IRCConnection) { } //Pings the server if we have not recived any messages for 5 minutes -func pinger(irc *IRCConnection) { +func (irc *IRCConnection) pingLoop() { irc.ticker = time.Tick(1 * time.Minute) //Tick every minute. irc.ticker2 = time.Tick(15 * time.Minute) //Tick every 15 minutes. for { select { case <-irc.ticker: - //Ping if we haven't recived anything from the server within 4 minutes + //Ping if we haven't received anything from the server within 4 minutes if time.Since(irc.lastMessage) >= (4 * time.Minute) { irc.SendRaw(fmt.Sprintf("PING %d", time.Now().UnixNano())) } @@ -141,6 +143,8 @@ func (irc *IRCConnection) SendRaw(message string) { } func (irc *IRCConnection) Reconnect() error { + irc.reconnecting = true + close(irc.pwrite) close(irc.pread) @@ -159,12 +163,12 @@ func (irc *IRCConnection) Reconnect() error { irc.log(fmt.Sprintf("Error: %s\n", err)) } - error_ = false + irc.reconnecting = false irc.log(fmt.Sprintf("Connected to %s (%s)\n", irc.server, irc.socket.RemoteAddr())) - go reader(irc) - go writer(irc) + go irc.readLoop() + go irc.writeLoop() irc.pwrite <-fmt.Sprintf("NICK %s\r\n", irc.nick) irc.pwrite <-fmt.Sprintf("USER %s 0.0.0.0 0.0.0.0 :%s\r\n", irc.user, irc.user) @@ -181,7 +185,6 @@ func (irc *IRCConnection) Loop() { } irc.log(fmt.Sprintf("Error: %s\n", e)) - error_ = true irc.Reconnect() } @@ -199,9 +202,9 @@ func (irc *IRCConnection) postConnect() error { irc.syncreader = make(chan bool) irc.syncwriter = make(chan bool) - go reader(irc) - go writer(irc) - go pinger(irc) + go irc.readLoop() + go irc.writeLoop() + go irc.pingLoop() if len(irc.Password) > 0 { irc.pwrite <-fmt.Sprintf("PASS %s\r\n", irc.Password) @@ -248,6 +251,7 @@ func (irc *IRCConnection) log(msg string) { } } +/* XXX: Change ctor name */ func IRC(nick, user string) *IRCConnection { irc := new(IRCConnection) irc.registered = false diff --git a/src/irc_struct.go b/src/irc_struct.go index 7bf4022..b7e4035 100644 --- a/src/irc_struct.go +++ b/src/irc_struct.go @@ -19,6 +19,8 @@ type IRCConnection struct { socket net.Conn pread, pwrite chan string syncreader, syncwriter chan bool + reconnecting bool + nick string //The nickname we want. nickcurrent string //The nickname we currently have. user string From 4f82a31c9ccf9679b7760f1d6f9897ab26adefef Mon Sep 17 00:00:00 2001 From: lye Date: Wed, 28 Mar 2012 01:15:26 -0500 Subject: [PATCH 11/11] Minor tweaks to play nicely with the new go tool --- Makefile | 9 --------- src/irc.go => irc.go | 2 +- src/irc_callback.go => irc_callback.go | 0 src/irc_struct.go => irc_struct.go | 2 +- 4 files changed, 2 insertions(+), 11 deletions(-) delete mode 100644 Makefile rename src/irc.go => irc.go (99%) rename src/irc_callback.go => irc_callback.go (100%) rename src/irc_struct.go => irc_struct.go (98%) diff --git a/Makefile b/Makefile deleted file mode 100644 index 870adf1..0000000 --- a/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include $(GOROOT)/src/Make.inc - -TARG=github.com/lye/cleanirc -GOFILES=\ - src/irc.go \ - src/irc_struct.go \ - src/irc_callback.go - -include $(GOROOT)/src/Make.pkg diff --git a/src/irc.go b/irc.go similarity index 99% rename from src/irc.go rename to irc.go index 14e5cfe..1961027 100644 --- a/src/irc.go +++ b/irc.go @@ -10,7 +10,7 @@ import ( "net" "strings" "time" - "tls" + "crypto/tls" ) const ( diff --git a/src/irc_callback.go b/irc_callback.go similarity index 100% rename from src/irc_callback.go rename to irc_callback.go diff --git a/src/irc_struct.go b/irc_struct.go similarity index 98% rename from src/irc_struct.go rename to irc_struct.go index b7e4035..95fecd4 100644 --- a/src/irc_struct.go +++ b/irc_struct.go @@ -7,7 +7,7 @@ package irc import ( "net" "time" - "tls" + "crypto/tls" ) type IRCConnection struct {